tryouts 3.0.0 → 3.1.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.
Files changed (35) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +51 -115
  3. data/exe/try +25 -4
  4. data/lib/tryouts/cli/formatters/base.rb +33 -21
  5. data/lib/tryouts/cli/formatters/compact.rb +122 -84
  6. data/lib/tryouts/cli/formatters/factory.rb +1 -1
  7. data/lib/tryouts/cli/formatters/output_manager.rb +13 -2
  8. data/lib/tryouts/cli/formatters/quiet.rb +22 -16
  9. data/lib/tryouts/cli/formatters/verbose.rb +102 -61
  10. data/lib/tryouts/console.rb +53 -17
  11. data/lib/tryouts/expectation_evaluators/base.rb +101 -0
  12. data/lib/tryouts/expectation_evaluators/boolean.rb +60 -0
  13. data/lib/tryouts/expectation_evaluators/exception.rb +61 -0
  14. data/lib/tryouts/expectation_evaluators/expectation_result.rb +67 -0
  15. data/lib/tryouts/expectation_evaluators/false.rb +60 -0
  16. data/lib/tryouts/expectation_evaluators/intentional_failure.rb +74 -0
  17. data/lib/tryouts/expectation_evaluators/output.rb +101 -0
  18. data/lib/tryouts/expectation_evaluators/performance_time.rb +81 -0
  19. data/lib/tryouts/expectation_evaluators/regex_match.rb +57 -0
  20. data/lib/tryouts/expectation_evaluators/registry.rb +66 -0
  21. data/lib/tryouts/expectation_evaluators/regular.rb +67 -0
  22. data/lib/tryouts/expectation_evaluators/result_type.rb +51 -0
  23. data/lib/tryouts/expectation_evaluators/true.rb +58 -0
  24. data/lib/tryouts/prism_parser.rb +221 -20
  25. data/lib/tryouts/test_batch.rb +556 -0
  26. data/lib/tryouts/test_case.rb +192 -0
  27. data/lib/tryouts/test_executor.rb +7 -5
  28. data/lib/tryouts/test_runner.rb +2 -2
  29. data/lib/tryouts/translators/minitest_translator.rb +78 -11
  30. data/lib/tryouts/translators/rspec_translator.rb +85 -12
  31. data/lib/tryouts/version.rb +1 -1
  32. data/lib/tryouts.rb +43 -1
  33. metadata +18 -5
  34. data/lib/tryouts/testbatch.rb +0 -314
  35. data/lib/tryouts/testcase.rb +0 -51
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f8f095071e066f97f3b04680aa28ca1cd98bfb2f40006937b08a59dd8d5fc04c
4
- data.tar.gz: bb5b18a0d0b703fa07fd4aad746b9bac66765fa23929571152b8f047bf310887
3
+ metadata.gz: ca346c47683b393c8c60c586ab053a73c09b7d5f3e19ce184b35453135ae6594
4
+ data.tar.gz: 2ba211232994981e53393167cce8e09fc29bbc988d14b9ac3178321cbf158aaf
5
5
  SHA512:
6
- metadata.gz: 633e4c03eb25ae604be0ea2d18d78f40917893182d9105e55f98ded0ee9f0d1c1505896976bd4d635ebd60ed69a297396db4e70fca28aa37b83e2610a110623b
7
- data.tar.gz: 7ba9e3714720b3e5546a8f5962d553ed8f20a94e1ea46ca9da0650302fe4b399158cbd2765a404ab2735c7840417c0643c318ef008f1edf74ed3479d5201ebbc
6
+ metadata.gz: 12f4d97b1f2049f1f6df4d0badca42d2a585360f12678e678cfbbcb85e0768cc2365eb7eb09a744713df15e2d51d3df9690fde78d160ff73947d2d9bdf986aa9
7
+ data.tar.gz: d919d1923c471aaa240f66dd0f03760bf7f5f9e53f71c35f8ace03d84935648dc21dafd5c8dceb68468a1b7e31ed6a47ad554471758d6e345146e15a661eca4f
data/README.md CHANGED
@@ -1,78 +1,44 @@
1
- # Tryouts v3.0.0-pre
1
+ # Tryouts v3.1
2
2
 
3
3
  **Ruby tests that read like documentation.**
4
4
 
