tryouts 2.4.1 → 3.0.0.pre2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,246 @@
1
+ # lib/tryouts/cli/formatters/compact.rb
2
+
3
+ class Tryouts
4
+ class CLI
5
+ # Compact single-line formatter focused on results
6
+ class CompactFormatter
7
+ include FormatterInterface
8
+
9
+ def initialize(options = {})
10
+ @show_debug = options.fetch(:debug, false)
11
+ @show_trace = options.fetch(:trace, false)
12
+ @show_passed = options.fetch(:show_passed, true)
13
+ end
14
+
15
+ # Phase-level output - minimal for compact mode
16
+ def phase_header(message, _file_count = nil, level = 0)
17
+ # Skip execution phase headers in compact mode - they create unwanted empty lines
18
+ return if level >= 1
19
+
20
+ text = "#{message}..."
21
+ puts indent_text(text, level)
22
+ end
23
+
24
+ # File-level operations - compact single lines
25
+ def file_start(file_path, context_info = {})
26
+ return unless @show_debug
27
+
28
+ framework = context_info[:framework] || :direct
29
+ context = context_info[:context] || :fresh
30
+ pretty_path = Console.pretty_path(file_path)
31
+
32
+ puts indent_text("Running: #{pretty_path} (#{framework}/#{context})", 1)
33
+ end
34
+
35
+ def file_parsed(_file_path, test_count, setup_present: false, teardown_present: false)
36
+ # Don't show parsing info in compact mode unless debug
37
+ return unless @show_debug
38
+
39
+ extras = []
40
+ extras << 'setup' if setup_present
41
+ extras << 'teardown' if teardown_present
42
+ suffix = extras.empty? ? '' : " +#{extras.join(',')}"
43
+
44
+ puts indent_text("Parsed #{test_count} tests#{suffix}", 1)
45
+ end
46
+
47
+ def file_execution_start(file_path, test_count, _context_mode)
48
+ pretty_path = Console.pretty_path(file_path)
49
+ puts
50
+ puts "#{pretty_path}: #{test_count} tests"
51
+ end
52
+
53
+ def file_result(_file_path, total_tests, failed_count, error_count, elapsed_time)
54
+ detail = []
55
+ if failed_count > 0
56
+ status = Console.color(:red, '✗')
57
+ detail << "#{failed_count}/#{total_tests} failed"
58
+ else
59
+ status = Console.color(:green, '✓')
60
+ detail << "#{total_tests} passed"
61
+ end
62
+
63
+ if error_count > 0
64
+ status = Console.color(:yellow, '⚠') if failed_count == 0
65
+ detail << "#{error_count} errors"
66
+ end
67
+
68
+ time_str = if elapsed_time
69
+ if elapsed_time < 2
70
+ " (#{(elapsed_time * 1000).to_i}ms)"
71
+ else
72
+ " (#{elapsed_time.round(2)}s)"
73
+ end
74
+ else
75
+ ''
76
+ end
77
+ puts " #{status} #{detail.join(', ')}#{time_str}"
78
+ end
79
+
80
+ # Test-level operations - only show in debug mode for compact
81
+ def test_start(test_case, index, _total)
82
+ return unless @show_debug
83
+
84
+ desc = test_case.description.to_s
85
+ desc = "test #{index}" if desc.empty?
86
+ puts " Running: #{desc}"
87
+ end
88
+
89
+ def test_result(test_case, result_status, actual_results = [], _elapsed_time = nil)
90
+ # Only show failed tests in compact mode unless show_passed is true
91
+ return if result_status == :passed && !@show_passed
92
+
93
+ desc = test_case.description.to_s
94
+ desc = 'unnamed test' if desc.empty?
95
+
96
+ case result_status
97
+ when :passed
98
+ status = Console.color(:green, '✓')
99
+ when :failed
100
+ status = Console.color(:red, '✗')
101
+ if actual_results.any?
102
+ failure_info = " (got: #{actual_results.first.inspect})"
103
+ desc += failure_info
104
+ end
105
+ when :skipped
106
+ status = Console.color(:yellow, '-')
107
+ else
108
+ status = '?'
109
+ end
110
+
111
+ puts indent_text("#{status} #{desc}", 1)
112
+ end
113
+
114
+ def test_output(test_case, output_text)
115
+ # In compact mode, only show output for failed tests and only if debug mode is enabled
116
+ return if output_text.nil? || output_text.strip.empty?
117
+ return unless @show_debug
118
+
119
+ puts " Output: #{output_text.lines.count} lines"
120
+ if output_text.lines.count <= 3
121
+ output_text.lines.each do |line|
122
+ puts " #{line.chomp}"
123
+ end
124
+ else
125
+ puts " #{output_text.lines.first.chomp}"
126
+ puts " ... (#{output_text.lines.count - 2} more lines)"
127
+ puts " #{output_text.lines.last.chomp}"
128
+ end
129
+ end
130
+
131
+ # Setup/teardown operations - minimal output
132
+ def setup_start(_line_range)
133
+ # No file setup start output for compact
134
+ end
135
+
136
+ def setup_output(output_text)
137
+ return if output_text.strip.empty?
138
+ return unless @show_debug
139
+
140
+ # In compact mode, just show that there was output
141
+ lines = output_text.lines.count
142
+ puts " Setup output (#{lines} lines)"
143
+ end
144
+
145
+ def teardown_start(_line_range)
146
+ return unless @show_debug
147
+
148
+ puts ' Teardown...'
149
+ end
150
+
151
+ def teardown_output(output_text)
152
+ return if output_text.strip.empty?
153
+ return unless @show_debug
154
+
155
+ # In compact mode, just show that there was output
156
+ lines = output_text.lines.count
157
+ puts " Teardown output (#{lines} lines)"
158
+ end
159
+
160
+ # Summary operations
161
+ def batch_summary(total_tests, failed_count, elapsed_time)
162
+ # Skip - file_result already shows this information with better alignment
163
+ end
164
+
165
+ def grand_total(total_tests, failed_count, error_count, successful_files, total_files, elapsed_time)
166
+ puts
167
+ puts '=' * 50
168
+
169
+ issues_count = failed_count + error_count
170
+ if issues_count > 0
171
+ passed = total_tests - issues_count
172
+ details = []
173
+ details << "#{failed_count} failed" if failed_count > 0
174
+ details << "#{error_count} errors" if error_count > 0
175
+ result = Console.color(:red, "#{details.join(', ')}, #{passed} passed")
176
+ else
177
+ result = Console.color(:green, "#{total_tests} tests passed")
178
+ end
179
+
180
+ time_str = if elapsed_time < 2
181
+ "#{(elapsed_time * 1000).to_i}ms"
182
+ else
183
+ "#{elapsed_time.round(2)}s"
184
+ end
185
+
186
+ puts "Total: #{result} (#{time_str})"
187
+ puts "Files: #{successful_files} of #{total_files} successful"
188
+ end
189
+
190
+ # Debug and diagnostic output - minimal in compact mode
191
+ def debug_info(message, level = 0)
192
+ return unless @show_debug
193
+
194
+ puts indent_text("DEBUG: #{message}", level)
195
+ end
196
+
197
+ def trace_info(message, level = 0)
198
+ return unless @show_trace
199
+
200
+ puts indent_text("TRACE: #{message}", level)
201
+ end
202
+
203
+ def error_message(message, backtrace = nil)
204
+ puts Console.color(:red, "ERROR: #{message}")
205
+
206
+ return unless backtrace && @show_debug
207
+
208
+ backtrace.first(3).each do |line|
209
+ puts indent_text(line.chomp, 1)
210
+ end
211
+ end
212
+
213
+ # Utility methods
214
+ def raw_output(text)
215
+ puts text
216
+ end
217
+
218
+ def separator(style = :light)
219
+ case style
220
+ when :heavy
221
+ puts '=' * 50
222
+ when :light
223
+ puts '-' * 50
224
+ when :dotted
225
+ puts '.' * 50
226
+ else
227
+ puts '-' * 50
228
+ end
229
+ end
230
+ end
231
+
232
+ # Compact formatter that only shows failures and errors
233
+ class CompactFailsFormatter < CompactFormatter
234
+ def initialize(options = {})
235
+ super(options.merge(show_passed: false))
236
+ end
237
+
238
+ def test_result(test_case, result_status, actual_results = [], elapsed_time = nil)
239
+ # Only show failed/error tests
240
+ return if result_status == :passed
241
+
242
+ super
243
+ end
244
+ end
245
+ end
246
+ end
@@ -0,0 +1,52 @@
1
+ # lib/tryouts/cli/formatters/factory.rb
2
+
3
+ class Tryouts
4
+ class CLI
5
+ # Factory for creating formatters and output managers
6
+ class FormatterFactory
7
+ def self.create_output_manager(options = {})
8
+ formatter = create_formatter(options)
9
+ OutputManager.new(formatter)
10
+ end
11
+
12
+ def self.create_formatter(options = {})
13
+ # Map boolean flags to format symbols if format not explicitly set
14
+ format = options[:format]&.to_sym || determine_format_from_flags(options)
15
+
16
+ case format
17
+ when :verbose
18
+ if options[:fails_only]
19
+ VerboseFailsFormatter.new(options)
20
+ else
21
+ VerboseFormatter.new(options)
22
+ end
23
+ when :compact
24
+ if options[:fails_only]
25
+ CompactFailsFormatter.new(options)
26
+ else
27
+ CompactFormatter.new(options)
28
+ end
29
+ when :quiet
30
+ if options[:fails_only]
31
+ QuietFailsFormatter.new(options)
32
+ else
33
+ QuietFormatter.new(options)
34
+ end
35
+ else
36
+ VerboseFormatter.new(options) # Default to verbose
37
+ end
38
+ end
39
+
40
+ class << self
41
+ private
42
+
43
+ def determine_format_from_flags(options)
44
+ return :quiet if options[:quiet]
45
+ return :verbose if options[:verbose]
46
+
47
+ :compact # Default
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,140 @@
1
+ # lib/tryouts/cli/formatters/output_manager.rb
2
+
3
+ class Tryouts
4
+ class CLI
5
+ # Output manager that coordinates all output through formatters
6
+ class OutputManager
7
+ attr_reader :formatter
8
+
9
+ def initialize(formatter)
10
+ @formatter = formatter
11
+ @indent_level = 0
12
+ end
13
+
14
+ # Phase-level methods
15
+ def processing_phase(file_count, level = 0)
16
+ @formatter.phase_header("PROCESSING #{file_count} FILES", file_count, level)
17
+ end
18
+
19
+ def execution_phase(test_count, level = 1)
20
+ @formatter.phase_header("EXECUTING #{test_count} TESTS", test_count, level)
21
+ end
22
+
23
+ def error_phase(level = 1)
24
+ @formatter.phase_header('ERROR DETAILS', level)
25
+ end
26
+
27
+ # File-level methods
28
+ def file_start(file_path, framework: :direct, context: :fresh)
29
+ context_info = { framework: framework, context: context }
30
+ @formatter.file_start(file_path, context_info)
31
+ end
32
+
33
+ def file_parsed(file_path, test_count, setup_present: false, teardown_present: false)
34
+ with_indent(1) do
35
+ @formatter.file_parsed(file_path, test_count,
36
+ setup_present: setup_present,
37
+ teardown_present: teardown_present
38
+ )
39
+ end
40
+ end
41
+
42
+ def file_execution_start(file_path, test_count, context_mode)
43
+ @formatter.file_execution_start(file_path, test_count, context_mode)
44
+ end
45
+
46
+ def file_success(file_path, total_tests, failed_count, error_count, elapsed_time)
47
+ with_indent(1) do
48
+ @formatter.file_result(file_path, total_tests, failed_count, error_count, elapsed_time)
49
+ end
50
+ end
51
+
52
+ def file_failure(file_path, error_message, backtrace = nil)
53
+ with_indent(1) do
54
+ @formatter.error_message("#{Console.pretty_path(file_path)}: #{error_message}", backtrace)
55
+ end
56
+ end
57
+
58
+ # Test-level methods
59
+ def test_start(test_case, index, total)
60
+ with_indent(2) do
61
+ @formatter.test_start(test_case, index, total)
62
+ end
63
+ end
64
+
65
+ def test_result(test_case, result_status, actual_results = [], elapsed_time = nil)
66
+ @formatter.test_result(test_case, result_status, actual_results, elapsed_time)
67
+ end
68
+
69
+ def test_output(test_case, output_text)
70
+ @formatter.test_output(test_case, output_text)
71
+ end
72
+
73
+ # Setup/teardown methods
74
+ def setup_start(line_range)
75
+ with_indent(2) do
76
+ @formatter.setup_start(line_range)
77
+ end
78
+ end
79
+
80
+ def setup_output(output_text)
81
+ @formatter.setup_output(output_text)
82
+ end
83
+
84
+ def teardown_start(line_range)
85
+ with_indent(2) do
86
+ @formatter.teardown_start(line_range)
87
+ end
88
+ end
89
+
90
+ def teardown_output(output_text)
91
+ @formatter.teardown_output(output_text)
92
+ end
93
+
94
+ # Summary methods
95
+ def batch_summary(total_tests, failed_count, elapsed_time)
96
+ @formatter.batch_summary(total_tests, failed_count, elapsed_time)
97
+ end
98
+
99
+ def grand_total(total_tests, failed_count, error_count, successful_files, total_files, elapsed_time)
100
+ @formatter.grand_total(total_tests, failed_count, error_count, successful_files, total_files, elapsed_time)
101
+ end
102
+
103
+ # Debug methods
104
+ def info(message, level = 0)
105
+ with_indent(level) do
106
+ @formatter.debug_info(message, level)
107
+ end
108
+ end
109
+
110
+ def trace(message, level = 0)
111
+ with_indent(level) do
112
+ @formatter.trace_info(message, level)
113
+ end
114
+ end
115
+
116
+ def error(message, backtrace = nil)
117
+ @formatter.error_message(message, backtrace)
118
+ end
119
+
120
+ # Utility methods
121
+ def raw(text)
122
+ @formatter.raw_output(text)
123
+ end
124
+
125
+ def separator(style = :light)
126
+ @formatter.separator(style)
127
+ end
128
+
129
+ private
130
+
131
+ def with_indent(level)
132
+ old_level = @indent_level
133
+ @indent_level = level
134
+ yield
135
+ ensure
136
+ @indent_level = old_level
137
+ end
138
+ end
139
+ end
140
+ end
@@ -0,0 +1,159 @@
1
+ # lib/tryouts/cli/formatters/quiet.rb
2
+
3
+ class Tryouts
4
+ class CLI
5
+ # Minimal output formatter - only shows essential information
6
+ class QuietFormatter
7
+ include FormatterInterface
8
+
9
+ def initialize(options = {})
10
+ @show_errors = options.fetch(:show_errors, true)
11
+ @show_final_summary = options.fetch(:show_final_summary, true)
12
+ @current_file = nil
13
+ end
14
+
15
+ # Phase-level output - silent
16
+ def phase_header(message, file_count = nil, level = nil)
17
+ # Silent in quiet mode
18
+ end
19
+
20
+ # File-level operations - minimal
21
+ def file_start(file_path, context_info = {})
22
+ # Silent in quiet mode
23
+ end
24
+
25
+ def file_parsed(file_path, test_count, setup_present: false, teardown_present: false)
26
+ # Silent in quiet mode
27
+ end
28
+
29
+ def file_execution_start(file_path, _test_count, _context_mode)
30
+ @current_file = file_path
31
+ end
32
+
33
+ def file_result(file_path, total_tests, failed_count, error_count, elapsed_time)
34
+ # Silent in quiet mode - results shown in batch_summary
35
+ end
36
+
37
+ # Test-level operations - dot notation
38
+ def test_start(test_case, index, total)
39
+ # Silent in quiet mode
40
+ end
41
+
42
+ def test_result(_test_case, result_status, _actual_results = [], _elapsed_time = nil)
43
+ case result_status
44
+ when :passed
45
+ print Console.color(:green, '.')
46
+ when :failed
47
+ print Console.color(:red, 'F')
48
+ when :error
49
+ print Console.color(:red, 'E')
50
+ when :skipped
51
+ print Console.color(:yellow, 'S')
52
+ else
53
+ print '?'
54
+ end
55
+ $stdout.flush
56
+ end
57
+
58
+ def test_output(test_case, output_text)
59
+ # Silent in quiet mode - could optionally show output for failed tests only
60
+ # For now, keeping it completely silent
61
+ end
62
+
63
+ # Setup/teardown operations - silent
64
+ def setup_start(line_range)
65
+ # Silent in quiet mode
66
+ end
67
+
68
+ def setup_output(output_text)
69
+ # Silent in quiet mode
70
+ end
71
+
72
+ def teardown_start(line_range)
73
+ # Silent in quiet mode
74
+ end
75
+
76
+ def teardown_output(output_text)
77
+ # Silent in quiet mode
78
+ end
79
+
80
+ # Summary operations - show results
81
+ def batch_summary(total_tests, failed_count, elapsed_time)
82
+ return unless @show_final_summary
83
+
84
+ puts # New line after dots
85
+
86
+ if failed_count > 0
87
+ passed = total_tests - failed_count
88
+ time_str = elapsed_time ? " (#{elapsed_time.round(2)}s)" : ''
89
+ puts "#{failed_count} failed, #{passed} passed#{time_str}"
90
+ else
91
+ time_str = elapsed_time ? " (#{elapsed_time.round(2)}s)" : ''
92
+ puts "#{total_tests} passed#{time_str}"
93
+ end
94
+ end
95
+
96
+ def grand_total(total_tests, failed_count, error_count, successful_files, total_files, elapsed_time)
97
+ return unless @show_final_summary
98
+
99
+ puts
100
+
101
+ time_str = if elapsed_time < 2
102
+ "#{(elapsed_time * 1000).to_i}ms"
103
+ else
104
+ "#{elapsed_time.round(2)}s"
105
+ end
106
+
107
+ issues_count = failed_count + error_count
108
+ if issues_count > 0
109
+ passed = total_tests - issues_count
110
+ details = []
111
+ details << "#{failed_count} failed" if failed_count > 0
112
+ details << "#{error_count} errors" if error_count > 0
113
+ puts Console.color(:red, "Total: #{details.join(', ')}, #{passed} passed (#{time_str})")
114
+ else
115
+ puts Console.color(:green, "Total: #{total_tests} passed (#{time_str})")
116
+ end
117
+
118
+ if total_files > 1
119
+ puts "Files: #{successful_files} of #{total_files} successful"
120
+ end
121
+ end
122
+
123
+ # Debug and diagnostic output - silent unless errors
124
+ def debug_info(message, level = 0)
125
+ # Silent in quiet mode
126
+ end
127
+
128
+ def trace_info(message, level = 0)
129
+ # Silent in quiet mode
130
+ end
131
+
132
+ def error_message(message, _details = nil)
133
+ return unless @show_errors
134
+
135
+ puts
136
+ puts Console.color(:red, "ERROR: #{message}")
137
+ end
138
+
139
+ # Utility methods
140
+ def raw_output(text)
141
+ puts text if @show_final_summary
142
+ end
143
+
144
+ def separator(style = :light)
145
+ # Silent in quiet mode
146
+ end
147
+ end
148
+
149
+ # Quiet formatter that only shows dots for failures and errors
150
+ class QuietFailsFormatter < QuietFormatter
151
+ def test_result(test_case, result_status, actual_results = [], elapsed_time = nil)
152
+ # Only show non-pass dots in fails mode
153
+ return if result_status == :passed
154
+
155
+ super
156
+ end
157
+ end
158
+ end
159
+ end