tryouts 3.0.0.pre2 → 3.1.0
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.
- checksums.yaml +4 -4
- data/README.md +51 -115
- data/exe/try +25 -4
- data/lib/tryouts/cli/formatters/base.rb +33 -21
- data/lib/tryouts/cli/formatters/compact.rb +122 -84
- data/lib/tryouts/cli/formatters/factory.rb +1 -1
- data/lib/tryouts/cli/formatters/output_manager.rb +13 -2
- data/lib/tryouts/cli/formatters/quiet.rb +22 -16
- data/lib/tryouts/cli/formatters/verbose.rb +101 -60
- data/lib/tryouts/console.rb +53 -17
- data/lib/tryouts/expectation_evaluators/base.rb +101 -0
- data/lib/tryouts/expectation_evaluators/boolean.rb +60 -0
- data/lib/tryouts/expectation_evaluators/exception.rb +61 -0
- data/lib/tryouts/expectation_evaluators/expectation_result.rb +67 -0
- data/lib/tryouts/expectation_evaluators/false.rb +60 -0
- data/lib/tryouts/expectation_evaluators/intentional_failure.rb +74 -0
- data/lib/tryouts/expectation_evaluators/output.rb +101 -0
- data/lib/tryouts/expectation_evaluators/performance_time.rb +81 -0
- data/lib/tryouts/expectation_evaluators/regex_match.rb +57 -0
- data/lib/tryouts/expectation_evaluators/registry.rb +66 -0
- data/lib/tryouts/expectation_evaluators/regular.rb +67 -0
- data/lib/tryouts/expectation_evaluators/result_type.rb +51 -0
- data/lib/tryouts/expectation_evaluators/true.rb +58 -0
- data/lib/tryouts/prism_parser.rb +112 -15
- data/lib/tryouts/test_executor.rb +6 -4
- data/lib/tryouts/test_runner.rb +1 -1
- data/lib/tryouts/testbatch.rb +288 -98
- data/lib/tryouts/testcase.rb +141 -0
- data/lib/tryouts/translators/minitest_translator.rb +40 -11
- data/lib/tryouts/translators/rspec_translator.rb +47 -12
- data/lib/tryouts/version.rb +1 -1
- data/lib/tryouts.rb +42 -0
- metadata +16 -3
@@ -13,26 +13,32 @@ class Tryouts
|
|
13
13
|
end
|
14
14
|
|
15
15
|
# Phase-level output - minimal for compact mode
|
16
|
-
def phase_header(message,
|
17
|
-
#
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
16
|
+
def phase_header(message, file_count = nil, level = 0, io = $stderr)
|
17
|
+
# Show processing header but skip execution phase headers to avoid empty lines
|
18
|
+
case level
|
19
|
+
when 0
|
20
|
+
# Main processing header
|
21
|
+
text = file_count ? "#{message}" : "#{message}..."
|
22
|
+
io.puts text
|
23
|
+
when 1
|
24
|
+
# Skip execution phase headers - they create unwanted empty lines
|
25
|
+
return
|
26
|
+
else
|
27
|
+
# Other phase headers with minimal formatting
|
28
|
+
io.puts indent_text(message, level - 1)
|
29
|
+
end
|
22
30
|
end
|
23
31
|
|
24
32
|
# File-level operations - compact single lines
|
25
|
-
def file_start(file_path,
|
26
|
-
|
27
|
-
|
28
|
-
framework = context_info[:framework] || :direct
|
29
|
-
context = context_info[:context] || :fresh
|
30
|
-
pretty_path = Console.pretty_path(file_path)
|
33
|
+
def file_start(file_path, _context_info = {}, io = $stderr)
|
34
|
+
# See file_execution_start
|
35
|
+
end
|
31
36
|
|
32
|
-
|
37
|
+
def file_end(file_path, context_info = {}, io = $stderr)
|
38
|
+
# No output in compact mode
|
33
39
|
end
|
34
40
|
|
35
|
-
def file_parsed(_file_path, test_count, setup_present: false, teardown_present: false)
|
41
|
+
def file_parsed(_file_path, test_count, io = $stderr, setup_present: false, teardown_present: false)
|
36
42
|
# Don't show parsing info in compact mode unless debug
|
37
43
|
return unless @show_debug
|
38
44
|
|
@@ -41,90 +47,119 @@ class Tryouts
|
|
41
47
|
extras << 'teardown' if teardown_present
|
42
48
|
suffix = extras.empty? ? '' : " +#{extras.join(',')}"
|
43
49
|
|
44
|
-
puts indent_text("Parsed #{test_count} tests#{suffix}", 1)
|
50
|
+
io.puts indent_text("Parsed #{test_count} tests#{suffix}", 1)
|
45
51
|
end
|
46
52
|
|
47
|
-
def file_execution_start(file_path, test_count, _context_mode)
|
53
|
+
def file_execution_start(file_path, test_count, _context_mode, io = $stderr)
|
48
54
|
pretty_path = Console.pretty_path(file_path)
|
49
|
-
puts
|
50
|
-
puts "#{pretty_path}: #{test_count} tests"
|
55
|
+
io.puts "#{pretty_path}: #{test_count} tests"
|
51
56
|
end
|
52
57
|
|
53
|
-
|
54
|
-
|
55
|
-
|
58
|
+
# Summary operations
|
59
|
+
def batch_summary(total_tests, failed_count, elapsed_time)
|
60
|
+
# Skip - file_result already shows this information with better alignment
|
61
|
+
end
|
62
|
+
|
63
|
+
def file_result(_file_path, total_tests, failed_count, error_count, elapsed_time, io = $stdout)
|
64
|
+
issues_count = failed_count + error_count
|
65
|
+
passed_count = total_tests - issues_count
|
66
|
+
details = [
|
67
|
+
# "#{passed_count} passed",
|
68
|
+
]
|
69
|
+
|
70
|
+
if issues_count > 0
|
56
71
|
status = Console.color(:red, '✗')
|
57
|
-
|
72
|
+
details << "#{passed_count}/#{total_tests} passed"
|
58
73
|
else
|
59
74
|
status = Console.color(:green, '✓')
|
60
|
-
|
75
|
+
details << "#{total_tests} passed"
|
61
76
|
end
|
62
77
|
|
63
78
|
if error_count > 0
|
79
|
+
status = Console.color(:yellow, '⚠') if error_count == 0
|
80
|
+
details << "#{error_count} errors"
|
81
|
+
end
|
82
|
+
|
83
|
+
if failed_count > 0
|
64
84
|
status = Console.color(:yellow, '⚠') if failed_count == 0
|
65
|
-
|
85
|
+
details << "#{failed_count} failed"
|
66
86
|
end
|
67
87
|
|
68
88
|
time_str = if elapsed_time
|
69
|
-
|
70
|
-
" (#{(elapsed_time * 1000).to_i}ms)"
|
71
|
-
else
|
72
|
-
" (#{elapsed_time.round(2)}s)"
|
73
|
-
end
|
89
|
+
format_timing(elapsed_time)
|
74
90
|
else
|
75
91
|
''
|
76
92
|
end
|
77
|
-
puts " #{status} #{
|
93
|
+
io.puts " #{status} #{details.join(', ')}#{time_str}"
|
78
94
|
end
|
79
95
|
|
80
96
|
# Test-level operations - only show in debug mode for compact
|
81
|
-
def test_start(test_case, index, _total)
|
97
|
+
def test_start(test_case, index, _total, io = $stdout)
|
82
98
|
return unless @show_debug
|
83
99
|
|
84
100
|
desc = test_case.description.to_s
|
85
101
|
desc = "test #{index}" if desc.empty?
|
86
|
-
|
102
|
+
|
103
|
+
io.puts " Running: #{desc}"
|
104
|
+
end
|
105
|
+
|
106
|
+
def test_end(test_case, index, _total, io = $stdout)
|
107
|
+
# No output for test end
|
87
108
|
end
|
88
109
|
|
89
|
-
def test_result(
|
110
|
+
def test_result(result_packet, io = $stdout)
|
90
111
|
# Only show failed tests in compact mode unless show_passed is true
|
91
|
-
return if
|
112
|
+
return if result_packet.passed? && !@show_passed
|
92
113
|
|
114
|
+
test_case = result_packet.test_case
|
93
115
|
desc = test_case.description.to_s
|
94
116
|
desc = 'unnamed test' if desc.empty?
|
95
117
|
|
96
|
-
case
|
118
|
+
case result_packet.status
|
97
119
|
when :passed
|
98
120
|
status = Console.color(:green, '✓')
|
121
|
+
io.puts indent_text("#{status} #{desc}", 1)
|
99
122
|
when :failed
|
100
123
|
status = Console.color(:red, '✗')
|
101
|
-
|
102
|
-
|
103
|
-
|
124
|
+
io.puts indent_text("#{status} #{desc}", 1)
|
125
|
+
|
126
|
+
# Show minimal context for failures
|
127
|
+
if result_packet.actual_results.any?
|
128
|
+
failure_info = "got: #{result_packet.first_actual.inspect}"
|
129
|
+
io.puts indent_text(" #{failure_info}", 1)
|
130
|
+
end
|
131
|
+
|
132
|
+
# Show 1-2 lines of test context if available
|
133
|
+
if test_case.source_lines && test_case.source_lines.size <= 3
|
134
|
+
test_case.source_lines.each do |line|
|
135
|
+
next if line.strip.empty? || line.strip.start_with?('#')
|
136
|
+
io.puts indent_text(" #{line.strip}", 1)
|
137
|
+
break # Only show first relevant line
|
138
|
+
end
|
104
139
|
end
|
105
140
|
when :skipped
|
106
141
|
status = Console.color(:yellow, '-')
|
142
|
+
io.puts indent_text("#{status} #{desc}", 1)
|
107
143
|
else
|
108
144
|
status = '?'
|
145
|
+
io.puts indent_text("#{status} #{desc}", 1)
|
109
146
|
end
|
110
|
-
|
111
|
-
puts indent_text("#{status} #{desc}", 1)
|
112
147
|
end
|
113
148
|
|
114
|
-
def test_output(
|
149
|
+
def test_output(_test_case, output_text, io = $stdout)
|
115
150
|
# In compact mode, only show output for failed tests and only if debug mode is enabled
|
116
151
|
return if output_text.nil? || output_text.strip.empty?
|
117
152
|
return unless @show_debug
|
118
153
|
|
119
|
-
puts " Output: #{output_text.lines.count} lines"
|
154
|
+
io.puts " Output: #{output_text.lines.count} lines"
|
120
155
|
if output_text.lines.count <= 3
|
121
156
|
output_text.lines.each do |line|
|
122
|
-
puts " #{line.chomp}"
|
157
|
+
io.puts " #{line.chomp}"
|
123
158
|
end
|
124
159
|
else
|
125
|
-
puts " #{output_text.lines.first.chomp}"
|
126
|
-
puts " ... (#{output_text.lines.count - 2} more lines)"
|
127
|
-
puts " #{output_text.lines.last.chomp}"
|
160
|
+
io.puts " #{output_text.lines.first.chomp}"
|
161
|
+
io.puts " ... (#{output_text.lines.count - 2} more lines)"
|
162
|
+
io.puts " #{output_text.lines.last.chomp}"
|
128
163
|
end
|
129
164
|
end
|
130
165
|
|
@@ -133,42 +168,37 @@ class Tryouts
|
|
133
168
|
# No file setup start output for compact
|
134
169
|
end
|
135
170
|
|
136
|
-
def setup_output(output_text)
|
171
|
+
def setup_output(output_text, io = $stderr)
|
137
172
|
return if output_text.strip.empty?
|
138
173
|
return unless @show_debug
|
139
174
|
|
140
175
|
# In compact mode, just show that there was output
|
141
176
|
lines = output_text.lines.count
|
142
|
-
puts " Setup output (#{lines} lines)"
|
177
|
+
io.puts " Setup output (#{lines} lines)"
|
143
178
|
end
|
144
179
|
|
145
|
-
def teardown_start(_line_range)
|
180
|
+
def teardown_start(_line_range, io = $stderr)
|
146
181
|
return unless @show_debug
|
147
182
|
|
148
|
-
puts ' Teardown...'
|
183
|
+
io.puts ' Teardown...'
|
149
184
|
end
|
150
185
|
|
151
|
-
def teardown_output(output_text)
|
186
|
+
def teardown_output(output_text, io = $stderr)
|
152
187
|
return if output_text.strip.empty?
|
153
188
|
return unless @show_debug
|
154
189
|
|
155
190
|
# In compact mode, just show that there was output
|
156
191
|
lines = output_text.lines.count
|
157
|
-
puts " Teardown output (#{lines} lines)"
|
192
|
+
io.puts " Teardown output (#{lines} lines)"
|
158
193
|
end
|
159
194
|
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
end
|
164
|
-
|
165
|
-
def grand_total(total_tests, failed_count, error_count, successful_files, total_files, elapsed_time)
|
166
|
-
puts
|
167
|
-
puts '=' * 50
|
195
|
+
def grand_total(total_tests, failed_count, error_count, successful_files, total_files, elapsed_time, io = $stderr)
|
196
|
+
io.puts
|
197
|
+
io.puts '=' * 50
|
168
198
|
|
169
199
|
issues_count = failed_count + error_count
|
170
200
|
if issues_count > 0
|
171
|
-
passed = total_tests - issues_count
|
201
|
+
passed = [total_tests - issues_count, 0].max # Ensure passed never goes negative
|
172
202
|
details = []
|
173
203
|
details << "#{failed_count} failed" if failed_count > 0
|
174
204
|
details << "#{error_count} errors" if error_count > 0
|
@@ -177,54 +207,62 @@ class Tryouts
|
|
177
207
|
result = Console.color(:green, "#{total_tests} tests passed")
|
178
208
|
end
|
179
209
|
|
180
|
-
time_str =
|
181
|
-
"#{(elapsed_time * 1000).to_i}ms"
|
182
|
-
else
|
183
|
-
"#{elapsed_time.round(2)}s"
|
184
|
-
end
|
210
|
+
time_str = format_timing(elapsed_time)
|
185
211
|
|
186
|
-
puts "Total: #{result} (#{time_str})"
|
187
|
-
puts "Files: #{successful_files} of #{total_files} successful"
|
212
|
+
io.puts "Total: #{result} (#{time_str})"
|
213
|
+
io.puts "Files: #{successful_files} of #{total_files} successful"
|
188
214
|
end
|
189
215
|
|
190
216
|
# Debug and diagnostic output - minimal in compact mode
|
191
|
-
def debug_info(message, level = 0)
|
217
|
+
def debug_info(message, level = 0, io = $stderr)
|
192
218
|
return unless @show_debug
|
193
219
|
|
194
|
-
puts indent_text("DEBUG: #{message}", level)
|
220
|
+
io.puts indent_text("DEBUG: #{message}", level)
|
195
221
|
end
|
196
222
|
|
197
|
-
def trace_info(message, level = 0)
|
223
|
+
def trace_info(message, level = 0, io = $stderr)
|
198
224
|
return unless @show_trace
|
199
225
|
|
200
|
-
puts indent_text("TRACE: #{message}", level)
|
226
|
+
io.puts indent_text("TRACE: #{message}", level)
|
201
227
|
end
|
202
228
|
|
203
|
-
def error_message(message, backtrace = nil)
|
204
|
-
puts Console.color(:red, "ERROR: #{message}")
|
229
|
+
def error_message(message, backtrace = nil, io = $stderr)
|
230
|
+
io.puts Console.color(:red, "ERROR: #{message}")
|
205
231
|
|
206
232
|
return unless backtrace && @show_debug
|
207
233
|
|
208
234
|
backtrace.first(3).each do |line|
|
209
|
-
puts indent_text(line.chomp, 1)
|
235
|
+
io.puts indent_text(line.chomp, 1)
|
210
236
|
end
|
211
237
|
end
|
212
238
|
|
213
239
|
# Utility methods
|
214
|
-
def raw_output(text)
|
215
|
-
puts text
|
240
|
+
def raw_output(text, io = $stdout)
|
241
|
+
io.puts text
|
216
242
|
end
|
217
243
|
|
218
|
-
def separator(style = :light)
|
244
|
+
def separator(style = :light, io = $stdout)
|
219
245
|
case style
|
220
246
|
when :heavy
|
221
|
-
puts '=' * 50
|
247
|
+
io.puts '=' * 50
|
222
248
|
when :light
|
223
|
-
puts '-' * 50
|
249
|
+
io.puts '-' * 50
|
224
250
|
when :dotted
|
225
|
-
puts '.' * 50
|
251
|
+
io.puts '.' * 50
|
252
|
+
else
|
253
|
+
io.puts '-' * 50
|
254
|
+
end
|
255
|
+
end
|
256
|
+
|
257
|
+
private
|
258
|
+
|
259
|
+
def format_timing(elapsed_time)
|
260
|
+
if elapsed_time < 0.001
|
261
|
+
" (#{(elapsed_time * 1_000_000).round}μs)"
|
262
|
+
elsif elapsed_time < 1
|
263
|
+
" (#{(elapsed_time * 1000).round}ms)"
|
226
264
|
else
|
227
|
-
|
265
|
+
" (#{elapsed_time.round(2)}s)"
|
228
266
|
end
|
229
267
|
end
|
230
268
|
end
|
@@ -235,9 +273,9 @@ class Tryouts
|
|
235
273
|
super(options.merge(show_passed: false))
|
236
274
|
end
|
237
275
|
|
238
|
-
def test_result(
|
276
|
+
def test_result(result_packet)
|
239
277
|
# Only show failed/error tests
|
240
|
-
return if
|
278
|
+
return if result_packet.passed?
|
241
279
|
|
242
280
|
super
|
243
281
|
end
|
@@ -30,6 +30,11 @@ class Tryouts
|
|
30
30
|
@formatter.file_start(file_path, context_info)
|
31
31
|
end
|
32
32
|
|
33
|
+
def file_end(file_path, framework: :direct, context: :fresh)
|
34
|
+
context_info = { framework: framework, context: context }
|
35
|
+
@formatter.file_end(file_path, context_info)
|
36
|
+
end
|
37
|
+
|
33
38
|
def file_parsed(file_path, test_count, setup_present: false, teardown_present: false)
|
34
39
|
with_indent(1) do
|
35
40
|
@formatter.file_parsed(file_path, test_count,
|
@@ -62,8 +67,14 @@ class Tryouts
|
|
62
67
|
end
|
63
68
|
end
|
64
69
|
|
65
|
-
def
|
66
|
-
|
70
|
+
def test_end(test_case, index, total)
|
71
|
+
with_indent(2) do
|
72
|
+
@formatter.test_end(test_case, index, total)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def test_result(result_packet)
|
77
|
+
@formatter.test_result(result_packet)
|
67
78
|
end
|
68
79
|
|
69
80
|
def test_output(test_case, output_text)
|
@@ -22,6 +22,10 @@ class Tryouts
|
|
22
22
|
# Silent in quiet mode
|
23
23
|
end
|
24
24
|
|
25
|
+
def file_end(_file_path, _context_info = {}, io = $stderr)
|
26
|
+
io.puts # add newline after all dots
|
27
|
+
end
|
28
|
+
|
25
29
|
def file_parsed(file_path, test_count, setup_present: false, teardown_present: false)
|
26
30
|
# Silent in quiet mode
|
27
31
|
end
|
@@ -39,20 +43,24 @@ class Tryouts
|
|
39
43
|
# Silent in quiet mode
|
40
44
|
end
|
41
45
|
|
42
|
-
def
|
43
|
-
|
46
|
+
def test_end(test_case, index, total, io = $stderr)
|
47
|
+
# Silent in quiet mode
|
48
|
+
end
|
49
|
+
|
50
|
+
def test_result(result_packet, io = $stderr)
|
51
|
+
case result_packet.status
|
44
52
|
when :passed
|
45
|
-
print Console.color(:green, '.')
|
53
|
+
io.print Console.color(:green, '.')
|
46
54
|
when :failed
|
47
|
-
print Console.color(:red, 'F')
|
55
|
+
io.print Console.color(:red, 'F')
|
48
56
|
when :error
|
49
|
-
print Console.color(:red, 'E')
|
57
|
+
io.print Console.color(:red, 'E')
|
50
58
|
when :skipped
|
51
|
-
print Console.color(:yellow, 'S')
|
59
|
+
io.print Console.color(:yellow, 'S')
|
52
60
|
else
|
53
|
-
print '?'
|
61
|
+
io.print '?'
|
54
62
|
end
|
55
|
-
|
63
|
+
io.flush
|
56
64
|
end
|
57
65
|
|
58
66
|
def test_output(test_case, output_text)
|
@@ -78,18 +86,16 @@ class Tryouts
|
|
78
86
|
end
|
79
87
|
|
80
88
|
# Summary operations - show results
|
81
|
-
def batch_summary(total_tests, failed_count, elapsed_time)
|
89
|
+
def batch_summary(total_tests, failed_count, elapsed_time, io = $stderr)
|
82
90
|
return unless @show_final_summary
|
83
91
|
|
84
|
-
puts # New line after dots
|
85
|
-
|
86
92
|
if failed_count > 0
|
87
93
|
passed = total_tests - failed_count
|
88
94
|
time_str = elapsed_time ? " (#{elapsed_time.round(2)}s)" : ''
|
89
|
-
puts "#{failed_count} failed, #{passed} passed#{time_str}"
|
95
|
+
io.puts "#{failed_count} failed, #{passed} passed#{time_str}"
|
90
96
|
else
|
91
97
|
time_str = elapsed_time ? " (#{elapsed_time.round(2)}s)" : ''
|
92
|
-
puts "#{total_tests} passed#{time_str}"
|
98
|
+
io.puts "#{total_tests} passed#{time_str}"
|
93
99
|
end
|
94
100
|
end
|
95
101
|
|
@@ -106,7 +112,7 @@ class Tryouts
|
|
106
112
|
|
107
113
|
issues_count = failed_count + error_count
|
108
114
|
if issues_count > 0
|
109
|
-
passed = total_tests - issues_count
|
115
|
+
passed = [total_tests - issues_count, 0].max # Ensure passed never goes negative
|
110
116
|
details = []
|
111
117
|
details << "#{failed_count} failed" if failed_count > 0
|
112
118
|
details << "#{error_count} errors" if error_count > 0
|
@@ -148,9 +154,9 @@ class Tryouts
|
|
148
154
|
|
149
155
|
# Quiet formatter that only shows dots for failures and errors
|
150
156
|
class QuietFailsFormatter < QuietFormatter
|
151
|
-
def test_result(
|
157
|
+
def test_result(result_packet)
|
152
158
|
# Only show non-pass dots in fails mode
|
153
|
-
return if
|
159
|
+
return if result_packet.passed?
|
154
160
|
|
155
161
|
super
|
156
162
|
end
|