5
5
  A modern test framework for Ruby that uses comments to define expectations. Tryouts are meant to double as documentation, so the Ruby code should be plain and reminiscent of real code.
6
6
 
7
7
  > [!WARNING]
8
- > Version 3.0 uses Ruby's Prism parser and pattern matching, requiring Ruby 3.4+
8
+ > Version 3.0+ uses Ruby's Prism parser and pattern matching, requiring Ruby 3.4+
9
9
 
10
10
  ## Key Features
11
11
 
12
12
  - **Documentation-style tests** using comment-based expectations (`#=>`)
13
- - **Framework integration** with RSpec and Minitest translators
14
- - **Modern Ruby architecture** using Prism parser and pattern matching
15
- - **Immutable data structures** with `Data.define` classes
13
+ - **Great expectation syntax** for more expressive assertions (`#==>` for true, `#=/=>` for false, `#=:>` for class/module)
14
+ - **Framework integration** write with tryouts syntax, run with RSpec or Minitest
16
15
  - **Enhanced error reporting** with line numbers and context
17
16
 
18
17
  ## Installation
19
18
 
20
- Add to your Gemfile:
21
19
  ```ruby
22
- gem 'tryouts', '~> 3.0.0-pre'
20
+ # Add to your Gemfile:
21
+ gem 'tryouts'
23
22
  ```
24
23
 
25
- Or install directly:
26
24
  ```bash
27
- gem install tryouts --pre
25
+ # Or install directly:
26
+ $ gem install tryouts
28
27
  ```
29
28
 
30
- From source:
31
- ```bash
32
- git clone https://github.com/delano/tryouts.git
33
- cd tryouts
34
- bundle install
35
- ```
36
-
37
- ## Requirements
38
-
39
- - **Ruby >= 3.4.4** (for Prism parser and pattern matching)
40
- - **RSpec** or **Minitest** (optional, for framework integration)
41
-
42
29
  ## Usage
43
30
 
44
- ### Basic Commands
45
-
46
31
  ```bash
47
32
  # Auto-discover and run all tests
48
33
  try
49
34
 
50
35
  # Run specific test file
51
- try try/proof1_try.rb
52
-
53
- # Framework integration
54
- try --rspec try/proof1_try.rb # Run with RSpec
55
- try --minitest try/proof1_try.rb # Run with Minitest
56
-
57
- # Code generation only
58
- try --generate-rspec try/proof1_try.rb
59
- try --generate-minitest try/proof1_try.rb
60
-
61
- # Output options
62
- try -v # verbose (includes source code and return values)
63
- try -q # quiet mode
64
- try -f # show failures only
65
- try -D # debug mode
36
+ try try/core/basic_syntax_try.rb
66
37
  ```
67
38
 
68
- ### Exit Codes
69
-
70
- - `0`: All tests pass
71
- - `1+`: Number of failing tests
72
-
73
39
  ## Writing Tests
74
40
 
75
- Tryouts use a unique comment-based expectation syntax:
41
+ Tryouts use a comment-based syntax for expecations:
76
42
 
77
43
  ```ruby
78
44
  # Setup code runs before all tests
@@ -111,56 +77,60 @@ puts 'Cleanup complete'
111
77
 
112
78
  ### Test Structure
113
79
 
114
- - **Setup Section**: Code before first test case (accessible via instance variables)
80
+ Each test file is made up of three sections:
81
+ - **Setup**: Code before first test case
115
82
  - **Test Cases**: Description lines (`##`), Ruby code, and expectations (`#=>`)
116
- - **Teardown Section**: Code after last test case
83
+ - **Teardown**: Code after last test case
117
84
 
118
- ## Framework Integration
85
+ ### Great Expectations System
119
86
 
