Packages

BEAM-native coding agent substrate for Elixir/OTP projects

Current section

Files

Jump to
vibe lib mix tasks vibe.test.ex
Raw

lib/mix/tasks/vibe.test.ex

defmodule Mix.Tasks.Vibe.Test do
use Mix.Task
alias Vibe.TestRunner
@shortdoc "Run tests with formatted output"
@moduledoc """
Runs tests with AI-friendly output formatting.
## Usage
mix vibe.test [TEST_PATHS] [OPTIONS]
## Examples
mix vibe.test
mix vibe.test test/my_module_test.exs
mix vibe.test --only authentication
mix vibe.test --coverage
## Options
--only TAG Run only tests with the given tag
--exclude TAG Exclude tests with the given tag
--seed NUMBER Set the random seed
--trace Run tests with detailed trace output
--coverage Run tests with coverage reporting
--format FORMAT Output format (text, json, markdown, default: config value or markdown)
--help, -h Show this help message
"""
@impl Mix.Task
def run(args) do
{opts, test_paths, _} =
OptionParser.parse(args,
strict: [
only: :string,
exclude: :string,
seed: :integer,
trace: :boolean,
coverage: :boolean,
format: :string,
help: :boolean
],
aliases: [h: :help]
)
format = opts_to_format(opts)
cond do
Keyword.get(opts, :help) == true ->
print_help()
Keyword.get(opts, :coverage) == true ->
test_opts = extract_test_options(opts)
run_with_coverage(test_paths, test_opts, format)
true ->
test_opts = extract_test_options(opts)
run_tests(test_paths, test_opts, format)
end
end
defp opts_to_format(opts) do
cond do
opts[:format] -> String.to_atom(opts[:format])
true -> Vibe.output_format()
end
end
defp extract_test_options(opts) do
Keyword.take(opts, [:only, :exclude, :seed, :trace])
end
defp run_tests(paths, options, format) do
case TestRunner.run_tests(paths, options) do
{:ok, results} ->
output = format_test_results(results, format)
IO.puts(output)
{:error, message} ->
print_error(message, format)
end
end
defp run_with_coverage(paths, options, format) do
case TestRunner.run_tests_with_coverage(paths, options) do
{:ok, results} ->
output = format_coverage_results(results, format)
IO.puts(output)
{:error, message} ->
print_error(message, format)
end
end
defp format_test_results(results, format) do
case format do
:json ->
Jason.encode!(results, pretty: true)
:markdown ->
# Format overall stats
stats_block = """
# Test Results
- **Total:** #{results.total}
- **Passed:** #{results.passed}
- **Failed:** #{results.failed}
- **Success:** #{if results.success, do: "โœ…", else: "โŒ"}
"""
# Add timing if available
stats_block =
if Map.has_key?(results, :execution_time_ms) do
"#{stats_block}\n- **Execution time:** #{results.execution_time_ms}ms"
else
stats_block
end
# Format failures
failures_block =
case results.failure_descriptions do
[] -> ""
failures ->
"""
## Failures
#{Enum.map_join(failures, "\n\n", fn desc -> "- #{desc}" end)}
"""
end
"""
#{stats_block}#{failures_block}
#{if results.success, do: "๐ŸŽต **Test suite is vibing!** ๐ŸŽต", else: "๐ŸŽต **Some tests are out of tune!** ๐ŸŽต"}
"""
_ ->
# Plain text format
stats_block = """
TEST RESULTS
Total: #{results.total}
Passed: #{results.passed}
Failed: #{results.failed}
Success: #{if results.success, do: "Yes", else: "No"}
"""
# Add timing if available
stats_block =
if Map.has_key?(results, :execution_time_ms) do
"#{stats_block}Execution time: #{results.execution_time_ms}ms\n"
else
stats_block
end
# Format failures
failures_block =
case results.failure_descriptions do
[] -> ""
failures ->
"""
Failures:
#{Enum.map_join(failures, "\n\n", fn desc -> "- #{desc}" end)}
"""
end
"""
#{stats_block}#{failures_block}
#{if results.success, do: "Test suite is vibing!", else: "Some tests are out of tune!"}
"""
end
end
defp format_coverage_results(results, format) do
# Get the basic test result first
test_results = format_test_results(results, format)
# Add coverage information
case format do
:json ->
# Already included in the results
test_results
:markdown ->
coverage_block = """
## Coverage Report
- **Total coverage:** #{results.coverage.percentage}%
### File Coverage
#{
results.coverage.files
|> Enum.sort_by(fn {_, percentage} -> percentage end, :desc)
|> Enum.map_join("\n", fn {file, percentage} -> "- `#{file}`: #{percentage}%" end)
}
"""
# Replace the closing line with our coverage block and a new closing line
String.replace(test_results, ~r/๐ŸŽต.*๐ŸŽต$/, "#{coverage_block}\n\n๐ŸŽต **Your code is grooving with #{Float.round(results.coverage.percentage, 1)}% coverage!** ๐ŸŽต")
_ ->
coverage_block = """
COVERAGE REPORT
Total coverage: #{results.coverage.percentage}%
File Coverage:
#{
results.coverage.files
|> Enum.sort_by(fn {_, percentage} -> percentage end, :desc)
|> Enum.map_join("\n", fn {file, percentage} -> " #{file}: #{percentage}%" end)
}
"""
# Add coverage block before the last line
line_count = String.split(test_results, "\n") |> length()
{main_block, last_line} =
test_results
|> String.split("\n")
|> Enum.split(line_count - 2)
(Enum.join(main_block, "\n") <> coverage_block <> "\n" <> Enum.join(last_line, "\n"))
end
end
defp print_error(message, format) do
output =
case format do
:json -> Jason.encode!(%{error: message}, pretty: true)
:markdown -> "**Error:** #{message}"
_ -> "Error: #{message}"
end
IO.puts(output)
end
defp print_help do
IO.puts("mix vibe.test - Run tests with formatted output")
IO.puts("")
IO.puts("Usage:")
IO.puts(" mix vibe.test [TEST_PATHS] [OPTIONS]")
IO.puts("")
IO.puts("Examples:")
IO.puts(" mix vibe.test")
IO.puts(" mix vibe.test test/my_module_test.exs")
IO.puts(" mix vibe.test --only authentication")
IO.puts(" mix vibe.test --coverage")
IO.puts("")
IO.puts("Options:")
IO.puts(" --only TAG Run only tests with the given tag")
IO.puts(" --exclude TAG Exclude tests with the given tag")
IO.puts(" --seed NUMBER Set the random seed")
IO.puts(" --trace Run tests with detailed trace output")
IO.puts(" --coverage Run tests with coverage reporting")
IO.puts(" --format FORMAT Output format (text, json, markdown, default: config value or markdown)")
IO.puts(" --help, -h Show this help message")
IO.puts("")
IO.puts("Feedback:")
IO.puts(" ๐Ÿงช Test your code with good vibes! ๐ŸŽต")
end
end