Class: Tryouts::CLI::VerboseFormatter

Inherits:
Object
  • Object
show all
Includes:
FormatterInterface
Defined in:
lib/tryouts/cli/formatters/verbose.rb

Overview

Detailed formatter with comprehensive output and clear visual hierarchy

Direct Known Subclasses

VerboseFailsFormatter

Instance Attribute Summary

Attributes included from FormatterInterface

#current_indent, #stderr, #stdout

Instance Method Summary collapse

Methods included from FormatterInterface

#file_end, #live_status_manager, #puts, #set_live_status_manager, #test_end, #update_live_status, #write

Constructor Details

#initialize(options = {}) ⇒ VerboseFormatter

Returns a new instance of VerboseFormatter.



11
12
13
14
15
16
17
18
# File 'lib/tryouts/cli/formatters/verbose.rb', line 11

def initialize(options = {})
  super
  @line_width     = options.fetch(:line_width, 70)
  @show_passed    = options.fetch(:show_passed, true)
  @show_debug     = options.fetch(:debug, false)
  @show_trace     = options.fetch(:trace, false)
  @show_stack_traces = options.fetch(:stack_traces, false) || options.fetch(:debug, false)
end

Instance Method Details

#batch_summary(failure_collector) ⇒ Object

Summary operations - show detailed failure summary



62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
# File 'lib/tryouts/cli/formatters/verbose.rb', line 62

def batch_summary(failure_collector)
  return unless failure_collector.any_failures?

  puts
  write '=== FAILURES ==='

  # Number failures sequentially across all files instead of per-file
  failure_number = 1
  failure_collector.failures_by_file.each do |file_path, failures|
    failures.each do |failure|
      pretty_path = Console.pretty_path(file_path)

      # Include line number with file path for easy copying/clicking
      if failure.line_number > 0
        location = "#{pretty_path}:#{failure.line_number + 1}"
      else
        location = pretty_path
      end

      puts
      puts Console.color(:yellow, location)
      puts "  #{failure_number}) #{failure.description}"
      puts "     #{Console.color(:red, failure.failure_reason)}"

      # Show source context in compact format
      if failure.source_context.any?
        failure.source_context.each do |line|
          puts "       #{line.strip}"
        end
      end
      failure_number += 1
    end
  end
end

#debug_info(message, level: 0) ⇒ Object

Debug and diagnostic output



235
236
237
238
239
240
241
# File 'lib/tryouts/cli/formatters/verbose.rb', line 235

def debug_info(message, level: 0)
  return unless @show_debug

  prefix = Console.color(:cyan, 'INFO ')
  puts
  puts indent_text("#{prefix} #{message}", level + 1)
end

#error_message(message, backtrace: nil) ⇒ Object



250
251
252
253
254
255
256
257
258
259
260
261
262
# File 'lib/tryouts/cli/formatters/verbose.rb', line 250

def error_message(message, backtrace: nil)
  error_msg = Console.color(:red, "ERROR: #{message}")
  puts indent_text(error_msg, 0)

  return unless backtrace && @show_stack_traces

  puts indent_text('Details:', 1)
  # Show first 10 lines of backtrace to avoid overwhelming output
  Console.pretty_backtrace(backtrace, limit: 10).each do |line|
    puts indent_text(line, 2)
  end
  puts indent_text("... (#{backtrace.length - 10} more lines)", 2) if backtrace.length > 10
end

#file_execution_start(_file_path, test_count:, context_mode:) ⇒ Object



56
57
58
59
# File 'lib/tryouts/cli/formatters/verbose.rb', line 56

def file_execution_start(_file_path, test_count:, context_mode:)
  message = "Running #{test_count} tests with #{context_mode} context"
  puts(indent_text(message, 0))
end

#file_parsed(_file_path, test_count:, setup_present: false, teardown_present: false) ⇒ Object



32
33
34
35
36
37
38
39
40
41
# File 'lib/tryouts/cli/formatters/verbose.rb', line 32

