tryouts 2.4.1 → 3.0.0.pre2
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/LICENSE.txt +1 -1
- data/README.md +187 -73
- data/exe/try +53 -45
- data/exe/tryouts +2 -2
- data/lib/tryouts/cli/formatters/base.rb +108 -0
- data/lib/tryouts/cli/formatters/compact.rb +246 -0
- data/lib/tryouts/cli/formatters/factory.rb +52 -0
- data/lib/tryouts/cli/formatters/output_manager.rb +140 -0
- data/lib/tryouts/cli/formatters/quiet.rb +159 -0
- data/lib/tryouts/cli/formatters/verbose.rb +344 -0
- data/lib/tryouts/cli/formatters.rb +6 -0
- data/lib/tryouts/cli/modes/generate.rb +22 -0
- data/lib/tryouts/cli/modes/inspect.rb +42 -0
- data/lib/tryouts/cli/opts.rb +88 -0
- data/lib/tryouts/cli.rb +54 -0
- data/lib/tryouts/console.rb +55 -40
- data/lib/tryouts/file_processor.rb +66 -0
- data/lib/tryouts/prism_parser.rb +314 -0
- data/lib/tryouts/test_executor.rb +82 -0
- data/lib/tryouts/test_runner.rb +128 -0
- data/lib/tryouts/testbatch.rb +293 -53
- data/lib/tryouts/testcase.rb +32 -91
- data/lib/tryouts/translators/minitest_translator.rb +106 -0
- data/lib/tryouts/translators/rspec_translator.rb +88 -0
- data/lib/tryouts/version.rb +2 -12
- data/lib/tryouts.rb +16 -263
- metadata +60 -13
- data/VERSION.yml +0 -4
- data/lib/tryouts/section.rb +0 -27
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 54c28bd26a736f9c78bbf68a4ea1202859304a032ea101d91720cc9eabf0d3d0
|
4
|
+
data.tar.gz: 3c28d8b2baa2a4e95c7cbf062b144706a60dd57e055e0b5d98de37a3a3ef6b6c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 33d673cbe361ee0d20a3e23d7a27ba65abf7bca0700df5c4fd66e4f9b9705494cc9c5de0dcfd59e6e015bfad87369f8a32cff0225efaac99a877588999469779
|
7
|
+
data.tar.gz: e280b2f0bdba2fe976c120061af0fe2d9711c8388db0554f74bed0d66641d2e4771138467d7941d0989802a4e58706283b731463098494705150dc6471f27740
|
data/LICENSE.txt
CHANGED
data/README.md
CHANGED
@@ -1,109 +1,223 @@
|
|
1
|
-
# Tryouts
|
1
|
+
# Tryouts v3.0.0-pre
|
2
2
|
|
3
3
|
**Ruby tests that read like documentation.**
|
4
4
|
|
5
|
-
A
|
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
|
+
|
7
|
+
> [!WARNING]
|
8
|
+
> Version 3.0 uses Ruby's Prism parser and pattern matching, requiring Ruby 3.4+
|
9
|
+
|
10
|
+
## Key Features
|
11
|
+
|
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
|
16
|
+
- **Enhanced error reporting** with line numbers and context
|
6
17
|
|
7
18
|
## Installation
|
8
19
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
20
|
+
Add to your Gemfile:
|
21
|
+
```ruby
|
22
|
+
gem 'tryouts', '~> 3.0.0-pre'
|
23
|
+
```
|
24
|
+
|
25
|
+
Or install directly:
|
26
|
+
```bash
|
27
|
+
gem install tryouts --pre
|
28
|
+
```
|
13
29
|
|
30
|
+
From source:
|
14
31
|
```bash
|
15
|
-
|
32
|
+
git clone https://github.com/delano/tryouts.git
|
33
|
+
cd tryouts
|
34
|
+
bundle install
|
16
35
|
```
|
17
36
|
|
37
|
+
## Requirements
|
38
|
+
|
39
|
+
- **Ruby >= 3.4.4** (for Prism parser and pattern matching)
|
40
|
+
- **RSpec** or **Minitest** (optional, for framework integration)
|
41
|
+
|
18
42
|
## Usage
|
19
43
|
|
44
|
+
### Basic Commands
|
45
|
+
|
20
46
|
```bash
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
47
|
+
# Auto-discover and run all tests
|
48
|
+
try
|
49
|
+
|
50
|
+
# 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
66
|
```
|
37
67
|
|
38
|
-
### Exit
|
68
|
+
### Exit Codes
|
39
69
|
|
40
|
-
|
70
|
+
- `0`: All tests pass
|
71
|
+
- `1+`: Number of failing tests
|
41
72
|
|
73
|
+
## Writing Tests
|
42
74
|
|
43
|
-
|
75
|
+
Tryouts use a unique comment-based expectation syntax:
|
44
76
|
|
45
77
|
```ruby
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
78
|
+
# Setup code runs before all tests
|
79
|
+
puts 'Setup running'
|
80
|
+
|
81
|
+
## Simple test with expectation
|
82
|
+
a = 1 + 1
|
83
|
+
#=> 2
|
84
|
+
|
85
|
+
## Multi-line test with description
|
86
|
+
## TEST: Addition works correctly
|
87
|
+
a = 1
|
88
|
+
b = 2
|
89
|
+
a + b
|
90
|
+
#=> 3
|
91
|
+
|
92
|
+
## Testing object methods
|
93
|
+
'hello'.upcase
|
94
|
+
#=> 'HELLO'
|
95
|
+
|
96
|
+
## Expressions are evaluated
|
97
|
+
81
|
98
|
+
#=> 9 * 9
|
99
|
+
|
100
|
+
## Testing errors with rescue
|
101
|
+
begin
|
102
|
+
raise RuntimeError, "test error"
|
103
|
+
rescue RuntimeError
|
104
|
+
:caught
|
105
|
+
end
|
106
|
+
#=> :caught
|
107
|
+
|
108
|
+
# Teardown runs after all tests
|
109
|
+
puts 'Cleanup complete'
|
74
110
|
```
|
75
111
|
|
76
|
-
|
112
|
+
### Test Structure
|
77
113
|
|
114
|
+
- **Setup Section**: Code before first test case (accessible via instance variables)
|
115
|
+
- **Test Cases**: Description lines (`##`), Ruby code, and expectations (`#=>`)
|
116
|
+
- **Teardown Section**: Code after last test case
|
78
117
|
|
79
|
-
|
118
|
+
## Framework Integration
|
80
119
|
|
81
|
-
|
120
|
+
Version 3.0 introduces framework translators that convert tryouts into established test frameworks:
|
82
121
|
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
122
|
+
### RSpec Integration
|
123
|
+
|
124
|
+
```bash
|
125
|
+
try --rspec file_try.rb
|
126
|
+
```
|
127
|
+
|
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
|
87
135
|
|
136
|
+
```bash
|
137
|
+
try --minitest file_try.rb
|
138
|
+
```
|
88
139
|
|
89
|
-
|
90
|
-
|
91
|
-
|
140
|
+
Creates Minitest classes with `test_*` methods for:
|
141
|
+
- Standard assertions
|
142
|
+
- Custom test cases
|
143
|
+
- CI/CD integration
|
144
|
+
- Debugging support
|
92
145
|
|
146
|
+
### Direct Mode
|
93
147
|
|
94
|
-
|
95
|
-
|
148
|
+
```bash
|
149
|
+
try file_try.rb # Original tryouts execution
|
96
150
|
```
|
97
151
|
|
98
|
-
|
152
|
+
Uses shared execution context with all tests running in the same environment.
|
153
|
+
|
154
|
+
## File Discovery
|
155
|
+
|
156
|
+
Auto-discovers test files matching these patterns:
|
157
|
+
- `./try/*_try.rb`
|
158
|
+
- `./tryouts/*_try.rb`
|
159
|
+
- `./*_try.rb`
|
160
|
+
|
161
|
+
Files are processed in lexical order.
|
162
|
+
|
163
|
+
## Modern Architecture (v3.0)
|
164
|
+
|
165
|
+
### Core Components
|
166
|
+
|
167
|
+
- **Prism Parser**: Native Ruby parsing with pattern matching for line classification
|
168
|
+
- **Data Structures**: Immutable `Data.define` classes for test representation
|
169
|
+
- **Framework Translators**: Convert tryouts to RSpec/Minitest format
|
170
|
+
- **CLI**: Modern command-line interface with framework selection
|
171
|
+
|
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
|
+
|
208
|
+
## Examples
|
209
|
+
|
210
|
+
For real-world usage examples, see:
|
211
|
+
- [Onetimesecret tryouts](https://github.com/onetimesecret/onetimesecret/)
|
212
|
+
- Test files in this repository: `try/*_try.rb` and `doc/`
|
99
213
|
|
214
|
+
## AI Development Assistance
|
100
215
|
|
101
|
-
|
216
|
+
This version of Tryouts was developed with assistance from AI tools. The following tools provided significant help with architecture design, code generation, and documentation:
|
102
217
|
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
* Christian Michon for suggesting a better default output format.
|
218
|
+
- **Claude Sonnet 4** - Architecture design, code generation, and documentation
|
219
|
+
- **Claude Desktop & Claude Code** - Interactive development sessions and debugging
|
220
|
+
- **GitHub Copilot** - Code completion and refactoring assistance
|
221
|
+
- **Qodo Merge Pro** - Code review and quality improvements
|
108
222
|
|
109
|
-
|
223
|
+
I remain responsible for all design decisions and the final code. I believe in being transparent about development tools, especially as AI becomes more integrated into our workflows as developers.
|
data/exe/try
CHANGED
@@ -1,51 +1,59 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
1
|
+
#!/usr/bin/env /Users/d/.rbenv/shims/ruby
|
2
|
+
|
3
|
+
# Coverage tracking (must be first)
|
4
|
+
if ENV['COVERAGE'] || ENV['SIMPLECOV']
|
5
|
+
require 'simplecov'
|
6
|
+
# Reset any existing coverage data to prevent line count mismatches
|
7
|
+
Coverage.result(stop: false, clear: true) if defined?(Coverage)
|
8
|
+
SimpleCov.start do
|
9
|
+
track_files 'lib/**/*.rb'
|
10
|
+
add_filter '/try/'
|
11
|
+
add_filter '/test/'
|
12
|
+
add_filter '/test_'
|
13
|
+
add_filter '/spec/'
|
14
|
+
add_filter '/examples/'
|
15
|
+
add_filter '/docs/'
|
16
|
+
|
17
|
+
add_group 'Core', 'lib/tryouts.rb'
|
18
|
+
add_group 'CLI', 'lib/tryouts/cli'
|
19
|
+
add_group 'Formatters', 'lib/tryouts/cli/formatters'
|
20
|
+
add_group 'Parsers', 'lib/tryouts/prism_parser.rb'
|
21
|
+
add_group 'Data Structures', ['lib/tryouts/testcase.rb', 'lib/tryouts/testbatch.rb']
|
22
|
+
add_group 'Translators', 'lib/tryouts/translators'
|
23
|
+
add_group 'Execution', ['lib/tryouts/test_executor.rb', 'lib/tryouts/test_runner.rb', 'lib/tryouts/file_processor.rb']
|
24
|
+
|
25
|
+
coverage_dir 'coverage'
|
26
|
+
|
27
|
+
# Coverage thresholds disabled to prevent CI failures
|
28
|
+
# minimum_coverage 80
|
29
|
+
# minimum_coverage_by_file 70
|
30
|
+
end
|
31
|
+
|
32
|
+
SimpleCov.command_name 'Tryouts CLI'
|
33
|
+
end
|
2
34
|
|
3
|
-
require 'optparse'
|
4
35
|
require_relative '../lib/tryouts'
|
5
36
|
|
6
|
-
#
|
7
|
-
# to require the ruby code they are testing without
|
8
|
-
# needing even if the files use regular requires. It
|
9
|
-
# skips the test files from having to muck with the
|
10
|
-
# load path or use `require_relative` which is easy
|
11
|
-
# to forget. This is a convenience feature.
|
12
|
-
#
|
13
|
-
# Here we're looking for directories relative to the
|
14
|
-
# current working directory (i.e. where this script
|
15
|
-
# is being run from.
|
37
|
+
# Add development library paths
|
16
38
|
lib_glob = File.join(Dir.pwd, '{lib,../lib,.}')
|
17
|
-
Tryouts.update_load_path(lib_glob)
|
39
|
+
Tryouts.update_load_path(lib_glob) if Tryouts.respond_to?(:update_load_path)
|
18
40
|
|
19
|
-
# Parse
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
opts.on('-h', '--help', 'Display this help') do
|
30
|
-
puts opts
|
31
|
-
exit
|
41
|
+
# Parse args and run CLI
|
42
|
+
begin
|
43
|
+
files, options = Tryouts::CLI.parse_args(ARGV)
|
44
|
+
|
45
|
+
# Default file discovery if no files specified
|
46
|
+
if files.empty?
|
47
|
+
files = Dir.glob(
|
48
|
+
['./{try,tryouts,.}/*_try.rb'],
|
49
|
+
base: Dir.pwd,
|
50
|
+
).sort
|
32
51
|
end
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
base: Dir.pwd
|
42
|
-
).sort # deterministic order
|
43
|
-
|
44
|
-
else
|
45
|
-
ARGV
|
46
|
-
end
|
47
|
-
|
48
|
-
# Running the tryouts returns the number of
|
49
|
-
# test failures so here we pass that value
|
50
|
-
# through as the exit code for the script.
|
51
|
-
exit Tryouts.run_all(*paths)
|
52
|
+
|
53
|
+
cli = Tryouts::CLI.new
|
54
|
+
exit cli.run(files, **options)
|
55
|
+
rescue StandardError => ex
|
56
|
+
warn "Error: #{ex.message}"
|
57
|
+
warn ex.backtrace.join("\n") if options[:debug]
|
58
|
+
exit 1
|
59
|
+
end
|
data/exe/tryouts
CHANGED
@@ -0,0 +1,108 @@
|
|
1
|
+
# lib/tryouts/cli/formatters/base.rb
|
2
|
+
|
3
|
+
require_relative 'factory'
|
4
|
+
require_relative 'output_manager'
|
5
|
+
|
6
|
+
class Tryouts
|
7
|
+
class CLI
|
8
|
+
# Enhanced interface for all test output formatting
|
9
|
+
module FormatterInterface
|
10
|
+
# Phase-level output (major sections)
|
11
|
+
def phase_header(message, file_count = nil, level = 0)
|
12
|
+
raise NotImplementedError, "#{self.class} must implement #phase_header"
|
13
|
+
end
|
14
|
+
|
15
|
+
# File-level operations
|
16
|
+
def file_start(file_path, context_info = {})
|
17
|
+
raise NotImplementedError, "#{self.class} must implement #file_start"
|
18
|
+
end
|
19
|
+
|
20
|
+
def file_parsed(file_path, test_count, setup_present: false, teardown_present: false)
|
21
|
+
raise NotImplementedError, "#{self.class} must implement #file_parsed"
|
22
|
+
end
|
23
|
+
|
24
|
+
def file_execution_start(file_path, test_count, context_mode)
|
25
|
+
raise NotImplementedError, "#{self.class} must implement #file_execution_start"
|
26
|
+
end
|
27
|
+
|
28
|
+
def file_result(file_path, total_tests, failed_count, error_count, elapsed_time)
|
29
|
+
raise NotImplementedError, "#{self.class} must implement #file_result"
|
30
|
+
end
|
31
|
+
|
32
|
+
# Test-level operations
|
33
|
+
def test_start(test_case, index, total)
|
34
|
+
raise NotImplementedError, "#{self.class} must implement #test_start"
|
35
|
+
end
|
36
|
+
|
37
|
+
def test_result(test_case, result_status, actual_results = [], elapsed_time = nil)
|
38
|
+
raise NotImplementedError, "#{self.class} must implement #test_result"
|
39
|
+
end
|
40
|
+
|
41
|
+
def test_output(test_case, output_text)
|
42
|
+
raise NotImplementedError, "#{self.class} must implement #test_output"
|
43
|
+
end
|
44
|
+
|
45
|
+
# Setup/teardown operations
|
46
|
+
def setup_start(line_range)
|
47
|
+
raise NotImplementedError, "#{self.class} must implement #setup_start"
|
48
|
+
end
|
49
|
+
|
50
|
+
def setup_output(output_text)
|
51
|
+
raise NotImplementedError, "#{self.class} must implement #setup_output"
|
52
|
+
end
|
53
|
+
|
54
|
+
def teardown_start(line_range)
|
55
|
+
raise NotImplementedError, "#{self.class} must implement #teardown_start"
|
56
|
+
end
|
57
|
+
|
58
|
+
def teardown_output(output_text)
|
59
|
+
raise NotImplementedError, "#{self.class} must implement #teardown_output"
|
60
|
+
end
|
61
|
+
|
62
|
+
# Summary operations
|
63
|
+
def batch_summary(total_tests, failed_count, elapsed_time)
|
64
|
+
raise NotImplementedError, "#{self.class} must implement #batch_summary"
|
65
|
+
end
|
66
|
+
|
67
|
+
def grand_total(total_tests, failed_count, error_count, successful_files, total_files, elapsed_time)
|
68
|
+
raise NotImplementedError, "#{self.class} must implement #grand_total"
|
69
|
+
end
|
70
|
+
|
71
|
+
# Debug and diagnostic output
|
72
|
+
def debug_info(message, level = 0)
|
73
|
+
raise NotImplementedError, "#{self.class} must implement #debug_info"
|
74
|
+
end
|
75
|
+
|
76
|
+
def trace_info(message, level = 0)
|
77
|
+
raise NotImplementedError, "#{self.class} must implement #trace_info"
|
78
|
+
end
|
79
|
+
|
80
|
+
def error_message(message, details = nil)
|
81
|
+
raise NotImplementedError, "#{self.class} must implement #error_message"
|
82
|
+
end
|
83
|
+
|
84
|
+
# Utility methods
|
85
|
+
def raw_output(text)
|
86
|
+
raise NotImplementedError, "#{self.class} must implement #raw_output"
|
87
|
+
end
|
88
|
+
|
89
|
+
def separator(style = :light)
|
90
|
+
raise NotImplementedError, "#{self.class} must implement #separator"
|
91
|
+
end
|
92
|
+
|
93
|
+
def indent_text(text, level)
|
94
|
+
indent = ' ' * level
|
95
|
+
"#{indent}#{text}"
|
96
|
+
end
|
97
|
+
|
98
|
+
def with_indent(level)
|
99
|
+
old_indent = @current_indent
|
100
|
+
@current_indent = level
|
101
|
+
yield
|
102
|
+
ensure
|
103
|
+
@current_indent = old_indent
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
end
|
108
|
+
end
|