tryouts 3.1.2 → 3.2.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/lib/tryouts/cli/formatters/base.rb +109 -47
- data/lib/tryouts/cli/formatters/compact.rb +98 -107
- data/lib/tryouts/cli/formatters/factory.rb +8 -2
- data/lib/tryouts/cli/formatters/live_status_manager.rb +138 -0
- data/lib/tryouts/cli/formatters/output_manager.rb +78 -66
- data/lib/tryouts/cli/formatters/quiet.rb +53 -101
- data/lib/tryouts/cli/formatters/test_run_state.rb +122 -0
- data/lib/tryouts/cli/formatters/tty_status_display.rb +273 -0
- data/lib/tryouts/cli/formatters/verbose.rb +99 -103
- data/lib/tryouts/cli/formatters.rb +3 -0
- data/lib/tryouts/cli/opts.rb +17 -8
- data/lib/tryouts/cli/tty_detector.rb +92 -0
- data/lib/tryouts/failure_collector.rb +109 -0
- data/lib/tryouts/test_batch.rb +9 -5
- data/lib/tryouts/test_runner.rb +11 -0
- data/lib/tryouts/version.rb +1 -1
- metadata +78 -3
data/lib/tryouts/cli/opts.rb
CHANGED
@@ -18,8 +18,16 @@ class Tryouts
|
|
18
18
|
|
19
19
|
File Format:
|
20
20
|
## Test description # Test case marker
|
21
|
-
code_to_test
|
22
|
-
#=> expected_result # Expectation
|
21
|
+
code_to_test # Ruby code
|
22
|
+
#=> expected_result # Expectation (various types available)
|
23
|
+
|
24
|
+
Great Expectations System:
|
25
|
+
Multiple expectation types are supported for different testing needs.
|
26
|
+
|
27
|
+
#=> Value equality #==> Must be true #=/=> Must be false
|
28
|
+
#=|> True OR false #=!> Must raise error #=:> Type matching
|
29
|
+
#=~> Regex matching #=%> Time constraints #=1> STDOUT content
|
30
|
+
#=2> STDERR content #=<> Intentional failure
|
23
31
|
HELP
|
24
32
|
|
25
33
|
class << self
|
@@ -50,12 +58,13 @@ class Tryouts
|
|
50
58
|
end
|
51
59
|
|
52
60
|
opts.separator "\nExecution Options:"
|
53
|
-
opts.on('--shared-context', 'Override default context mode') { options[:shared_context]
|
54
|
-
opts.on('--no-shared-context', 'Override default context mode') { options[:shared_context]
|
55
|
-
opts.on('-v', '--verbose', 'Show detailed test output with line numbers') { options[:verbose]
|
56
|
-
opts.on('-f', '--fails', 'Show only failing tests
|
57
|
-
opts.on('-q', '--quiet', 'Minimal output (dots and summary only)') { options[:quiet]
|
58
|
-
opts.on('-c', '--compact', 'Compact single-line output') { options[:compact]
|
61
|
+
opts.on('--shared-context', 'Override default context mode') { options[:shared_context] = true }
|
62
|
+
opts.on('--no-shared-context', 'Override default context mode') { options[:shared_context] = false }
|
63
|
+
opts.on('-v', '--verbose', 'Show detailed test output with line numbers') { options[:verbose] = true }
|
64
|
+
opts.on('-f', '--fails', 'Show only failing tests') { options[:fails_only] = true }
|
65
|
+
opts.on('-q', '--quiet', 'Minimal output (dots and summary only)') { options[:quiet] = true }
|
66
|
+
opts.on('-c', '--compact', 'Compact single-line output') { options[:compact] = true }
|
67
|
+
opts.on('-l', '--live', 'Live status display') { options[:live_status] = true }
|
59
68
|
|
60
69
|
opts.separator "\nInspection Options:"
|
61
70
|
opts.on('-i', '--inspect', 'Inspect file structure without running tests') { options[:inspect] = true }
|
@@ -0,0 +1,92 @@
|
|
1
|
+
# lib/tryouts/cli/tty_detector.rb
|
2
|
+
|
3
|
+
require 'tty-screen'
|
4
|
+
|
5
|
+
class Tryouts
|
6
|
+
class CLI
|
7
|
+
# TTY detection utility for determining live formatter availability
|
8
|
+
module TTYDetector
|
9
|
+
STATUS_LINES = 4 # Lines needed for live formatter status area
|
10
|
+
|
11
|
+
# Check if TTY features are available for live formatting
|
12
|
+
# Returns: { available: boolean, reason: string }
|
13
|
+
def self.check_tty_support(debug: false)
|
14
|
+
result = { available: false, reason: nil }
|
15
|
+
|
16
|
+
# FORCE_LIVE override for testing
|
17
|
+
if ENV['FORCE_LIVE'] == '1'
|
18
|
+
debug_log('FORCE_LIVE=1 - forcing TTY support', debug)
|
19
|
+
result[:available] = true
|
20
|
+
result[:reason] = 'Forced via FORCE_LIVE=1'
|
21
|
+
return result
|
22
|
+
end
|
23
|
+
|
24
|
+
# Enhanced TTY detection to work with bundler and other execution contexts
|
25
|
+
debug_log('TTY Detection:', debug)
|
26
|
+
debug_log(" $stdout.tty? = #{$stdout.tty?}", debug)
|
27
|
+
debug_log(" $stderr.tty? = #{$stderr.tty?}", debug)
|
28
|
+
debug_log(" $stdin.tty? = #{$stdin.tty?}", debug)
|
29
|
+
|
30
|
+
# Check if any standard stream is a TTY or if we have a controlling terminal
|
31
|
+
has_tty = $stdout.tty? || $stderr.tty? || $stdin.tty?
|
32
|
+
debug_log(" Combined streams TTY: #{has_tty}", debug)
|
33
|
+
|
34
|
+
# Additional check: try to access controlling terminal directly
|
35
|
+
unless has_tty
|
36
|
+
begin
|
37
|
+
# On Unix systems, /dev/tty represents the controlling terminal
|
38
|
+
File.open('/dev/tty', 'r') { |f| has_tty = f.tty? }
|
39
|
+
debug_log(" /dev/tty accessible: #{has_tty}", debug)
|
40
|
+
rescue StandardError => ex
|
41
|
+
debug_log(" /dev/tty error: #{ex.class}: #{ex.message}", debug)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
unless has_tty
|
46
|
+
debug_log(' Final result: No TTY detected', debug)
|
47
|
+
result[:reason] = 'No TTY detected (not running in terminal)'
|
48
|
+
return result
|
49
|
+
end
|
50
|
+
|
51
|
+
# Skip in CI or dumb terminals
|
52
|
+
if ENV['CI'] || ENV['TERM'] == 'dumb'
|
53
|
+
debug_log(" CI or dumb terminal detected (CI=#{ENV['CI']}, TERM=#{ENV.fetch('TERM', nil)})", debug)
|
54
|
+
result[:reason] = 'CI environment or dumb terminal detected'
|
55
|
+
return result
|
56
|
+
end
|
57
|
+
|
58
|
+
# Test TTY gem availability and basic functionality
|
59
|
+
begin
|
60
|
+
height = TTY::Screen.height
|
61
|
+
debug_log(" Screen height: #{height}, need minimum: #{STATUS_LINES + 5}", debug)
|
62
|
+
|
63
|
+
if height < STATUS_LINES + 5 # Need minimum screen space
|
64
|
+
debug_log(' Screen too small', debug)
|
65
|
+
result[:reason] = "Terminal too small (#{height} lines < #{STATUS_LINES + 5} needed)"
|
66
|
+
return result
|
67
|
+
end
|
68
|
+
|
69
|
+
# Test cursor control (basic check without actually saving)
|
70
|
+
require 'tty-cursor'
|
71
|
+
TTY::Cursor.save # Just test that it exists
|
72
|
+
|
73
|
+
debug_log(' TTY support enabled', debug)
|
74
|
+
result[:available] = true
|
75
|
+
result[:reason] = 'TTY support available'
|
76
|
+
rescue LoadError => ex
|
77
|
+
debug_log(" TTY gem loading failed: #{ex.message}", debug)
|
78
|
+
result[:reason] = "TTY gems not available: #{ex.message}"
|
79
|
+
rescue StandardError => ex
|
80
|
+
debug_log(" TTY setup failed: #{ex.class}: #{ex.message}", debug)
|
81
|
+
result[:reason] = "TTY setup failed: #{ex.message}"
|
82
|
+
end
|
83
|
+
|
84
|
+
result
|
85
|
+
end
|
86
|
+
|
87
|
+
def self.debug_log(message, debug_enabled)
|
88
|
+
$stderr.puts "DEBUG: #{message}" if debug_enabled
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
@@ -0,0 +1,109 @@
|
|
1
|
+
# lib/tryouts/failure_collector.rb
|
2
|
+
|
3
|
+
require_relative 'console'
|
4
|
+
|
5
|
+
class Tryouts
|
6
|
+
# Collects and organizes failed test results across files for summary display
|
7
|
+
# Similar to RSpec's failure summary at the end of test runs
|
8
|
+
class FailureCollector
|
9
|
+
# Data structure for a single failure entry
|
10
|
+
FailureEntry = Data.define(:file_path, :test_case, :result_packet) do
|
11
|
+
def line_number
|
12
|
+
# Use last line of range (expectation line) for failure display
|
13
|
+
test_case.line_range&.last || test_case.first_expectation_line || 0
|
14
|
+
end
|
15
|
+
|
16
|
+
def description
|
17
|
+
desc = test_case.description.to_s.strip
|
18
|
+
desc.empty? ? 'unnamed test' : desc
|
19
|
+
end
|
20
|
+
|
21
|
+
def failure_reason
|
22
|
+
case result_packet.status
|
23
|
+
when :failed
|
24
|
+
if result_packet.actual_results.any? && result_packet.expected_results.any?
|
25
|
+
"expected #{result_packet.first_expected.inspect}, got #{result_packet.first_actual.inspect}"
|
26
|
+
else
|
27
|
+
'test failed'
|
28
|
+
end
|
29
|
+
when :error
|
30
|
+
error_msg = result_packet.error&.message || 'unknown error'
|
31
|
+
"#{result_packet.error&.class&.name || 'Error'}: #{error_msg}"
|
32
|
+
else
|
33
|
+
'test did not pass'
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def source_context
|
38
|
+
return [] unless test_case.source_lines
|
39
|
+
|
40
|
+
# Show the test code (excluding setup/teardown)
|
41
|
+
test_case.source_lines.reject do |line|
|
42
|
+
line.strip.empty? || line.strip.start_with?('#')
|
43
|
+
end.first(3) # Limit to first 3 relevant lines
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def initialize
|
48
|
+
@failures = []
|
49
|
+
@files_with_failures = Set.new
|
50
|
+
end
|
51
|
+
|
52
|
+
# Add a failed test result
|
53
|
+
def add_failure(file_path, result_packet)
|
54
|
+
return unless result_packet.failed? || result_packet.error?
|
55
|
+
|
56
|
+
entry = FailureEntry.new(
|
57
|
+
file_path: file_path,
|
58
|
+
test_case: result_packet.test_case,
|
59
|
+
result_packet: result_packet,
|
60
|
+
)
|
61
|
+
|
62
|
+
@failures << entry
|
63
|
+
@files_with_failures << file_path
|
64
|
+
end
|
65
|
+
|
66
|
+
# Check if any failures were collected
|
67
|
+
def any_failures?
|
68
|
+
!@failures.empty?
|
69
|
+
end
|
70
|
+
|
71
|
+
# Get count of total failures
|
72
|
+
def failure_count
|
73
|
+
@failures.count { |f| f.result_packet.failed? }
|
74
|
+
end
|
75
|
+
|
76
|
+
# Get count of total errors
|
77
|
+
def error_count
|
78
|
+
@failures.count { |f| f.result_packet.error? }
|
79
|
+
end
|
80
|
+
|
81
|
+
# Get total issues (failures + errors)
|
82
|
+
def total_issues
|
83
|
+
@failures.size
|
84
|
+
end
|
85
|
+
|
86
|
+
# Get count of files with failures
|
87
|
+
def files_with_failures_count
|
88
|
+
@files_with_failures.size
|
89
|
+
end
|
90
|
+
|
91
|
+
# Get failures grouped by file for summary display
|
92
|
+
def failures_by_file
|
93
|
+
@failures.group_by(&:file_path).transform_values do |file_failures|
|
94
|
+
file_failures.sort_by(&:line_number)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
# Get all failure entries (for detailed processing)
|
99
|
+
def all_failures
|
100
|
+
@failures.dup
|
101
|
+
end
|
102
|
+
|
103
|
+
# Reset the collector (useful for testing)
|
104
|
+
def clear
|
105
|
+
@failures.clear
|
106
|
+
@files_with_failures.clear
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
data/lib/tryouts/test_batch.rb
CHANGED
@@ -110,7 +110,7 @@ class Tryouts
|
|
110
110
|
|
111
111
|
result
|
112
112
|
rescue StandardError => ex
|
113
|
-
@output_manager&.test_end(test_case, idx, @test_case_count
|
113
|
+
@output_manager&.test_end(test_case, idx, @test_case_count)
|
114
114
|
# Create error result packet to maintain consistent data flow
|
115
115
|
error_result = build_error_result(test_case, ex)
|
116
116
|
process_test_result(error_result)
|
@@ -367,13 +367,18 @@ class Tryouts
|
|
367
367
|
|
368
368
|
if result.failed? || result.error?
|
369
369
|
@failed_count += 1
|
370
|
+
|
371
|
+
# Collect failure details for end-of-run summary
|
372
|
+
if @global_tally && @global_tally[:failure_collector]
|
373
|
+
@global_tally[:failure_collector].add_failure(@testrun.source_file, result)
|
374
|
+
end
|
370
375
|
end
|
371
376
|
|
372
377
|
show_test_result(result)
|
373
378
|
|
374
379
|
# Show captured output if any exists
|
375
380
|
if result.has_output?
|
376
|
-
@output_manager&.test_output(result.test_case, result.captured_output)
|
381
|
+
@output_manager&.test_output(result.test_case, result.captured_output, result)
|
377
382
|
end
|
378
383
|
end
|
379
384
|
|
@@ -494,9 +499,8 @@ class Tryouts
|
|
494
499
|
end
|
495
500
|
|
496
501
|
def show_summary(elapsed_time)
|
497
|
-
#
|
498
|
-
|
499
|
-
@output_manager&.batch_summary(executed_count, @failed_count, elapsed_time)
|
502
|
+
# Summary is now handled by TestRunner with failure details
|
503
|
+
# This method kept for compatibility but no longer calls batch_summary
|
500
504
|
end
|
501
505
|
|
502
506
|
# Helper methods using pattern matching
|
data/lib/tryouts/test_runner.rb
CHANGED
@@ -5,6 +5,7 @@ require_relative 'test_batch'
|
|
5
5
|
require_relative 'translators/rspec_translator'
|
6
6
|
require_relative 'translators/minitest_translator'
|
7
7
|
require_relative 'file_processor'
|
8
|
+
require_relative 'failure_collector'
|
8
9
|
|
9
10
|
class Tryouts
|
10
11
|
class TestRunner
|
@@ -32,6 +33,7 @@ class Tryouts
|
|
32
33
|
validate_framework
|
33
34
|
|
34
35
|
result = process_files
|
36
|
+
show_failure_summary
|
35
37
|
show_grand_total if @global_tally[:file_count] > 1
|
36
38
|
result
|
37
39
|
end
|
@@ -73,6 +75,7 @@ class Tryouts
|
|
73
75
|
file_count: 0,
|
74
76
|
start_time: Time.now,
|
75
77
|
successful_files: 0,
|
78
|
+
failure_collector: FailureCollector.new,
|
76
79
|
}
|
77
80
|
end
|
78
81
|
|
@@ -104,8 +107,16 @@ class Tryouts
|
|
104
107
|
1
|
105
108
|
end
|
106
109
|
|
110
|
+
def show_failure_summary
|
111
|
+
# Show failure summary if any failures exist
|
112
|
+
if @global_tally[:failure_collector].any_failures?
|
113
|
+
@output_manager.batch_summary(@global_tally[:failure_collector])
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
107
117
|
def show_grand_total
|
108
118
|
elapsed_time = Time.now - @global_tally[:start_time]
|
119
|
+
|
109
120
|
@output_manager.grand_total(
|
110
121
|
@global_tally[:total_tests],
|
111
122
|
@global_tally[:total_failed],
|
data/lib/tryouts/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: tryouts
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 3.1
|
4
|
+
version: 3.2.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Delano Mandelbaum
|
@@ -9,6 +9,34 @@ bindir: exe
|
|
9
9
|
cert_chain: []
|
10
10
|
date: 1980-01-02 00:00:00.000000000 Z
|
11
11
|
dependencies:
|
12
|
+
- !ruby/object:Gem::Dependency
|
13
|
+
name: irb
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
15
|
+
requirements:
|
16
|
+
- - ">="
|
17
|
+
- !ruby/object:Gem::Version
|
18
|
+
version: '0'
|
19
|
+
type: :runtime
|
20
|
+
prerelease: false
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
22
|
+
requirements:
|
23
|
+
- - ">="
|
24
|
+
- !ruby/object:Gem::Version
|
25
|
+
version: '0'
|
26
|
+
- !ruby/object:Gem::Dependency
|
27
|
+
name: prism
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
29
|
+
requirements:
|
30
|
+
- - "~>"
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '1.0'
|
33
|
+
type: :runtime
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
36
|
+
requirements:
|
37
|
+
- - "~>"
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
version: '1.0'
|
12
40
|
- !ruby/object:Gem::Dependency
|
13
41
|
name: minitest
|
14
42
|
requirement: !ruby/object:Gem::Requirement
|
@@ -37,8 +65,50 @@ dependencies:
|
|
37
65
|
- - "~>"
|
38
66
|
- !ruby/object:Gem::Version
|
39
67
|
version: '3.0'
|
40
|
-
|
41
|
-
|
68
|
+
- !ruby/object:Gem::Dependency
|
69
|
+
name: pastel
|
70
|
+
requirement: !ruby/object:Gem::Requirement
|
71
|
+
requirements:
|
72
|
+
- - "~>"
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
version: '0.8'
|
75
|
+
type: :runtime
|
76
|
+
prerelease: false
|
77
|
+
version_requirements: !ruby/object:Gem::Requirement
|
78
|
+
requirements:
|
79
|
+
- - "~>"
|
80
|
+
- !ruby/object:Gem::Version
|
81
|
+
version: '0.8'
|
82
|
+
- !ruby/object:Gem::Dependency
|
83
|
+
name: tty-cursor
|
84
|
+
requirement: !ruby/object:Gem::Requirement
|
85
|
+
requirements:
|
86
|
+
- - "~>"
|
87
|
+
- !ruby/object:Gem::Version
|
88
|
+
version: '0.7'
|
89
|
+
type: :runtime
|
90
|
+
prerelease: false
|
91
|
+
version_requirements: !ruby/object:Gem::Requirement
|
92
|
+
requirements:
|
93
|
+
- - "~>"
|
94
|
+
- !ruby/object:Gem::Version
|
95
|
+
version: '0.7'
|
96
|
+
- !ruby/object:Gem::Dependency
|
97
|
+
name: tty-screen
|
98
|
+
requirement: !ruby/object:Gem::Requirement
|
99
|
+
requirements:
|
100
|
+
- - "~>"
|
101
|
+
- !ruby/object:Gem::Version
|
102
|
+
version: '0.8'
|
103
|
+
type: :runtime
|
104
|
+
prerelease: false
|
105
|
+
version_requirements: !ruby/object:Gem::Requirement
|
106
|
+
requirements:
|
107
|
+
- - "~>"
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: '0.8'
|
110
|
+
description: A simple test framework for Ruby code where the test descriptions and
|
111
|
+
expectations are written as comments.
|
42
112
|
email: gems@solutious.com
|
43
113
|
executables:
|
44
114
|
- try
|
@@ -56,12 +126,16 @@ files:
|
|
56
126
|
- lib/tryouts/cli/formatters/base.rb
|
57
127
|
- lib/tryouts/cli/formatters/compact.rb
|
58
128
|
- lib/tryouts/cli/formatters/factory.rb
|
129
|
+
- lib/tryouts/cli/formatters/live_status_manager.rb
|
59
130
|
- lib/tryouts/cli/formatters/output_manager.rb
|
60
131
|
- lib/tryouts/cli/formatters/quiet.rb
|
132
|
+
- lib/tryouts/cli/formatters/test_run_state.rb
|
133
|
+
- lib/tryouts/cli/formatters/tty_status_display.rb
|
61
134
|
- lib/tryouts/cli/formatters/verbose.rb
|
62
135
|
- lib/tryouts/cli/modes/generate.rb
|
63
136
|
- lib/tryouts/cli/modes/inspect.rb
|
64
137
|
- lib/tryouts/cli/opts.rb
|
138
|
+
- lib/tryouts/cli/tty_detector.rb
|
65
139
|
- lib/tryouts/console.rb
|
66
140
|
- lib/tryouts/expectation_evaluators/base.rb
|
67
141
|
- lib/tryouts/expectation_evaluators/boolean.rb
|
@@ -76,6 +150,7 @@ files:
|
|
76
150
|
- lib/tryouts/expectation_evaluators/regular.rb
|
77
151
|
- lib/tryouts/expectation_evaluators/result_type.rb
|
78
152
|
- lib/tryouts/expectation_evaluators/true.rb
|
153
|
+
- lib/tryouts/failure_collector.rb
|
79
154
|
- lib/tryouts/file_processor.rb
|
80
155
|
- lib/tryouts/prism_parser.rb
|
81
156
|
- lib/tryouts/test_batch.rb
|