def file_parsed(_file_path, test_count:, setup_present: false, teardown_present: false)
  message = ''

  extras = []
  extras << 'setup' if setup_present
  extras << 'teardown' if teardown_present
  message += " (#{extras.join(', ')})" unless extras.empty?

  puts(indent_text(message, 1))
end

#file_result(_file_path, total_tests:, failed_count:, error_count:, elapsed_time: nil) ⇒ Object



97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
# File 'lib/tryouts/cli/formatters/verbose.rb', line 97

def file_result(_file_path, total_tests:, failed_count:, error_count:, elapsed_time: nil)
  issues_count = failed_count + error_count
  passed_count = total_tests - issues_count
  details = ["#{passed_count} passed"]

  puts
  if issues_count > 0
    details << "#{failed_count} failed" if failed_count > 0
    details << "#{error_count} errors" if error_count > 0
    details_str = details.join(', ')
    color = :red

    time_str = elapsed_time ? " (#{elapsed_time.round(2)}s)" : ''
    message = "✗ Out of #{total_tests} tests: #{details_str}#{time_str}"
    puts indent_text(Console.color(color, message), 1)
  else
    message = "#{total_tests} tests passed"
    color = :green
    puts indent_text(Console.color(color, "#{message}"), 1)
  end

  return unless elapsed_time

  time_msg = "Completed in #{format_timing(elapsed_time).strip.tr('()', '')}"
  puts indent_text(Console.color(:dim, time_msg), 1)
end

#file_start(file_path, context_info: {}) ⇒ Object

File-level operations



28
29
30
# File 'lib/tryouts/cli/formatters/verbose.rb', line 28

def file_start(file_path, context_info: {})
  puts(file_header_visual(file_path))
end

#grand_total(total_tests:, failed_count:, error_count:, successful_files:, total_files:, elapsed_time:) ⇒ Object



210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
# File 'lib/tryouts/cli/formatters/verbose.rb', line 210

def grand_total(total_tests:, failed_count:, error_count:, successful_files:, total_files:, elapsed_time:)
  puts
  puts '=== TOTAL ==='

  issues_count = failed_count + error_count
  time_str = if elapsed_time < 2.0
    " (#{(elapsed_time * 1000).round}ms)"
  else
    " (#{elapsed_time.round(2)}s)"
  end

  if issues_count > 0
    passed = [total_tests - issues_count, 0].max  # Ensure passed never goes negative
    details = []
    details << "#{failed_count} failed" if failed_count > 0
    details << "#{error_count} errors" if error_count > 0
    printf("%-10s %s\n", "Testcases:", "#{details.join(', ')}, #{passed} passed#{time_str}")
  else
    printf("%-10s %s\n", "Testcases:", "#{total_tests} tests passed#{time_str}")
  end

  printf("%-10s %s\n", "Files:", "#{successful_files} of #{total_files} passed")
end

#live_status_capabilitiesObject



264
265
266
267
268
269
270
# File 'lib/tryouts/cli/formatters/verbose.rb', line 264

def live_status_capabilities
  {
    supports_coordination: true,     # Verbose can work with coordinated output
    output_frequency: :high,         # Outputs frequently for each test
    requires_tty: false,             # Works without TTY
  }
end

#parser_warnings(file_path, warnings:) ⇒ Object



43
44
45
46
47
48
49
50
51
52
53
54
# File 'lib/tryouts/cli/formatters/verbose.rb', line 43

def parser_warnings(file_path, warnings:)
  return if warnings.empty? || !@options.fetch(:warnings, true)

  puts
  puts Console.color(:yellow, "Parser Warnings:")
  warnings.each do |warning|
    puts "  #{Console.color(:yellow, 'WARNING')}: #{warning.message} (line #{warning.line_number})"
    puts "    #{Console.color(:dim, warning.context)}" unless warning.context.empty?
    puts "    #{Console.color(:blue, warning.suggestion)}"
  end
  puts
end

#phase_header(message, file_count: nil) ⇒ Object

Phase-level output



21
22
23
24
25
# File 'lib/tryouts/cli/formatters/verbose.rb', line 21

def phase_header(message, file_count: nil)
  return if message.include?('EXECUTING') # Skip execution phase headers

  puts("=== #{message} ===")
