tryouts 2.4.1 → 3.0.0.pre

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,218 @@
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 = elapsed_time ? " (#{elapsed_time.round(2)}s)" : ''
69
+ puts " #{status} #{detail.join(', ')}#{time_str}"
70
+ end
71
+
72
+ # Test-level operations - only show in debug mode for compact
73
+ def test_start(test_case, index, _total)
74
+ return unless @show_debug
75
+
76
+ desc = test_case.description.to_s
77
+ desc = "test #{index}" if desc.empty?
78
+ puts " Running: #{desc}"
79
+ end
80
+
81
+ def test_result(test_case, result_status, actual_results = [], _elapsed_time = nil)
82
+ # Only show failed tests in compact mode unless show_passed is true
83
+ return if result_status == :passed && !@show_passed
84
+
85
+ desc = test_case.description.to_s
86
+ desc = 'unnamed test' if desc.empty?
87
+
88
+ case result_status
89
+ when :passed
90
+ status = Console.color(:green, '✓')
91
+ when :failed
92
+ status = Console.color(:red, '✗')
93
+ if actual_results.any?
94
+ failure_info = " (got: #{actual_results.first.inspect})"
95
+ desc += failure_info
96
+ end
97
+ when :skipped
98
+ status = Console.color(:yellow, '-')
99
+ else
100
+ status = '?'
101
+ end
102
+
103
+ puts indent_text("#{status} #{desc}", 1)
104
+ end
105
+
106
+ def test_output(test_case, output_text)
107
+ # In compact mode, only show output for failed tests and only if debug mode is enabled
108
+ return if output_text.nil? || output_text.strip.empty?
109
+ return unless @show_debug
110
+
111
+ puts " Output: #{output_text.lines.count} lines"
112
+ if output_text.lines.count <= 3
113
+ output_text.lines.each do |line|
114
+ puts " #{line.chomp}"
115
+ end
116
+ else
117
+ puts " #{output_text.lines.first.chomp}"
118
+ puts " ... (#{output_text.lines.count - 2} more lines)"
119
+ puts " #{output_text.lines.last.chomp}"
120
+ end
121
+ end
122
+
123
+ # Setup/teardown operations - minimal output
124
+ def setup_start(_line_range)
125
+ # No file setup start output for compact
126
+ end
127
+
128
+ def setup_output(output_text)
129
+ return if output_text.strip.empty?
130
+ return unless @show_debug
131
+
132
+ # In compact mode, just show that there was output
133
+ lines = output_text.lines.count
134
+ puts " Setup output (#{lines} lines)"
135
+ end
136
+
137
+ def teardown_start(_line_range)
138
+ return unless @show_debug
139
+
140
+ puts ' Teardown...'
141
+ end
142
+
143
+ def teardown_output(output_text)
144
+ return if output_text.strip.empty?
145
+ return unless @show_debug
146
+
147
+ # In compact mode, just show that there was output
148
+ lines = output_text.lines.count
149
+ puts " Teardown output (#{lines} lines)"
150
+ end
151
+
152
+ # Summary operations
153
+ def batch_summary(total_tests, failed_count, elapsed_time)
154
+ # Skip - file_result already shows this information with better alignment
155
+ end
156
+
157
+ def grand_total(total_tests, failed_count, error_count, successful_files, total_files, elapsed_time)
158
+ puts
159
+ puts '=' * 50
160
+
161
+ issues_count = failed_count + error_count
162
+ if issues_count > 0
163
+ passed = total_tests - issues_count
164
+ details = []
165
+ details << "#{failed_count} failed" if failed_count > 0
166
+ details << "#{error_count} errors" if error_count > 0
167
+ result = Console.color(:red, "#{details.join(', ')}, #{passed} passed")
168
+ else
169
+ result = Console.color(:green, "#{total_tests} tests passed")
170
+ end
171
+
172
+ puts "Total: #{result} (#{elapsed_time.round(2)}s)"
173
+ puts "Files: #{successful_files} of #{total_files} successful"
174
+ end
175
+
176
+ # Debug and diagnostic output - minimal in compact mode
177
+ def debug_info(message, level = 0)
178
+ return unless @show_debug
179
+
180
+ puts indent_text("DEBUG: #{message}", level)
181
+ end
182
+
183
+ def trace_info(message, level = 0)
184
+ return unless @show_trace
185
+
186
+ puts indent_text("TRACE: #{message}", level)
187
+ end
188
+
189
+ def error_message(message, backtrace = nil)
190
+ puts Console.color(:red, "ERROR: #{message}")
191
+
192
+ return unless backtrace && @show_debug
193
+
194
+ backtrace.first(3).each do |line|
195
+ puts indent_text(line.chomp, 1)
196
+ end
197
+ end
198
+
199
+ # Utility methods
200
+ def raw_output(text)
201
+ puts text
202
+ end
203
+
204
+ def separator(style = :light)
205
+ case style
206
+ when :heavy
207
+ puts '=' * 50
208
+ when :light
209
+ puts '-' * 50
210
+ when :dotted
211
+ puts '.' * 50
212
+ else
213
+ puts '-' * 50
214
+ end
215
+ end
216
+ end
217
+ end
218
+ end
@@ -0,0 +1,44 @@
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
+ CompactFormatter.new(options)
25
+ when :quiet
26
+ QuietFormatter.new(options)
27
+ else
28
+ VerboseFormatter.new(options) # Default to verbose
29
+ end
30
+ end
31
+
32
+ class << self
33
+ private
34
+
35
+ def determine_format_from_flags(options)
36
+ return :quiet if options[:quiet]
37
+ return :verbose if options[:verbose]
38
+
39
+ :compact # Default
40
+ end
41
+ end
42
+ end
43
+ end
44
+ 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,143 @@
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
+ issues_count = failed_count + error_count
102
+ if issues_count > 0
103
+ passed = total_tests - issues_count
104
+ details = []
105
+ details << "#{failed_count} failed" if failed_count > 0
106
+ details << "#{error_count} errors" if error_count > 0
107
+ puts Console.color(:red, "Total: #{details.join(', ')}, #{passed} passed (#{elapsed_time.round(2)}s)")
108
+ else
109
+ puts Console.color(:green, "Total: #{total_tests} passed (#{elapsed_time.round(2)}s)")
110
+ end
111
+
112
+ if total_files > 1
113
+ puts "Files: #{successful_files} of #{total_files} successful"
114
+ end
115
+ end
116
+
117
+ # Debug and diagnostic output - silent unless errors
118
+ def debug_info(message, level = 0)
119
+ # Silent in quiet mode
120
+ end
121
+
122
+ def trace_info(message, level = 0)
123
+ # Silent in quiet mode
124
+ end
125
+
126
+ def error_message(message, _details = nil)
127
+ return unless @show_errors
128
+
129
+ puts
130
+ puts Console.color(:red, "ERROR: #{message}")
131
+ end
132
+
133
+ # Utility methods
134
+ def raw_output(text)
135
+ puts text if @show_final_summary
136
+ end
137
+
138
+ def separator(style = :light)
139
+ # Silent in quiet mode
140
+ end
141
+ end
142
+ end
143
+ end