120
- Version 3.0 introduces framework translators that convert tryouts into established test frameworks:
87
+ | Syntax | Description | Example | Context |
88
+ |--------|----------------------------|-------------------------------------|---------------|
89
+ | `#=>` | Traditional value equality | `#=> [1, 2, 3]` | result, _ |
90
+ | `#==>` | Must be exactly true | `#==> result.include?(2)` | result, _ |
91
+ | `#=/=>`| Must be exactly false | `#=/=> _.empty?` | result, _ |
92
+ | `#=\|>` | Must be true OR false | `#=\|> 0.positive?` | result, _ |
93
+ | `#=!>` | Must raise an exception | `#=!> error.is_a?(ZeroDivisionError)` | error |
94
+ | `#=:>` | Must match result type | `#=:> String` | result, _ |
95
+ | `#=~>` | Must match regex pattern | `#=~> /^[^@]+@[^@]+\.[^@]+$/` | result, _ |
96
+ | `#=%>` | Must complete within time | `#=%> 2000 # in milliseconds` | result, _ |
97
+ | `#=1>` | Match content in STDOUT | `#=1> "You have great success"` | result, _ |
98
+ | `#=2>` | Match content in STDERR | `#=2> /[a-zA-Z0-9]+-?[0-9]{1,5}` | result, _ |
99
+ | `#=<>` | Fails on purpose | `#==<> result.include?(4)` | result, _ |
121
100
 
122
- ### RSpec Integration
123
101
 
124
- ```bash
125
- try --rspec file_try.rb
126
- ```
102
+ ### Using other test runners
127
103
 
128
- Generates RSpec `describe/it` blocks with full RSpec ecosystem support including:
129
- - Mocking and stubbing
130
- - Custom matchers
131
- - Parallel execution
132
- - IDE integration
133
-
134
- ### Minitest Integration
104
+ Version 3 introduces framework translators that convert tryouts into the equivalent tests in popular test tools, like RSpec and Minitest.
135
105
 
136
106
  ```bash
137
- try --minitest file_try.rb
138
- ```
139
-
140
- Creates Minitest classes with `test_*` methods for:
141
- - Standard assertions
142
- - Custom test cases
143
- - CI/CD integration
144
- - Debugging support
107
+ # Framework integration
108
+ try --rspec try/core/basic_syntax_try.rb # Run with RSpec
109
+ try --minitest try/core/basic_syntax_try.rb # Run with Minitest
145
110
 
146
- ### Direct Mode
111
+ # Code generation only
112
+ try --generate-rspec try/core/basic_syntax_try.rb > spec/basic_syntax_spec.rb
113
+ try --generate-minitest try/core/basic_syntax_try.rb > test/basic_syntax_test.rb
147
114
 
148
- ```bash
149
- try file_try.rb # Original tryouts execution
115
+ # Output options
116
+ try -v # verbose (includes source code and return values)
117
+ try -q # quiet mode
118
+ try -f # show failures only
119
+ try -D # debug mode
150
120
  ```
151
121
 
152
- Uses shared execution context with all tests running in the same environment.
122
+ ### Exit Codes
153
123
 
154
- ## File Discovery
124
+ - `0`: All tests pass
125
+ - `1+`: Number of failing tests
155
126
 
156
- Auto-discovers test files matching these patterns:
157
- - `./try/*_try.rb`
158
- - `./tryouts/*_try.rb`
159
- - `./*_try.rb`
160
127
 
161
- Files are processed in lexical order.
128
+ ## Requirements
129
+
130
+ - **Ruby >= 3.2+** (for Prism parser and pattern matching)
131
+ - **RSpec** or **Minitest** (optional, for framework integration)
162
132
 
163
- ## Modern Architecture (v3.0)
133
+ ## Modern Architecture (v3+)
164
134
 
165
135
  ### Core Components
166
136
 
@@ -169,47 +139,13 @@ Files are processed in lexical order.
169
139
  - **Framework Translators**: Convert tryouts to RSpec/Minitest format
170
140
  - **CLI**: Modern command-line interface with framework selection
171
141
 
172
- ### Ruby 3.4+ Features
173
-
174
- - **Pattern matching** throughout parsing and classification logic
175
- - **Prism native parser** (no external grammar compilation)
176
- - **Data.define classes** for immutable data structures
177
- - **Enhanced error context** with line numbers and suggestions
178
-
179
- ## Development
180
-
181
- ### Running Tests
182
-
183
- ```bash
184
- # Test framework using itself
185
- try try/*_try.rb
186
-
187
- # With coverage
188
- COVERAGE=1 try try/*_try.rb
189
- ```
190
-
191
- ### Code Quality
192
-
193
- ```bash
194
- bundle exec rubocop # Check style
195
- bundle exec rubocop -A # Auto-fix issues
196
- ```
197
-
198
- ## Migration from v2.x
199
-
200
- Version 3.0 represents a complete modernization:
201
-
202
- - **Parser**: Tree-sitter → Prism (native Ruby)
203
- - **Execution**: Custom runner → Framework delegation
204
- - **Data**: Traditional classes → `Data.define` immutable structures
205
- - **Syntax**: Standard Ruby → Pattern matching throughout
206
- - **Ruby**: 2.7+ → 3.4+ requirement
207
142
 
