tryouts 3.2.2 → 3.3.1
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 +1 -1
- data/exe/try +1 -1
- data/lib/tryouts/cli/formatters/compact.rb +4 -1
- data/lib/tryouts/cli/formatters/live_status_manager.rb +2 -2
- data/lib/tryouts/cli/formatters/verbose.rb +5 -2
- data/lib/tryouts/cli/opts.rb +4 -0
- data/lib/tryouts/expectation_evaluators/regex_match.rb +11 -3
- data/lib/tryouts/expectation_evaluators/result_type.rb +9 -1
- data/lib/tryouts/file_processor.rb +20 -2
- data/lib/tryouts/parsers/base_parser.rb +23 -0
- data/lib/tryouts/parsers/enhanced_parser.rb +113 -0
- data/lib/tryouts/parsers/prism_parser.rb +120 -0
- data/lib/tryouts/parsers/shared_methods.rb +412 -0
- data/lib/tryouts/test_batch.rb +29 -4
- data/lib/tryouts/test_runner.rb +2 -1
- data/lib/tryouts/version.rb +1 -1
- data/lib/tryouts.rb +2 -1
- metadata +5 -2
- data/lib/tryouts/prism_parser.rb +0 -515
data/lib/tryouts/prism_parser.rb
DELETED
@@ -1,515 +0,0 @@
|
|
1
|
-
# Modern Ruby 3.4+ solution for the teardown bug
|
2
|
-
|
3
|
-
require 'prism'
|
4
|
-
require_relative 'test_case'
|
5
|
-
|
6
|
-
class Tryouts
|
7
|
-
# Fixed PrismParser with pattern matching for robust token filtering
|
8
|
-
class PrismParser
|
9
|
-
def initialize(source_path)
|
10
|
-
@source_path = source_path
|
11
|
-
@source = File.read(source_path)
|
12
|
-
@lines = @source.lines.map(&:chomp)
|
13
|
-
@prism_result = Prism.parse(@source)
|
14
|
-
end
|
15
|
-
|
16
|
-
def parse
|
17
|
-
return handle_syntax_errors if @prism_result.failure?
|
18
|
-
|
19
|
-
tokens = tokenize_content
|
20
|
-
test_boundaries = find_test_case_boundaries(tokens)
|
21
|
-
tokens = classify_potential_descriptions_with_boundaries(tokens, test_boundaries)
|
22
|
-
test_blocks = group_into_test_blocks(tokens)
|
23
|
-
process_test_blocks(test_blocks)
|
24
|
-
end
|
25
|
-
|
26
|
-
private
|
27
|
-
|
28
|
-
# Tokenize content using pattern matching for clean line classification
|
29
|
-
def tokenize_content
|
30
|
-
tokens = []
|
31
|
-
|
32
|
-
@lines.each_with_index do |line, index|
|
33
|
-
token = case line
|
34
|
-
in /^##\s*(.*)$/ # Test description format: ## description
|
35
|
-
{ type: :description, content: $1.strip, line: index }
|
36
|
-
in /^#\s*TEST\s*\d*:\s*(.*)$/ # rubocop:disable Lint/DuplicateBranch
|
37
|
-
{ type: :description, content: $1.strip, line: index }
|
38
|
-
in /^#\s*=!>\s*(.*)$/ # Exception expectation (updated for consistency)
|
39
|
-
{ type: :exception_expectation, content: $1.strip, line: index, ast: parse_expectation($1.strip) }
|
40
|
-
in /^#\s*=<>\s*(.*)$/ # Intentional failure expectation
|
41
|
-
{ type: :intentional_failure_expectation, content: $1.strip, line: index, ast: parse_expectation($1.strip) }
|
42
|
-
in /^#\s*==>\s*(.*)$/ # Boolean true expectation
|
43
|
-
{ type: :true_expectation, content: $1.strip, line: index, ast: parse_expectation($1.strip) }
|
44
|
-
in %r{^#\s*=/=>\s*(.*)$} # Boolean false expectation
|
45
|
-
{ type: :false_expectation, content: $1.strip, line: index, ast: parse_expectation($1.strip) }
|
46
|
-
in /^#\s*=\|>\s*(.*)$/ # Boolean (true or false) expectation
|
47
|
-
{ type: :boolean_expectation, content: $1.strip, line: index, ast: parse_expectation($1.strip) }
|
48
|
-
in /^#\s*=:>\s*(.*)$/ # Result type expectation
|
49
|
-
{ type: :result_type_expectation, content: $1.strip, line: index, ast: parse_expectation($1.strip) }
|
50
|
-
in /^#\s*=~>\s*(.*)$/ # Regex match expectation
|
51
|
-
{ type: :regex_match_expectation, content: $1.strip, line: index, ast: parse_expectation($1.strip) }
|
52
|
-
in /^#\s*=%>\s*(.*)$/ # Performance time expectation
|
53
|
-
{ type: :performance_time_expectation, content: $1.strip, line: index, ast: parse_expectation($1.strip) }
|
54
|
-
in /^#\s*=(\d+)>\s*(.*)$/ # Output expectation (stdout/stderr with pipe number)
|
55
|
-
{ type: :output_expectation, content: $2.strip, pipe: $1.to_i, line: index, ast: parse_expectation($2.strip) }
|
56
|
-
in /^#\s*=>\s*(.*)$/ # Regular expectation
|
57
|
-
{ type: :expectation, content: $1.strip, line: index, ast: parse_expectation($1.strip) }
|
58
|
-
in /^##\s*=>\s*(.*)$/ # Commented out expectation (should be ignored)
|
59
|
-
{ type: :comment, content: '=>' + $1.strip, line: index }
|
60
|
-
in /^#\s*(.*)$/ # Single hash comment - potential description
|
61
|
-
{ type: :potential_description, content: $1.strip, line: index }
|
62
|
-
in /^\s*$/ # Blank line
|
63
|
-
{ type: :blank, line: index }
|
64
|
-
else # Ruby code
|
65
|
-
{ type: :code, content: line, line: index, ast: parse_ruby_line(line) }
|
66
|
-
end
|
67
|
-
|
68
|
-
tokens << token
|
69
|
-
end
|
70
|
-
|
71
|
-
# Return tokens with potential_descriptions - they'll be classified later with test boundaries
|
72
|
-
tokens
|
73
|
-
end
|
74
|
-
|
75
|
-
# Find actual test case boundaries by looking for ## descriptions or # TEST: patterns
|
76
|
-
# followed by code and expectations
|
77
|
-
def find_test_case_boundaries(tokens)
|
78
|
-
boundaries = []
|
79
|
-
|
80
|
-
tokens.each_with_index do |token, index|
|
81
|
-
# Look for explicit test descriptions (## or # TEST:)
|
82
|
-
if token[:type] == :description
|
83
|
-
# Find the end of this test case by looking for the last expectation
|
84
|
-
# before the next description or end of file
|
85
|
-
start_line = token[:line]
|
86
|
-
end_line = find_test_case_end(tokens, index)
|
87
|
-
|
88
|
-
boundaries << { start: start_line, end: end_line } if end_line
|
89
|
-
end
|
90
|
-
end
|
91
|
-
|
92
|
-
boundaries
|
93
|
-
end
|
94
|
-
|
95
|
-
# Find where a test case ends by looking for the last expectation
|
96
|
-
# before the next test description or end of tokens
|
97
|
-
def find_test_case_end(tokens, start_index)
|
98
|
-
last_expectation_line = nil
|
99
|
-
|
100
|
-
# Look forward from the description for expectations
|
101
|
-
(start_index + 1).upto(tokens.length - 1) do |i|
|
102
|
-
token = tokens[i]
|
103
|
-
|
104
|
-
# Stop if we hit another test description
|
105
|
-
break if token[:type] == :description
|
106
|
-
|
107
|
-
# Track the last expectation we see
|
108
|
-
if is_expectation_type?(token[:type])
|
109
|
-
last_expectation_line = token[:line]
|
110
|
-
end
|
111
|
-
end
|
112
|
-
|
113
|
-
last_expectation_line
|
114
|
-
end
|
115
|
-
|
116
|
-
# Convert potential_descriptions to descriptions or comments using test case boundaries
|
117
|
-
def classify_potential_descriptions_with_boundaries(tokens, test_boundaries)
|
118
|
-
tokens.map.with_index do |token, index|
|
119
|
-
if token[:type] == :potential_description
|
120
|
-
# Check if this comment falls within any test case boundary
|
121
|
-
line_num = token[:line]
|
122
|
-
within_test_case = test_boundaries.any? do |boundary|
|
123
|
-
line_num >= boundary[:start] && line_num <= boundary[:end]
|
124
|
-
end
|
125
|
-
|
126
|
-
if within_test_case
|
127
|
-
# This comment is within a test case, treat as regular comment
|
128
|
-
token.merge(type: :comment)
|
129
|
-
else
|
130
|
-
# For comments outside test boundaries, be more conservative
|
131
|
-
# Only treat as description if it immediately precedes a test pattern AND
|
132
|
-
# looks like a test description
|
133
|
-
content = token[:content].strip
|
134
|
-
|
135
|
-
# Check if this looks like a test description based on content
|
136
|
-
looks_like_test_description = content.match?(/test|example|demonstrate|show|should|when|given/i) &&
|
137
|
-
content.length > 10
|
138
|
-
|
139
|
-
# Check if there's code immediately before this (suggesting it's mid-test)
|
140
|
-
prev_token = index > 0 ? tokens[index - 1] : nil
|
141
|
-
has_code_before = prev_token && prev_token[:type] == :code
|
142
|
-
|
143
|
-
if has_code_before || !looks_like_test_description
|
144
|
-
# Treat as regular comment
|
145
|
-
token.merge(type: :comment)
|
146
|
-
else
|
147
|
-
# Look ahead for IMMEDIATE test pattern (stricter than before)
|
148
|
-
following_tokens = tokens[(index + 1)..]
|
149
|
-
|
150
|
-
# Skip blanks and comments to find meaningful content
|
151
|
-
meaningful_following = following_tokens.reject { |t| [:blank, :comment].include?(t[:type]) }
|
152
|
-
|
153
|
-
# Look for test pattern within next 5 tokens (more restrictive)
|
154
|
-
test_window = meaningful_following.first(5)
|
155
|
-
has_code = test_window.any? { |t| t[:type] == :code }
|
156
|
-
has_expectation = test_window.any? { |t| is_expectation_type?(t[:type]) }
|
157
|
-
|
158
|
-
# Only promote to description if BOTH code and expectation are found nearby
|
159
|
-
# AND it looks like a test description
|
160
|
-
if has_code && has_expectation && looks_like_test_description
|
161
|
-
token.merge(type: :description)
|
162
|
-
else
|
163
|
-
token.merge(type: :comment)
|
164
|
-
end
|
165
|
-
end
|
166
|
-
end
|
167
|
-
else
|
168
|
-
token
|
169
|
-
end
|
170
|
-
end
|
171
|
-
end
|
172
|
-
|
173
|
-
# Convert potential_descriptions to descriptions or comments based on context
|
174
|
-
def classify_potential_descriptions(tokens)
|
175
|
-
tokens.map.with_index do |token, index|
|
176
|
-
if token[:type] == :potential_description
|
177
|
-
# Check if this looks like a test description based on content and context
|
178
|
-
content = token[:content].strip
|
179
|
-
|
180
|
-
# Skip if it's clearly just a regular comment (short, lowercase, etc.)
|
181
|
-
# Test descriptions are typically longer and more descriptive
|
182
|
-
looks_like_regular_comment = content.length < 20 &&
|
183
|
-
content.downcase == content &&
|
184
|
-
!content.match?(/test|example|demonstrate|show/i)
|
185
|
-
|
186
|
-
# Check if there's code immediately before this (suggesting it's mid-test)
|
187
|
-
prev_token = index > 0 ? tokens[index - 1] : nil
|
188
|
-
has_code_before = prev_token && prev_token[:type] == :code
|
189
|
-
|
190
|
-
if looks_like_regular_comment || has_code_before
|
191
|
-
# Treat as regular comment
|
192
|
-
token.merge(type: :comment)
|
193
|
-
else
|
194
|
-
# Look ahead for test pattern: code + at least one expectation within reasonable distance
|
195
|
-
following_tokens = tokens[(index + 1)..]
|
196
|
-
|
197
|
-
# Skip blanks and comments to find meaningful content
|
198
|
-
meaningful_following = following_tokens.reject { |t| [:blank, :comment].include?(t[:type]) }
|
199
|
-
|
200
|
-
# Look for test pattern: at least one code token followed by at least one expectation
|
201
|
-
# within the next 10 meaningful tokens (to avoid matching setup/teardown)
|
202
|
-
test_window = meaningful_following.first(10)
|
203
|
-
has_code = test_window.any? { |t| t[:type] == :code }
|
204
|
-
has_expectation = test_window.any? { |t| is_expectation_type?(t[:type]) }
|
205
|
-
|
206
|
-
if has_code && has_expectation
|
207
|
-
token.merge(type: :description)
|
208
|
-
else
|
209
|
-
token.merge(type: :comment)
|
210
|
-
end
|
211
|
-
end
|
212
|
-
else
|
213
|
-
token
|
214
|
-
end
|
215
|
-
end
|
216
|
-
end
|
217
|
-
|
218
|
-
# Check if token type represents any kind of expectation
|
219
|
-
def is_expectation_type?(type)
|
220
|
-
[
|
221
|
-
:expectation, :exception_expectation, :intentional_failure_expectation,
|
222
|
-
:true_expectation, :false_expectation, :boolean_expectation,
|
223
|
-
:result_type_expectation, :regex_match_expectation,
|
224
|
-
:performance_time_expectation, :output_expectation
|
225
|
-
].include?(type)
|
226
|
-
end
|
227
|
-
|
228
|
-
# Group tokens into logical test blocks using pattern matching
|
229
|
-
def group_into_test_blocks(tokens)
|
230
|
-
blocks = []
|
231
|
-
current_block = new_test_block
|
232
|
-
|
233
|
-
tokens.each do |token|
|
234
|
-
case [current_block, token]
|
235
|
-
in [_, { type: :description, content: String => desc, line: Integer => line_num }]
|
236
|
-
# Only combine descriptions if current block has a description but no code/expectations yet
|
237
|
-
# Allow blank lines between multi-line descriptions
|
238
|
-
if !current_block[:description].empty? && current_block[:code].empty? && current_block[:expectations].empty?
|
239
|
-
# Multi-line description continuation
|
240
|
-
current_block[:description] = [current_block[:description], desc].join(' ').strip
|
241
|
-
else
|
242
|
-
# Start new test block on description
|
243
|
-
blocks << current_block if block_has_content?(current_block)
|
244
|
-
current_block = new_test_block.merge(description: desc, start_line: line_num)
|
245
|
-
end
|
246
|
-
|
247
|
-
in [{ expectations: [], start_line: nil }, { type: :code, content: String => code, line: Integer => line_num }]
|
248
|
-
# First code in a new block - set start_line
|
249
|
-
current_block[:code] << token
|
250
|
-
current_block[:start_line] = line_num
|
251
|
-
|
252
|
-
in [{ expectations: [] }, { type: :code, content: String => code }]
|
253
|
-
# Code before expectations - add to current block
|
254
|
-
current_block[:code] << token
|
255
|
-
|
256
|
-
in [{ expectations: Array => exps }, { type: :code }] if !exps.empty?
|
257
|
-
# Code after expectations - finalize current block and start new one
|
258
|
-
blocks << current_block
|
259
|
-
current_block = new_test_block.merge(code: [token], start_line: token[:line])
|
260
|
-
|
261
|
-
in [_, { type: :expectation }]
|
262
|
-
current_block[:expectations] << token
|
263
|
-
|
264
|
-
in [_, { type: :exception_expectation }]
|
265
|
-
current_block[:expectations] << token
|
266
|
-
|
267
|
-
in [_, { type: :intentional_failure_expectation }]
|
268
|
-
current_block[:expectations] << token
|
269
|
-
|
270
|
-
in [_, { type: :true_expectation }]
|
271
|
-
current_block[:expectations] << token
|
272
|
-
|
273
|
-
in [_, { type: :false_expectation }]
|
274
|
-
current_block[:expectations] << token
|
275
|
-
|
276
|
-
in [_, { type: :boolean_expectation }]
|
277
|
-
current_block[:expectations] << token
|
278
|
-
|
279
|
-
in [_, { type: :result_type_expectation }]
|
280
|
-
current_block[:expectations] << token
|
281
|
-
|
282
|
-
in [_, { type: :regex_match_expectation }]
|
283
|
-
current_block[:expectations] << token
|
284
|
-
|
285
|
-
in [_, { type: :performance_time_expectation }]
|
286
|
-
current_block[:expectations] << token
|
287
|
-
|
288
|
-
in [_, { type: :output_expectation }]
|
289
|
-
current_block[:expectations] << token
|
290
|
-
|
291
|
-
in [_, { type: :comment | :blank }]
|
292
|
-
add_context_to_block(current_block, token)
|
293
|
-
end
|
294
|
-
end
|
295
|
-
|
296
|
-
blocks << current_block if block_has_content?(current_block)
|
297
|
-
classify_blocks(blocks)
|
298
|
-
end
|
299
|
-
|
300
|
-
# Process classified test blocks into domain objects
|
301
|
-
def process_test_blocks(classified_blocks)
|
302
|
-
setup_blocks = classified_blocks.filter { |block| block[:type] == :setup }
|
303
|
-
test_blocks = classified_blocks.filter { |block| block[:type] == :test }
|
304
|
-
teardown_blocks = classified_blocks.filter { |block| block[:type] == :teardown }
|
305
|
-
|
306
|
-
Testrun.new(
|
307
|
-
setup: build_setup(setup_blocks),
|
308
|
-
test_cases: test_blocks.map { |block| build_test_case(block) },
|
309
|
-
teardown: build_teardown(teardown_blocks),
|
310
|
-
source_file: @source_path,
|
311
|
-
metadata: { parsed_at: Time.now, parser: :prism_v2_fixed },
|
312
|
-
)
|
313
|
-
end
|
314
|
-
|
315
|
-
def build_setup(setup_blocks)
|
316
|
-
return Setup.new(code: '', line_range: 0..0, path: @source_path) if setup_blocks.empty?
|
317
|
-
|
318
|
-
Setup.new(
|
319
|
-
code: extract_pure_code_from_blocks(setup_blocks),
|
320
|
-
line_range: calculate_block_range(setup_blocks),
|
321
|
-
path: @source_path,
|
322
|
-
)
|
323
|
-
end
|
324
|
-
|
325
|
-
def build_teardown(teardown_blocks)
|
326
|
-
return Teardown.new(code: '', line_range: 0..0, path: @source_path) if teardown_blocks.empty?
|
327
|
-
|
328
|
-
Teardown.new(
|
329
|
-
code: extract_pure_code_from_blocks(teardown_blocks),
|
330
|
-
line_range: calculate_block_range(teardown_blocks),
|
331
|
-
path: @source_path,
|
332
|
-
)
|
333
|
-
end
|
334
|
-
|
335
|
-
# Modern Ruby 3.4+ pattern matching for robust code extraction
|
336
|
-
# This filters out comments added by add_context_to_block explicitly
|
337
|
-
def extract_pure_code_from_blocks(blocks)
|
338
|
-
blocks
|
339
|
-
.flat_map { |block| block[:code] }
|
340
|
-
.filter_map do |token|
|
341
|
-
case token
|
342
|
-
in { type: :code, content: String => content }
|
343
|
-
content
|
344
|
-
else
|
345
|
-
nil
|
346
|
-
end
|
347
|
-
end
|
348
|
-
.join("\n")
|
349
|
-
end
|
350
|
-
|
351
|
-
def calculate_block_range(blocks)
|
352
|
-
return 0..0 if blocks.empty?
|
353
|
-
|
354
|
-
# Filter out blocks with nil line numbers and build valid ranges
|
355
|
-
valid_blocks = blocks.filter { |block| block[:start_line] && block[:end_line] }
|
356
|
-
return 0..0 if valid_blocks.empty?
|
357
|
-
|
358
|
-
line_ranges = valid_blocks.map { |block| block[:start_line]..block[:end_line] }
|
359
|
-
line_ranges.first.first..line_ranges.last.last
|
360
|
-
end
|
361
|
-
|
362
|
-
def extract_code_content(code_tokens)
|
363
|
-
code_tokens
|
364
|
-
.filter_map do |token|
|
365
|
-
case token
|
366
|
-
in { type: :code, content: String => content }
|
367
|
-
content
|
368
|
-
else
|
369
|
-
nil
|
370
|
-
end
|
371
|
-
end
|
372
|
-
.join("\n")
|
373
|
-
end
|
374
|
-
|
375
|
-
def parse_ruby_line(line)
|
376
|
-
return nil if line.strip.empty?
|
377
|
-
|
378
|
-
result = Prism.parse(line.strip)
|
379
|
-
case result
|
380
|
-
in { errors: [] => errors, value: { body: { body: [ast] } } }
|
381
|
-
ast
|
382
|
-
in { errors: Array => errors } if errors.any?
|
383
|
-
{ type: :parse_error, errors: errors, raw: line }
|
384
|
-
else
|
385
|
-
nil
|
386
|
-
end
|
387
|
-
end
|
388
|
-
|
389
|
-
def parse_expectation(expr)
|
390
|
-
parse_ruby_line(expr)
|
391
|
-
end
|
392
|
-
|
393
|
-
def new_test_block
|
394
|
-
{
|
395
|
-
description: '',
|
396
|
-
code: [],
|
397
|
-
expectations: [],
|
398
|
-
comments: [],
|
399
|
-
start_line: nil,
|
400
|
-
end_line: nil,
|
401
|
-
}
|
402
|
-
end
|
403
|
-
|
404
|
-
def block_has_content?(block)
|
405
|
-
case block
|
406
|
-
in { description: String => desc, code: Array => code, expectations: Array => exps }
|
407
|
-
!desc.empty? || !code.empty? || !exps.empty?
|
408
|
-
else
|
409
|
-
false
|
410
|
-
end
|
411
|
-
end
|
412
|
-
|
413
|
-
def add_context_to_block(block, token)
|
414
|
-
case [block[:expectations].empty?, token]
|
415
|
-
in [true, { type: :comment | :blank }]
|
416
|
-
# Comments before expectations go with code
|
417
|
-
block[:code] << token
|
418
|
-
in [false, { type: :comment | :blank }]
|
419
|
-
# Comments after expectations are test context
|
420
|
-
block[:comments] << token
|
421
|
-
end
|
422
|
-
end
|
423
|
-
|
424
|
-
# Classify blocks as setup, test, or teardown based on content
|
425
|
-
def classify_blocks(blocks)
|
426
|
-
blocks.map.with_index do |block, index|
|
427
|
-
block_type = case block
|
428
|
-
in { expectations: [] } if index == 0
|
429
|
-
:setup
|
430
|
-
in { expectations: [] } if index == blocks.size - 1
|
431
|
-
:teardown
|
432
|
-
in { expectations: Array => exps } if !exps.empty?
|
433
|
-
:test
|
434
|
-
else
|
435
|
-
:preamble # Default fallback
|
436
|
-
end
|
437
|
-
|
438
|
-
block.merge(type: block_type, end_line: calculate_end_line(block))
|
439
|
-
end
|
440
|
-
end
|
441
|
-
|
442
|
-
def calculate_end_line(block)
|
443
|
-
# Only consider actual content (code and expectations), not blank lines/comments
|
444
|
-
content_tokens = [*block[:code], *block[:expectations]]
|
445
|
-
return block[:start_line] if content_tokens.empty?
|
446
|
-
|
447
|
-
content_tokens.map { |token| token[:line] }.max || block[:start_line]
|
448
|
-
end
|
449
|
-
|
450
|
-
def build_test_case(block)
|
451
|
-
case block
|
452
|
-
in {
|
453
|
-
type: :test,
|
454
|
-
description: String => desc,
|
455
|
-
code: Array => code_tokens,
|
456
|
-
expectations: Array => exp_tokens,
|
457
|
-
start_line: Integer => start_line,
|
458
|
-
end_line: Integer => end_line
|
459
|
-
}
|
460
|
-
# Extract source lines from the original source during parsing
|
461
|
-
source_lines = @lines[start_line..end_line]
|
462
|
-
|
463
|
-
# Find the first expectation line for better error reporting
|
464
|
-
first_expectation_line = exp_tokens.empty? ? start_line : exp_tokens.first[:line]
|
465
|
-
|
466
|
-
TestCase.new(
|
467
|
-
description: desc,
|
468
|
-
code: extract_code_content(code_tokens),
|
469
|
-
expectations: exp_tokens.map do |token|
|
470
|
-
type = case token[:type]
|
471
|
-
when :exception_expectation then :exception
|
472
|
-
when :intentional_failure_expectation then :intentional_failure
|
473
|
-
when :true_expectation then :true # rubocop:disable Lint/BooleanSymbol
|
474
|
-
when :false_expectation then :false # rubocop:disable Lint/BooleanSymbol
|
475
|
-
when :boolean_expectation then :boolean
|
476
|
-
when :result_type_expectation then :result_type
|
477
|
-
when :regex_match_expectation then :regex_match
|
478
|
-
when :performance_time_expectation then :performance_time
|
479
|
-
when :output_expectation then :output
|
480
|
-
else :regular
|
481
|
-
end
|
482
|
-
|
483
|
-
# For output expectations, we need to preserve the pipe number
|
484
|
-
if token[:type] == :output_expectation
|
485
|
-
OutputExpectation.new(content: token[:content], type: type, pipe: token[:pipe])
|
486
|
-
else
|
487
|
-
Expectation.new(content: token[:content], type: type)
|
488
|
-
end
|
489
|
-
end,
|
490
|
-
line_range: start_line..end_line,
|
491
|
-
path: @source_path,
|
492
|
-
source_lines: source_lines,
|
493
|
-
first_expectation_line: first_expectation_line,
|
494
|
-
)
|
495
|
-
else
|
496
|
-
raise "Invalid test block structure: #{block}"
|
497
|
-
end
|
498
|
-
end
|
499
|
-
|
500
|
-
def handle_syntax_errors
|
501
|
-
errors = @prism_result.errors.map do |error|
|
502
|
-
line_context = @lines[error.location.start_line - 1] || ''
|
503
|
-
|
504
|
-
TryoutSyntaxError.new(
|
505
|
-
error.message,
|
506
|
-
line_number: error.location.start_line,
|
507
|
-
context: line_context,
|
508
|
-
source_file: @source_path,
|
509
|
-
)
|
510
|
-
end
|
511
|
-
|
512
|
-
raise errors.first if errors.any?
|
513
|
-
end
|
514
|
-
end
|
515
|
-
end
|