tryouts 3.0.0 → 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
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2b560848d9f29bcfe84ae8984317c4c9d87eaff34f9ebd91141ad7bfc0af1ca8
|
4
|
+
data.tar.gz: 87c833052a1267da1f0e8a6786565baf0cd880280ca676951aaeddde7aa0601a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 72a738d41925f4e24ffc233d4e1e5f3dc96f7eaa9ee72eca9ab181da10ec3a283dd5b85b14b91c436acb777a005cb3529a1958c97fdc5f30c13f642de3466551
|
7
|
+
data.tar.gz: 0102df1a296fa62e04e05fd97c0e21ea83e4e2e718885cde380047c784c8ef0304e64182fee7ae45aa2c8ccac585fe7df6f21f527a2cb11a7e88d33d912028eb
|
data/README.md
CHANGED
@@ -1,78 +1,44 @@
|
|
1
|
-
# Tryouts v3.
|
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
|
-
- **
|
14
|
-
- **
|
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
|
-
|
20
|
+
# Add to your Gemfile:
|
21
|
+
gem 'tryouts'
|
23
22
|
```
|
24
23
|
|
25
|
-
Or install directly:
|
26
24
|
```bash
|
27
|
-
|
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/
|
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
|
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
|
-
|
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
|
83
|
+
- **Teardown**: Code after last test case
|
117
84
|
|
118
|
-
|
85
|
+
### Great Expectations System
|
119
86
|
|
120
|
-
|
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
|
-
|
125
|
-
try --rspec file_try.rb
|
126
|
-
```
|
102
|
+
### Using other test runners
|
127
103
|
|
128
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
149
|
-
try
|
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
|
-
|
122
|
+
### Exit Codes
|
153
123
|
|
154
|
-
|
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
|
-
|
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
|
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
|
-
-
|
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
|
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
|
-
|
48
|
-
['
|
61
|
+
raw_files = Dir.glob(
|
62
|
+
['{app,apps,lib,try,tryouts}/**/*_try.rb', './*_try.rb'],
|
49
63
|
base: Dir.pwd,
|
50
|
-
)
|
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
|
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
|
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
|
-
|
104
|
+
def indent_text(text, level = nil)
|
105
|
+
level ||= current_indent || 0
|
106
|
+
indent = ' ' * level
|
95
107
|
"#{indent}#{text}"
|
96
108
|
end
|
97
109
|
|