208
- ## Examples
143
+ ## Live Examples
209
144
 
210
145
  For real-world usage examples, see:
211
146
  - [Onetimesecret tryouts](https://github.com/onetimesecret/onetimesecret/)
212
- - Test files in this repository: `try/*_try.rb` and `doc/`
147
+ - [Rhales](https://github.com/onetimesecret/rhales)
148
+ - [Familia](https://github.com/delano/familia)
213
149
 
214
150
  ## AI Development Assistance
215
151
 
data/exe/try CHANGED
@@ -1,4 +1,4 @@
1
- #!/usr/bin/env /Users/d/.rbenv/shims/ruby
1
+ #!/usr/bin/env ruby
2
2
 
3
3
  # Coverage tracking (must be first)
4
4
  if ENV['COVERAGE'] || ENV['SIMPLECOV']
@@ -42,12 +42,33 @@ Tryouts.update_load_path(lib_glob) if Tryouts.respond_to?(:update_load_path)
42
42
  begin
43
43
  files, options = Tryouts::CLI.parse_args(ARGV)
44
44
 
45
+ # Expand files if directories are given
46
+ expanded_files = []
47
+ files.each do |file_or_dir|
48
+ if File.directory?(file_or_dir)
49
+ # If it's a directory, find all *_try.rb files within it
50
+ dir_files = Dir.glob('**/*_try.rb', base: file_or_dir)
51
+ expanded_files.concat(dir_files.map { |f| File.join(file_or_dir, f) })
52
+ else
53
+ # If it's a file, add it as-is
54
+ expanded_files << file_or_dir
55
+ end
56
+ end
57
+ files = expanded_files
58
+
45
59
  # Default file discovery if no files specified
46
60
  if files.empty?
47
- files = Dir.glob(
48
- ['./{try,tryouts,.}/*_try.rb'],
61
+ raw_files = Dir.glob(
62
+ ['{app,apps,lib,try,tryouts}/**/*_try.rb', './*_try.rb'],
49
63
  base: Dir.pwd,
50
- ).sort
64
+ )
65
+
66
+ # Normalize paths so that slight variations are ignored
67
+ # e.g. ./test_try.rb and ././test_try.rb
68
+ files = raw_files.map { |f| File.expand_path(f, Dir.pwd) }.uniq.sort
69
+ else
70
+ # Normalize paths for explicitly specified files/directories
71
+ files = files.map { |f| File.expand_path(f, Dir.pwd) }.uniq.sort
51
72
  end
52
73
 
53
74
  cli = Tryouts::CLI.new
@@ -7,91 +7,103 @@ class Tryouts
7
7
  class CLI
8
8
  # Enhanced interface for all test output formatting
9
9
  module FormatterInterface
10
+
11
+ attr_reader :current_indent
12
+
10
13
  # Phase-level output (major sections)
11
- def phase_header(message, file_count = nil, level = 0)
14
+ def phase_header(message, file_count = nil, level = 0, io = $stdout)
12
15
  raise NotImplementedError, "#{self.class} must implement #phase_header"
13
16
  end
14
17
 
15
18
  # File-level operations
16
- def file_start(file_path, context_info = {})
19
+ def file_start(file_path, context_info = {}, io = $stdout)
17
20
  raise NotImplementedError, "#{self.class} must implement #file_start"
18
21
  end
19
22
 
20
- def file_parsed(file_path, test_count, setup_present: false, teardown_present: false)
23
+ def file_end(file_path, context_info = {}, io = $stdout)
24
+ raise NotImplementedError, "#{self.class} must implement #file_end"
25
+ end
26
+
27
+ def file_parsed(file_path, test_count, io = $stdout, setup_present: false, teardown_present: false)
21
28
  raise NotImplementedError, "#{self.class} must implement #file_parsed"
22
29
  end
23
30
 
24
- def file_execution_start(file_path, test_count, context_mode)
31
+ def file_execution_start(file_path, test_count, context_mode, io = $stdout)
25
32
  raise NotImplementedError, "#{self.class} must implement #file_execution_start"
26
33
  end
27
34
 
28
- def file_result(file_path, total_tests, failed_count, error_count, elapsed_time)
35
+ def file_result(file_path, total_tests, failed_count, error_count, elapsed_time, io = $stdout)
29
36
  raise NotImplementedError, "#{self.class} must implement #file_result"
30
37
  end
31
38
 
32
39
  # Test-level operations
33
- def test_start(test_case, index, total)
40
+ def test_start(test_case, index, total, io = $stdout)
34
41
  raise NotImplementedError, "#{self.class} must implement #test_start"
35
42
  end
36
43
 
37
- def test_result(test_case, result_status, actual_results = [], elapsed_time = nil)
44
+ def test_end(test_case, index, total, io = $stdout)
45
+ raise NotImplementedError, "#{self.class} must implement #test_end"
46
+ end
47
+
48
+ def test_result(result_packet, io = $stdout)
38
49
  raise NotImplementedError, "#{self.class} must implement #test_result"
39
50
  end
40
51
 
41
- def test_output(test_case, output_text)
52
+ def test_output(test_case, output_text, io = $stdout)
42
53
  raise NotImplementedError, "#{self.class} must implement #test_output"
43
54
  end
44
55
 
45
56
  # Setup/teardown operations
46
- def setup_start(line_range)
57
+ def setup_start(line_range, io = $stdout)
47
58
  raise NotImplementedError, "#{self.class} must implement #setup_start"
48
59
  end
49
60
 
50
- def setup_output(output_text)
61
+ def setup_output(output_text, io = $stdout)
51
62
  raise NotImplementedError, "#{self.class} must implement #setup_output"
52
63
  end
53
64
 
54
- def teardown_start(line_range)
65
+ def teardown_start(line_range, io = $stdout)
55
66
  raise NotImplementedError, "#{self.class} must implement #teardown_start"
56
67
  end
57
68
 
58
- def teardown_output(output_text)
69
+ def teardown_output(output_text, io = $stdout)
59
70
  raise NotImplementedError, "#{self.class} must implement #teardown_output"
60
71
  end
61
72
 
62
73
  # Summary operations
63
- def batch_summary(total_tests, failed_count, elapsed_time)
74
+ def batch_summary(total_tests, failed_count, elapsed_time, io = $stdout)
64
75
  raise NotImplementedError, "#{self.class} must implement #batch_summary"
65
76
  end
66
77
 
67
- def grand_total(total_tests, failed_count, error_count, successful_files, total_files, elapsed_time)
78
+ def grand_total(total_tests, failed_count, error_count, successful_files, total_files, elapsed_time, io = $stdout)
68
79
  raise NotImplementedError, "#{self.class} must implement #grand_total"
69
80
  end
70
81
 
71
82
  # Debug and diagnostic output
72
- def debug_info(message, level = 0)
83
+ def debug_info(message, level = 0, io = $stdout)
73
84
  raise NotImplementedError, "#{self.class} must implement #debug_info"
74
85
  end
75
86
 
76
- def trace_info(message, level = 0)
87
+ def trace_info(message, level = 0, io = $stdout)
77
88
  raise NotImplementedError, "#{self.class} must implement #trace_info"
78
89
  end
79
90
 
80
- def error_message(message, details = nil)
91
+ def error_message(message, details = nil, io = $stdout)
81
92
  raise NotImplementedError, "#{self.class} must implement #error_message"
82
93
  end
83
94
 
84
95
  # Utility methods
85
- def raw_output(text)
96
+ def raw_output(text, io = $stdout)
86
97
  raise NotImplementedError, "#{self.class} must implement #raw_output"
87
98
  end
88
99
 
89
- def separator(style = :light)
100
+ def separator(style = :light, io = $stdout)
90
101
  raise NotImplementedError, "#{self.class} must implement #separator"
91
102
  end
92
103
 
93
- def indent_text(text, level)
94
- indent = ' ' * level
104
+ def indent_text(text, level = nil)
105
+ level ||= current_indent || 0
106
+ indent = ' ' * level
95
107
  "#{indent}#{text}"
96
108
  end
97
109