Class: TddGuardMinitest::Reporter

Inherits:
Minitest::StatisticsReporter
  • Object
show all
Defined in:
lib/tdd_guard_minitest/reporter.rb

Overview

Minitest reporter that captures test results for TDD Guard validation. Mirrors the RSpec reporter’s single-class architecture.

Constant Summary collapse

DEFAULT_DATA_DIR =
".claude/tdd-guard/data"

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(io = $stdout, options = {}) ⇒ Reporter

Returns a new instance of Reporter.



21
22
23
24
25
26
# File 'lib/tdd_guard_minitest/reporter.rb', line 21

def initialize(io = $stdout, options = {})
  super
  @test_results = []
  @expected_count = 0
  @storage_dir = determine_storage_dir
end

Class Method Details

.append_unhandled_errors(errors) ⇒ Object

Reads the existing test.json, merges in the unhandledErrors field, and re-writes it. Called from the post-after_run at_exit hook in autorun.rb after Minitest.after_run blocks have completed.



55
56
57
58
59
# File 'lib/tdd_guard_minitest/reporter.rb', line 55

def self.append_unhandled_errors(errors)
  new(StringIO.new).append_unhandled_errors(errors)
rescue ArgumentError
  # Same as above: skip when the project root is not configured.
end

.handle_load_error(exception) ⇒ Object

Writes a synthetic failed-test JSON for an exception raised before Minitest had a chance to run (typically a LoadError from a missing require at the top of a test file). Called from the autorun entry point’s at_exit hook when $! is set.

Injects a synthetic entry into @test_results and writes the JSON through the normal report path. Skips when this process has already written test.json via the normal Minitest flow, so it never clobbers real results from the same run. A stale test.json left behind by a previous process is overwritten so the file always reflects the most recent run’s state.



44
45
46
47
48
49
50
# File 'lib/tdd_guard_minitest/reporter.rb', line 44

def self.handle_load_error(exception)
  new(StringIO.new).handle_load_error(exception)
rescue ArgumentError
  # Project root is not configured; the user has already seen the
  # configuration error from the main test run. Avoid double-raising
  # from the autorun at_exit hook.
end

Instance Method Details

#append_unhandled_errors(errors) ⇒ Object



61
62
63
64
65
66
67
68
# File 'lib/tdd_guard_minitest/reporter.rb', line 61

def append_unhandled_errors(errors)
  json_path = File.join(@storage_dir, "test.json")
  return unless File.exist?(json_path)

  data = JSON.parse(File.read(json_path))
  data["unhandledErrors"] = errors.map { |e| build_unhandled_error(e) }
  File.write(json_path, JSON.pretty_generate(data))
end

#handle_load_error(exception) ⇒ Object



70
71
72
73
74
75
# File 'lib/tdd_guard_minitest/reporter.rb', line 70

def handle_load_error(exception)
  return if TddGuardMinitest.reported

  add_load_error(exception)
  report
end

#passed?Boolean

Returns:

  • (Boolean)


126
127
128
# File 'lib/tdd_guard_minitest/reporter.rb', line 126

def passed?
  @test_results.none? { |t| t["state"] == "failed" }
end

#record(result) ⇒ Object



77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
# File 'lib/tdd_guard_minitest/reporter.rb', line 77

def record(result)
  state = if result.skipped?
            "skipped"
          elsif result.passed?
            "passed"
          else
            "failed"
          end

  file_path = extract_file_path(result)
  test = {
    "name" => result.name,
    "fullName" => "#{file_path}::#{result.klass}##{result.name}",
    "state" => state
  }

  if state == "failed" && result.failures.any?
    test["errors"] = result.failures.map { |failure| build_error(failure) }
  end

  @test_results << test
end

#reportObject



100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
# File 'lib/tdd_guard_minitest/reporter.rb', line 100

def report
  modules_map = {}
  @test_results.each do |test|
    module_path = test["fullName"].split("::").first
    modules_map[module_path] ||= { "moduleId" => module_path, "tests" => [] }
    modules_map[module_path]["tests"] << test
  end

  has_failures = @test_results.any? { |t| t["state"] == "failed" }
  reason = if has_failures
             "failed"
           elsif @expected_count > 0 && @test_results.length < @expected_count
             "interrupted"
           else
             "passed"
           end
  result = {
    "testModules" => modules_map.values,
    "reason" => reason
  }

  FileUtils.mkdir_p(@storage_dir)
  File.write(File.join(@storage_dir, "test.json"), JSON.pretty_generate(result))
  TddGuardMinitest.reported = true
end

#startObject



28
29
30
31
# File 'lib/tdd_guard_minitest/reporter.rb', line 28

def start
  super
  @expected_count = compute_expected_count
end