end

#setup_output(output_text) ⇒ Object



189
190
191
192
193
194
195
# File 'lib/tryouts/cli/formatters/verbose.rb', line 189

def setup_output(output_text)
  return if output_text.strip.empty?

  output_text.lines.each do |line|
    puts indent_text(line.chomp, 0)
  end
end

#setup_start(line_range:) ⇒ Object

Setup/teardown operations



183
184
185
186
187
# File 'lib/tryouts/cli/formatters/verbose.rb', line 183

def setup_start(line_range:)
  message = "Executing global setup (lines #{line_range.first}..#{line_range.last})"
  puts indent_text(Console.color(:cyan, message), 1)
  puts
end

#teardown_output(output_text) ⇒ Object



202
203
204
205
206
207
208
# File 'lib/tryouts/cli/formatters/verbose.rb', line 202

def teardown_output(output_text)
  return if output_text.strip.empty?

  output_text.lines.each do |line|
    puts indent_text(line.chomp, 0)
  end
end

#teardown_start(line_range:) ⇒ Object



197
198
199
200
# File 'lib/tryouts/cli/formatters/verbose.rb', line 197

def teardown_start(line_range:)
  message = "Executing teardown (lines #{line_range.first}..#{line_range.last})"
  puts indent_text(Console.color(:cyan, message), 1)
end

#test_output(test_case:, output_text:, result_packet:) ⇒ Object



168
169
170
171
172
173
174
175
176
177
178
179
180
# File 'lib/tryouts/cli/formatters/verbose.rb', line 168

def test_output(test_case:, output_text:, result_packet:)
  return if output_text.nil? || output_text.strip.empty?

  puts indent_text('Test Output:', 2)
  puts indent_text(Console.color(:dim, '--- BEGIN OUTPUT ---'), 2)

  output_text.lines.each do |line|
    puts indent_text(line.chomp, 3)
  end

  puts indent_text(Console.color(:dim, '--- END OUTPUT ---'), 2)
  puts
end

#test_result(result_packet) ⇒ Object



134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
# File 'lib/tryouts/cli/formatters/verbose.rb', line 134

def test_result(result_packet)
  should_show = @show_passed || !result_packet.passed?
  return unless should_show

  status_line = case result_packet.status
  when :passed
    Console.color(:green, 'PASSED')
  when :failed
    Console.color(:red, 'FAILED')
  when :error
    Console.color(:red, 'ERROR')
  when :skipped
    Console.color(:yellow, 'SKIPPED')
  else
    'UNKNOWN'
  end

  test_case = result_packet.test_case
  location = "#{Console.pretty_path(test_case.path)}:#{test_case.first_expectation_line + 1}"
  puts
  puts indent_text("#{status_line} @ #{location}", 1)

  # Show source code for verbose mode
  show_test_source_code(test_case)

  # Show failure details for failed tests
  if result_packet.failed? || result_packet.error?
    show_failure_details(test_case, result_packet.actual_results, result_packet.expected_results, result_packet.result_value)
  # Show exception details for passed exception expectations
  elsif result_packet.passed? && has_exception_expectations?(test_case)
    show_exception_details(test_case, result_packet.actual_results, result_packet.expected_results, result_packet.result_value)
  end
end

#test_start(test_case:, index:, total:) ⇒ Object

Test-level operations



125
126
127
128
129
130
131
132
# File 'lib/tryouts/cli/formatters/verbose.rb', line 125

def test_start(test_case:, index:, total:)
  return unless @show_passed

  desc = test_case.description.to_s
  desc = 'Unnamed test' if desc.empty?
  message = "Test #{index}/#{total}: #{desc}"
  puts indent_text(Console.color(:dim, message), 1)
end

#trace_info(message, level: 0) ⇒ Object



243
244
245
246
247
248
# File 'lib/tryouts/cli/formatters/verbose.rb', line 243

def trace_info(message, level: 0)
  return unless @show_trace

  prefix = Console.color(:dim, 'TRACE')
  puts indent_text("#{prefix} #{message}", level + 1)
end