rspock 2.3.1 → 2.5.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/CHANGELOG.md +143 -19
- data/Gemfile.lock +1 -1
- data/README.md +115 -14
- data/lib/rspock/ast/interaction_to_mocha_mock_transformation.rb +11 -2
- data/lib/rspock/ast/node.rb +35 -1
- data/lib/rspock/ast/parser/expect_block.rb +12 -0
- data/lib/rspock/ast/parser/interaction_parser.rb +12 -4
- data/lib/rspock/ast/parser/statement_parser.rb +75 -0
- data/lib/rspock/ast/parser/then_block.rb +16 -2
- data/lib/rspock/ast/statement_to_assertion_transformation.rb +68 -0
- data/lib/rspock/ast/test_method_transformation.rb +50 -7
- data/lib/rspock/version.rb +1 -1
- metadata +4 -3
- data/lib/rspock/ast/comparison_to_assertion_transformation.rb +0 -40
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: d6c10b52d342827b092af647c5bc144f4e381dff91167eedf93a0f99c9488ee2
|
|
4
|
+
data.tar.gz: 20ea274b4fa0e3a79eefb88f33c53ddf84adfc582fd37fe3e2ee9688a8137747
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: '019855311bf85f23d10fb75d3b1c0020b52fbf2ae7083205ca69a3f15b142ee7c2ba24e9be0506c690e65b42302b347ea7dc256c596edf1069ec8a0700aad666'
|
|
7
|
+
data.tar.gz: a559d70b0f3509d361234019935a2105c4a2b96ddac5ce0df7198edff346fe83a5c7a9c889d5152fbf0a856a60edd069017c3b24f88b144eda651ab35a57b47b
|
data/CHANGELOG.md
CHANGED
|
@@ -1,46 +1,170 @@
|
|
|
1
1
|
# Changelog
|
|
2
|
+
|
|
2
3
|
All notable changes to this project will be documented in this file.
|
|
3
4
|
|
|
4
|
-
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
5
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
6
7
|
|
|
7
|
-
## [
|
|
8
|
+
## [Unreleased]
|
|
9
|
+
|
|
10
|
+
## [2.5.0] - 2026-02-28
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- Exception conditions: `raises ExceptionClass` in Then blocks wraps the preceding When block in an exception assertion.
|
|
15
|
+
- Exception capture: `e = raises ExceptionClass` captures the exception for further assertions in the same Then block.
|
|
16
|
+
- Exception conditions work with data-driven `Where` blocks.
|
|
17
|
+
|
|
18
|
+
### Changed
|
|
19
|
+
|
|
20
|
+
- Renamed interaction outcome nodes from `rspock_returns` / `rspock_raises` to `rspock_stub_returns` / `rspock_stub_raises` to distinguish them from the new exception condition `rspock_raises`.
|
|
21
|
+
|
|
22
|
+
## [2.4.0] - 2026-02-28
|
|
23
|
+
|
|
24
|
+
### Added
|
|
25
|
+
|
|
26
|
+
- Spock-style implicit assertions: every non-assignment statement in Then/Expect blocks is now an assertion — no assertion API needed.
|
|
27
|
+
- Binary operator assertions: `=~`, `!~`, `>`, `<`, `>=`, `<=` with clear error messages.
|
|
28
|
+
- General statement assertions: bare boolean expressions (e.g. `obj.valid?`) with the original source text in the error message.
|
|
29
|
+
- Negation support: `!expr` is detected automatically and produces a clear error message.
|
|
30
|
+
- `>> raises(...)` syntax for exception stubbing in interactions.
|
|
31
|
+
|
|
32
|
+
### Changed
|
|
33
|
+
|
|
34
|
+
- Renamed `ConditionParser` to `StatementParser` and `ConditionToAssertionTransformation` to `StatementToAssertionTransformation` for consistency with Spock's model.
|
|
35
|
+
- Then and Expect block parsers now use `StatementParser` for statement classification.
|
|
36
|
+
|
|
37
|
+
### Removed
|
|
38
|
+
|
|
39
|
+
- `ComparisonToAssertionTransformation` — replaced by `StatementToAssertionTransformation`.
|
|
40
|
+
|
|
41
|
+
## [2.3.1] - 2026-02-27
|
|
42
|
+
|
|
43
|
+
### Fixed
|
|
44
|
+
|
|
45
|
+
- Require `block_capture` so it is available at runtime.
|
|
46
|
+
|
|
47
|
+
## [2.3.0] - 2026-02-27
|
|
48
|
+
|
|
49
|
+
### Added
|
|
50
|
+
|
|
51
|
+
- Interaction transformations and block identity verification via `&` operator.
|
|
52
|
+
- RSpock AST node hierarchy (`Node`, `InteractionNode`, `BodyNode`, etc.) for type-safe AST handling.
|
|
53
|
+
- `TestMethodParser` extracted from `TestMethodTransformation` for separation of parsing and transformation.
|
|
54
|
+
|
|
55
|
+
### Changed
|
|
56
|
+
|
|
57
|
+
- Restructured block classes into `Parser` namespace and converted `InteractionParser` to a class.
|
|
58
|
+
- Introduced `BodyNode` and removed legacy interaction transformations.
|
|
59
|
+
|
|
60
|
+
## [2.2.0] - 2026-02-25
|
|
61
|
+
|
|
62
|
+
### Added
|
|
63
|
+
|
|
64
|
+
- Interaction stubbing with `>>` for return value stubbing in Then block interactions.
|
|
65
|
+
|
|
66
|
+
### Fixed
|
|
67
|
+
|
|
68
|
+
- Pry and pry-byebug compatibility.
|
|
69
|
+
- Failing test on Ruby 3+.
|
|
70
|
+
- `filter_string` for `ast_transform` 2.1.4 source mapping change.
|
|
71
|
+
|
|
72
|
+
## [2.1.0] - 2026-02-21
|
|
73
|
+
|
|
8
74
|
### Added
|
|
9
|
-
|
|
75
|
+
|
|
76
|
+
- Ruby 4.0 support.
|
|
77
|
+
|
|
78
|
+
### Fixed
|
|
79
|
+
|
|
80
|
+
- Codecov badge URL to use master branch.
|
|
81
|
+
|
|
82
|
+
## [2.0.0] - 2026-02-21
|
|
10
83
|
|
|
11
84
|
### Changed
|
|
85
|
+
|
|
86
|
+
- Minimum Ruby version bumped to 3.2.
|
|
87
|
+
- Upgraded to Ruby 3.x compatibility.
|
|
88
|
+
- Use `ast_transform` 2.0.0 from RubyGems.
|
|
89
|
+
- CI modernization and release workflow improvements.
|
|
90
|
+
|
|
91
|
+
## [1.0.0] - 2020-07-09
|
|
92
|
+
|
|
93
|
+
### Added
|
|
94
|
+
|
|
95
|
+
- Interaction-based testing: mock with expectations in the Then block.
|
|
96
|
+
- Travis CI and code coverage.
|
|
97
|
+
|
|
98
|
+
### Changed
|
|
99
|
+
|
|
12
100
|
- Test names now have the test index and line number as suffix instead of prefix.
|
|
13
|
-
- Cleanup
|
|
101
|
+
- Removed unnecessary ensure block when Cleanup block is empty; moved source map wrapper to class scope.
|
|
102
|
+
- Bump `ast_transform` to release 1.0.0.
|
|
14
103
|
|
|
15
104
|
### Fixed
|
|
16
|
-
- Fixed source mapping for transformed assertion nodes.
|
|
17
105
|
|
|
18
|
-
|
|
106
|
+
- Source mapping for transformed assertion nodes.
|
|
107
|
+
- Truth table generator command with proper escaping.
|
|
108
|
+
|
|
109
|
+
## [0.2.5] - 2019-05-28
|
|
110
|
+
|
|
19
111
|
### Fixed
|
|
20
|
-
- Fixed BacktraceFilter so that source mapping works again
|
|
21
112
|
|
|
22
|
-
|
|
113
|
+
- BacktraceFilter so that source mapping works again.
|
|
114
|
+
|
|
115
|
+
## [0.2.4] - 2019-05-27
|
|
116
|
+
|
|
23
117
|
### Changed
|
|
24
|
-
- Bump Unparser dependency from ~> 0.2.8 to ~> 0.4
|
|
25
118
|
|
|
26
|
-
|
|
119
|
+
- Bump Unparser dependency from `~> 0.2.8` to `~> 0.4`.
|
|
120
|
+
|
|
121
|
+
## [0.2.3] - 2018-11-09
|
|
122
|
+
|
|
27
123
|
### Fixed
|
|
28
|
-
- Cleanup block can now contain more than one node
|
|
29
124
|
|
|
30
|
-
|
|
125
|
+
- Cleanup block can now contain more than one node.
|
|
126
|
+
|
|
127
|
+
## [0.2.2] - 2018-11-08
|
|
128
|
+
|
|
31
129
|
### Changed
|
|
32
|
-
- Extracted ASTTransform to its own gem: `ast_transform`
|
|
33
130
|
|
|
34
|
-
|
|
131
|
+
- Extracted ASTTransform to its own gem: `ast_transform`.
|
|
132
|
+
|
|
133
|
+
## [0.2.1] - 2018-10-09
|
|
134
|
+
|
|
35
135
|
### Added
|
|
36
|
-
|
|
136
|
+
|
|
137
|
+
- `_line_number_` is now displayed in the test name and available in test scope for debugging.
|
|
37
138
|
|
|
38
139
|
### Changed
|
|
39
|
-
- Renamed test_index to _test_index_
|
|
40
140
|
|
|
41
|
-
|
|
141
|
+
- Renamed `test_index` to `_test_index_`.
|
|
142
|
+
|
|
143
|
+
## [0.2.0] - 2018-09-21
|
|
144
|
+
|
|
42
145
|
### Added
|
|
146
|
+
|
|
43
147
|
- Truth table generator Rake task.
|
|
44
148
|
|
|
45
|
-
## [0.1.1] 2018-09-
|
|
46
|
-
|
|
149
|
+
## [0.1.1] - 2018-09-18
|
|
150
|
+
|
|
151
|
+
### Added
|
|
152
|
+
|
|
153
|
+
- Initial release.
|
|
154
|
+
|
|
155
|
+
[Unreleased]: https://github.com/rspockframework/rspock/compare/v2.5.0...HEAD
|
|
156
|
+
[2.5.0]: https://github.com/rspockframework/rspock/compare/v2.4.0...v2.5.0
|
|
157
|
+
[2.4.0]: https://github.com/rspockframework/rspock/compare/v2.3.1...v2.4.0
|
|
158
|
+
[2.3.1]: https://github.com/rspockframework/rspock/compare/v2.3.0...v2.3.1
|
|
159
|
+
[2.3.0]: https://github.com/rspockframework/rspock/compare/v2.2.0...v2.3.0
|
|
160
|
+
[2.2.0]: https://github.com/rspockframework/rspock/compare/v2.1.0...v2.2.0
|
|
161
|
+
[2.1.0]: https://github.com/rspockframework/rspock/compare/v2.0.0...v2.1.0
|
|
162
|
+
[2.0.0]: https://github.com/rspockframework/rspock/compare/1.0.0...v2.0.0
|
|
163
|
+
[1.0.0]: https://github.com/rspockframework/rspock/compare/0.2.5...1.0.0
|
|
164
|
+
[0.2.5]: https://github.com/rspockframework/rspock/compare/0.2.4...0.2.5
|
|
165
|
+
[0.2.4]: https://github.com/rspockframework/rspock/compare/0.2.3...0.2.4
|
|
166
|
+
[0.2.3]: https://github.com/rspockframework/rspock/compare/0.2.2...0.2.3
|
|
167
|
+
[0.2.2]: https://github.com/rspockframework/rspock/compare/0.2.1...0.2.2
|
|
168
|
+
[0.2.1]: https://github.com/rspockframework/rspock/compare/0.2.0...0.2.1
|
|
169
|
+
[0.2.0]: https://github.com/rspockframework/rspock/compare/0.1.1...0.2.0
|
|
170
|
+
[0.1.1]: https://github.com/rspockframework/rspock/releases/tag/0.1.1
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
|
@@ -16,8 +16,11 @@ Note: RSpock is heavily inspired by Spock for the Groovy programming language.
|
|
|
16
16
|
|
|
17
17
|
* BDD-style code blocks: Given, When, Then, Expect, Cleanup, Where
|
|
18
18
|
* Data-driven testing with incredibly expressive table-based Where blocks
|
|
19
|
-
*
|
|
20
|
-
*
|
|
19
|
+
* Spock-style implicit assertions: every statement in Then/Expect blocks is an assertion (no assertion API needed)
|
|
20
|
+
* Binary operator assertions: `==`, `!=`, `=~`, `!~`, `>`, `<`, `>=`, `<=` with clear error messages
|
|
21
|
+
* General statement assertions: bare boolean expressions (e.g. `obj.valid?`, `list.include?(x)`) and negation (`!obj.empty?`) with source-text error messages
|
|
22
|
+
* [Exception conditions](#exception-conditions): `raises ExceptionClass` in Then blocks wraps the When block in an exception assertion, with optional capture for property assertions
|
|
23
|
+
* [Interaction-based testing](#mocking-with-interactions), i.e. `1 * object.receive("message")` in Then blocks, with optional [return value stubbing](#stubbing-return-values) via `>>`, [exception stubbing](#stubbing-exceptions) via `>> raises(...)`, and [block forwarding verification](#block-forwarding-verification) via `&block`
|
|
21
24
|
* (Planned) BDD-style custom reporter that outputs information from Code Blocks
|
|
22
25
|
* (Planned) Capture all Then block violations
|
|
23
26
|
|
|
@@ -150,15 +153,65 @@ The When block describes the stimulus to be applied to the system under test. It
|
|
|
150
153
|
|
|
151
154
|
```ruby
|
|
152
155
|
Then "The product is added to the cart"
|
|
156
|
+
!cart.products.empty?
|
|
153
157
|
cart.products.size == 1
|
|
154
158
|
cart.products.first == product
|
|
159
|
+
cart.products.size > 0
|
|
155
160
|
```
|
|
156
161
|
|
|
157
|
-
The Then block describes the response from the stimulus.
|
|
162
|
+
The Then block describes the response from the stimulus. Following Spock's core model, **every statement is an assertion** unless it's a variable assignment. No assertion API is needed.
|
|
163
|
+
|
|
164
|
+
**Binary operators** (`==`, `!=`, `=~`, `!~`, `>`, `<`, `>=`, `<=`) produce clear error messages on failure. By convention, the **LHS** operand is the **actual** value and the **RHS** is the **expected** value.
|
|
165
|
+
|
|
166
|
+
**General statements** (bare boolean expressions like `obj.valid?`, `list.include?(x)`) include the original source text in the error message, so you see exactly which expression failed. **Negation** (`!expr`) is detected automatically.
|
|
167
|
+
|
|
168
|
+
**Variable assignments** pass through unchanged and execute in source order after the stimulus.
|
|
169
|
+
|
|
170
|
+
##### Exception Conditions
|
|
171
|
+
|
|
172
|
+
Use `raises` in a Then block to assert that the preceding When block raises a specific exception:
|
|
173
|
+
|
|
174
|
+
```ruby
|
|
175
|
+
When "Dividing by zero"
|
|
176
|
+
1 / 0
|
|
177
|
+
|
|
178
|
+
Then "An error is raised"
|
|
179
|
+
raises ZeroDivisionError
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
To inspect the exception, capture it into a variable:
|
|
183
|
+
|
|
184
|
+
```ruby
|
|
185
|
+
When "Parsing bad input"
|
|
186
|
+
JSON.parse("not json")
|
|
187
|
+
|
|
188
|
+
Then "A parse error is raised with a message"
|
|
189
|
+
e = raises JSON::ParserError
|
|
190
|
+
e.message.include?("unexpected token")
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
The captured variable is available for further assertions in the same Then block. Exception conditions work with data-driven `Where` blocks as well:
|
|
194
|
+
|
|
195
|
+
```ruby
|
|
196
|
+
When "Parsing invalid input"
|
|
197
|
+
JSON.parse(input)
|
|
198
|
+
|
|
199
|
+
Then "The expected error is raised"
|
|
200
|
+
raises expected_error
|
|
201
|
+
|
|
202
|
+
Where
|
|
203
|
+
input | expected_error
|
|
204
|
+
"not json" | JSON::ParserError
|
|
205
|
+
"{invalid" | JSON::ParserError
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
Only **one** `raises` condition is allowed per Then block, and `raises` is **not supported** in Expect blocks (use a When + Then block instead).
|
|
158
209
|
|
|
159
210
|
#### Expect Block
|
|
160
211
|
|
|
161
|
-
The Expect block is useful when expressing the stimulus and the response in one statement is more natural.
|
|
212
|
+
The Expect block is useful when expressing the stimulus and the response in one statement is more natural. The same assertion rules apply as in Then blocks — every statement is an assertion unless it's a variable assignment.
|
|
213
|
+
|
|
214
|
+
For example, let's compare two equivalent ways of describing some behaviour:
|
|
162
215
|
|
|
163
216
|
##### When + Then
|
|
164
217
|
```ruby
|
|
@@ -175,6 +228,17 @@ Expect "absolute of -2 is 2"
|
|
|
175
228
|
-2.abs == 2
|
|
176
229
|
```
|
|
177
230
|
|
|
231
|
+
The Expect block supports the full range of assertion expressions:
|
|
232
|
+
|
|
233
|
+
```ruby
|
|
234
|
+
Expect "string matching and predicates"
|
|
235
|
+
str =~ /potato/
|
|
236
|
+
str.include?("pot")
|
|
237
|
+
!str.empty?
|
|
238
|
+
str.length > 3
|
|
239
|
+
str.length == 6
|
|
240
|
+
```
|
|
241
|
+
|
|
178
242
|
A good rule of thumb is using When + Then blocks to describe methods with side-effects and Expect blocks to describe purely functional methods.
|
|
179
243
|
|
|
180
244
|
#### Cleanup Block
|
|
@@ -306,12 +370,12 @@ test "#publish sends a message to all subscribers" do
|
|
|
306
370
|
end
|
|
307
371
|
```
|
|
308
372
|
|
|
309
|
-
The above ___Then___ block contains 2 interactions, each of which has 4 parts: the _cardinality_, the _receiver_, the _message_ and its _arguments_. Optionally,
|
|
373
|
+
The above ___Then___ block contains 2 interactions, each of which has 4 parts: the _cardinality_, the _receiver_, the _message_ and its _arguments_. Optionally, an _outcome_ can be specified using the `>>` operator (either a return value or `raises(...)` for exceptions), and _block forwarding_ can be verified using the `&` operator.
|
|
310
374
|
|
|
311
375
|
```
|
|
312
376
|
1 * receiver.message('hello', &blk) >> "result"
|
|
313
377
|
| | | | | |
|
|
314
|
-
| | | | |
|
|
378
|
+
| | | | | outcome (optional): value or raises(...)
|
|
315
379
|
| | | | block forwarding (optional)
|
|
316
380
|
| | | argument(s) (optional)
|
|
317
381
|
| | message
|
|
@@ -421,6 +485,39 @@ _ * cache.fetch("key") >> expensive_result # a variable
|
|
|
421
485
|
|
|
422
486
|
**Note**: Without `>>`, an interaction sets up an expectation only (the method will return `nil` by default). Use `>>` when the code under test depends on the return value.
|
|
423
487
|
|
|
488
|
+
#### Stubbing Exceptions
|
|
489
|
+
|
|
490
|
+
When the code under test needs a collaborator to raise an exception, use `>> raises(...)` instead of a return value. This sets up the mock to raise the given exception when called.
|
|
491
|
+
|
|
492
|
+
```ruby
|
|
493
|
+
test "#fetch raises when the record is not found" do
|
|
494
|
+
Given
|
|
495
|
+
repository = mock
|
|
496
|
+
service = Service.new(repository)
|
|
497
|
+
|
|
498
|
+
When
|
|
499
|
+
service.fetch(42)
|
|
500
|
+
|
|
501
|
+
Then
|
|
502
|
+
1 * repository.find(42) >> raises(RecordNotFound)
|
|
503
|
+
end
|
|
504
|
+
```
|
|
505
|
+
|
|
506
|
+
You can also pass a message or an exception instance:
|
|
507
|
+
|
|
508
|
+
```ruby
|
|
509
|
+
1 * repository.find(42) >> raises(RecordNotFound, "not found") # class + message
|
|
510
|
+
1 * repository.find(42) >> raises(RecordNotFound.new("not found")) # instance
|
|
511
|
+
```
|
|
512
|
+
|
|
513
|
+
This works with all interaction features — cardinality, arguments, and ranges:
|
|
514
|
+
|
|
515
|
+
```ruby
|
|
516
|
+
(1..3) * service.call(anything) >> raises(TimeoutError)
|
|
517
|
+
```
|
|
518
|
+
|
|
519
|
+
**Note**: Without `raises(...)`, the `>>` operator stubs a return value. With `raises(...)`, it stubs an exception instead.
|
|
520
|
+
|
|
424
521
|
#### Block Forwarding Verification
|
|
425
522
|
|
|
426
523
|
When the code under test forwards a block (or proc) to a collaborator, you may want to verify that the _exact_ block was passed through. RSpock supports this with the `&` operator in interactions, performing an identity check on the block reference.
|
|
@@ -527,20 +624,24 @@ To install this gem onto your local machine, run `bundle exec rake install`.
|
|
|
527
624
|
|
|
528
625
|
## Releasing a New Version
|
|
529
626
|
|
|
530
|
-
There are two ways to create a release. Both require that `version.rb` has already been updated and merged to main.
|
|
627
|
+
There are two ways to create a release. Both require that `version.rb` has already been updated, `CHANGELOG.md` has been updated, and changes have been merged to main.
|
|
531
628
|
|
|
532
629
|
### Via GitHub UI
|
|
533
630
|
|
|
534
|
-
1. Update `VERSION` in `lib/rspock/version.rb` and run `bundle install` to regenerate `Gemfile.lock
|
|
535
|
-
2.
|
|
536
|
-
3.
|
|
537
|
-
4.
|
|
538
|
-
5.
|
|
631
|
+
1. Update `VERSION` in `lib/rspock/version.rb` and run `bundle install` to regenerate `Gemfile.lock`
|
|
632
|
+
2. Move the `[Unreleased]` section in `CHANGELOG.md` to a new version heading with today's date (e.g. `## [2.4.0] - 2026-03-01`) and add a fresh empty `[Unreleased]` section above it. Update the comparison links at the bottom of the file.
|
|
633
|
+
3. Commit, open a PR, and merge to main
|
|
634
|
+
4. Go to the repo on GitHub → **Releases** → **Draft a new release**
|
|
635
|
+
5. Enter a new tag (e.g. `v2.4.0`), select `main` as the target branch
|
|
636
|
+
6. Add a title and release notes (GitHub can auto-generate these from merged PRs)
|
|
637
|
+
7. Click **Publish release**
|
|
539
638
|
|
|
540
639
|
### Via CLI
|
|
541
640
|
|
|
542
|
-
1. Update `VERSION` in `lib/rspock/version.rb` and run `bundle install` to regenerate `Gemfile.lock
|
|
543
|
-
2.
|
|
641
|
+
1. Update `VERSION` in `lib/rspock/version.rb` and run `bundle install` to regenerate `Gemfile.lock`
|
|
642
|
+
2. Move the `[Unreleased]` section in `CHANGELOG.md` to a new version heading with today's date and add a fresh empty `[Unreleased]` section above it. Update the comparison links at the bottom of the file.
|
|
643
|
+
3. Commit, open a PR, and merge to main
|
|
644
|
+
4. Tag and push:
|
|
544
645
|
```
|
|
545
646
|
git checkout main && git pull
|
|
546
647
|
git tag v2.0.0
|
|
@@ -6,11 +6,20 @@ module RSpock
|
|
|
6
6
|
module AST
|
|
7
7
|
# Transforms an :rspock_interaction node into Mocha mock setup code.
|
|
8
8
|
#
|
|
9
|
-
# Input: s(:rspock_interaction, cardinality, receiver, sym, args,
|
|
9
|
+
# Input: s(:rspock_interaction, cardinality, receiver, sym, args, outcome, block_pass)
|
|
10
10
|
# Output: receiver.expects(:message).with(*args).times(n).returns(value)
|
|
11
11
|
#
|
|
12
|
+
# The outcome node type maps directly to the Mocha chain method:
|
|
13
|
+
# :rspock_stub_returns -> .returns(value)
|
|
14
|
+
# :rspock_stub_raises -> .raises(exception_class, ...)
|
|
15
|
+
#
|
|
12
16
|
# When block_pass is present, wraps the expects chain with a BlockCapture.capture call.
|
|
13
17
|
class InteractionToMochaMockTransformation < ASTTransform::AbstractTransformation
|
|
18
|
+
OUTCOME_METHODS = {
|
|
19
|
+
rspock_stub_returns: :returns,
|
|
20
|
+
rspock_stub_raises: :raises,
|
|
21
|
+
}.freeze
|
|
22
|
+
|
|
14
23
|
def initialize(index = 0)
|
|
15
24
|
@index = index
|
|
16
25
|
end
|
|
@@ -21,7 +30,7 @@ module RSpock
|
|
|
21
30
|
result = chain_call(interaction.receiver, :expects, s(:sym, interaction.message))
|
|
22
31
|
result = chain_call(result, :with, *interaction.args.children) if interaction.args
|
|
23
32
|
result = build_cardinality(result, interaction.cardinality)
|
|
24
|
-
result = chain_call(result,
|
|
33
|
+
result = chain_call(result, OUTCOME_METHODS.fetch(interaction.outcome.type), *interaction.outcome.children) if interaction.outcome
|
|
25
34
|
|
|
26
35
|
if interaction.block_pass
|
|
27
36
|
build_block_capture_setup(result, interaction.receiver, interaction.message)
|
data/lib/rspock/ast/node.rb
CHANGED
|
@@ -70,6 +70,25 @@ module RSpock
|
|
|
70
70
|
end
|
|
71
71
|
end
|
|
72
72
|
|
|
73
|
+
class OutcomeNode < Node
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
class StubReturnsNode < OutcomeNode
|
|
77
|
+
register :rspock_stub_returns
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
class StubRaisesNode < OutcomeNode
|
|
81
|
+
register :rspock_stub_raises
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
class RaisesNode < Node
|
|
85
|
+
register :rspock_raises
|
|
86
|
+
|
|
87
|
+
def exception_class = children[0]
|
|
88
|
+
def capture_var = children[1]
|
|
89
|
+
def capture_name = capture_var&.children&.[](0)
|
|
90
|
+
end
|
|
91
|
+
|
|
73
92
|
class InteractionNode < Node
|
|
74
93
|
register :rspock_interaction
|
|
75
94
|
|
|
@@ -78,10 +97,25 @@ module RSpock
|
|
|
78
97
|
def message_sym = children[2]
|
|
79
98
|
def message = message_sym.children[0]
|
|
80
99
|
def args = children[3]
|
|
81
|
-
def
|
|
100
|
+
def outcome = children[4]
|
|
82
101
|
def block_pass = children[5]
|
|
83
102
|
end
|
|
84
103
|
|
|
104
|
+
class BinaryStatementNode < Node
|
|
105
|
+
register :rspock_binary_statement
|
|
106
|
+
|
|
107
|
+
def lhs = children[0]
|
|
108
|
+
def operator = children[1]
|
|
109
|
+
def rhs = children[2]
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
class StatementNode < Node
|
|
113
|
+
register :rspock_statement
|
|
114
|
+
|
|
115
|
+
def expression = children[0]
|
|
116
|
+
def source = children[1]
|
|
117
|
+
end
|
|
118
|
+
|
|
85
119
|
module NodeBuilder
|
|
86
120
|
include ASTTransform::TransformationHelper
|
|
87
121
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
require 'rspock/ast/parser/block'
|
|
3
|
+
require 'rspock/ast/parser/statement_parser'
|
|
3
4
|
|
|
4
5
|
module RSpock
|
|
5
6
|
module AST
|
|
@@ -20,6 +21,17 @@ module RSpock
|
|
|
20
21
|
def successors
|
|
21
22
|
@successors ||= [:Cleanup, :Where].freeze
|
|
22
23
|
end
|
|
24
|
+
|
|
25
|
+
def to_rspock_node
|
|
26
|
+
statement_parser = StatementParser.new
|
|
27
|
+
spock_children = @children.map { |child| statement_parser.parse(child) }
|
|
28
|
+
|
|
29
|
+
if spock_children.any? { |c| c.type == :rspock_raises }
|
|
30
|
+
raise BlockError, "raises() is not supported in Expect blocks @ #{range}. Use a When + Then block instead."
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
s(:rspock_expect, *spock_children)
|
|
34
|
+
end
|
|
23
35
|
end
|
|
24
36
|
end
|
|
25
37
|
end
|
|
@@ -7,14 +7,14 @@ module RSpock
|
|
|
7
7
|
# Parses raw Ruby AST interaction nodes into structured :rspock_interaction nodes.
|
|
8
8
|
#
|
|
9
9
|
# Input: 1 * receiver.message("arg", &blk) >> "result"
|
|
10
|
-
# Output: s(:rspock_interaction, cardinality, receiver, sym, args,
|
|
10
|
+
# Output: s(:rspock_interaction, cardinality, receiver, sym, args, outcome, block_pass)
|
|
11
11
|
#
|
|
12
12
|
# :rspock_interaction children:
|
|
13
13
|
# [0] cardinality - e.g. s(:int, 1), s(:begin, s(:irange, ...)), s(:send, nil, :_)
|
|
14
14
|
# [1] receiver - e.g. s(:send, nil, :subscriber)
|
|
15
15
|
# [2] message - e.g. s(:sym, :receive)
|
|
16
16
|
# [3] args - nil if no args, s(:array, *arg_nodes) otherwise
|
|
17
|
-
# [4]
|
|
17
|
+
# [4] outcome - nil if no >>, otherwise s(:rspock_stub_returns, value) or s(:rspock_stub_raises, *args)
|
|
18
18
|
# [5] block_pass - nil if no &, otherwise s(:block_pass, ...)
|
|
19
19
|
class InteractionParser
|
|
20
20
|
include RSpock::AST::NodeBuilder
|
|
@@ -35,7 +35,7 @@ module RSpock
|
|
|
35
35
|
return node unless interaction_node?(node)
|
|
36
36
|
|
|
37
37
|
if return_value_node?(node)
|
|
38
|
-
|
|
38
|
+
outcome = parse_outcome(node.children[2])
|
|
39
39
|
node = node.children[0]
|
|
40
40
|
end
|
|
41
41
|
|
|
@@ -50,7 +50,7 @@ module RSpock
|
|
|
50
50
|
receiver,
|
|
51
51
|
s(:sym, message),
|
|
52
52
|
args,
|
|
53
|
-
|
|
53
|
+
outcome,
|
|
54
54
|
block_pass
|
|
55
55
|
)
|
|
56
56
|
end
|
|
@@ -61,6 +61,14 @@ module RSpock
|
|
|
61
61
|
node.type == :send && node.children[1] == :>> && interaction_node?(node.children[0])
|
|
62
62
|
end
|
|
63
63
|
|
|
64
|
+
def parse_outcome(node)
|
|
65
|
+
if node.type == :send && node.children[0].nil? && node.children[1] == :raises
|
|
66
|
+
s(:rspock_stub_raises, *node.children[2..])
|
|
67
|
+
else
|
|
68
|
+
s(:rspock_stub_returns, node)
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
64
72
|
def validate_cardinality(node)
|
|
65
73
|
case node.type
|
|
66
74
|
when *ALLOWED_CARDINALITY_NODES
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
require 'rspock/ast/node'
|
|
3
|
+
|
|
4
|
+
module RSpock
|
|
5
|
+
module AST
|
|
6
|
+
module Parser
|
|
7
|
+
# Classifies raw Ruby AST statements into RSpock node types for Then/Expect blocks.
|
|
8
|
+
#
|
|
9
|
+
# - Assignments pass through as raw AST (no wrapping).
|
|
10
|
+
# - Binary operators (==, !=, =~, etc.) become :rspock_binary_statement nodes.
|
|
11
|
+
# - Everything else becomes :rspock_statement nodes with the original source text captured.
|
|
12
|
+
class StatementParser
|
|
13
|
+
include RSpock::AST::NodeBuilder
|
|
14
|
+
|
|
15
|
+
BINARY_OPERATORS = %i[== != =~ !~ > < >= <=].freeze
|
|
16
|
+
ASSIGNMENT_TYPES = %i[lvasgn masgn op_asgn or_asgn and_asgn].freeze
|
|
17
|
+
|
|
18
|
+
def parse(node)
|
|
19
|
+
return build_raises(node) if raises_condition?(node)
|
|
20
|
+
return node if assignment?(node)
|
|
21
|
+
return build_binary_statement(node) if binary_statement?(node)
|
|
22
|
+
|
|
23
|
+
build_statement(node)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
private
|
|
27
|
+
|
|
28
|
+
def raises_condition?(node)
|
|
29
|
+
direct_raises?(node) || assigned_raises?(node)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def direct_raises?(node)
|
|
33
|
+
node.type == :send && node.children[0].nil? && node.children[1] == :raises
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def assigned_raises?(node)
|
|
37
|
+
node.type == :lvasgn &&
|
|
38
|
+
node.children[1]&.type == :send &&
|
|
39
|
+
node.children[1].children[0].nil? &&
|
|
40
|
+
node.children[1].children[1] == :raises
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def build_raises(node)
|
|
44
|
+
if node.type == :lvasgn
|
|
45
|
+
variable = s(:sym, node.children[0])
|
|
46
|
+
exception_class = node.children[1].children[2]
|
|
47
|
+
s(:rspock_raises, exception_class, variable)
|
|
48
|
+
else
|
|
49
|
+
exception_class = node.children[2]
|
|
50
|
+
s(:rspock_raises, exception_class)
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def assignment?(node)
|
|
55
|
+
ASSIGNMENT_TYPES.include?(node.type)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def binary_statement?(node)
|
|
59
|
+
node.type == :send &&
|
|
60
|
+
node.children.length == 3 &&
|
|
61
|
+
BINARY_OPERATORS.include?(node.children[1])
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def build_binary_statement(node)
|
|
65
|
+
s(:rspock_binary_statement, node.children[0], s(:sym, node.children[1]), node.children[2])
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def build_statement(node)
|
|
69
|
+
source = node.loc&.expression&.source || node.inspect
|
|
70
|
+
s(:rspock_statement, node, s(:str, source))
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
require 'rspock/ast/parser/block'
|
|
3
3
|
require 'rspock/ast/parser/interaction_parser'
|
|
4
|
+
require 'rspock/ast/parser/statement_parser'
|
|
4
5
|
|
|
5
6
|
module RSpock
|
|
6
7
|
module AST
|
|
@@ -19,8 +20,21 @@ module RSpock
|
|
|
19
20
|
end
|
|
20
21
|
|
|
21
22
|
def to_rspock_node
|
|
22
|
-
|
|
23
|
-
|
|
23
|
+
interaction_parser = InteractionParser.new
|
|
24
|
+
statement_parser = StatementParser.new
|
|
25
|
+
|
|
26
|
+
spock_children = @children.map do |child|
|
|
27
|
+
parsed = interaction_parser.parse(child)
|
|
28
|
+
next parsed unless parsed.equal?(child)
|
|
29
|
+
|
|
30
|
+
statement_parser.parse(child)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
raises_count = spock_children.count { |c| c.type == :rspock_raises }
|
|
34
|
+
if raises_count > 1
|
|
35
|
+
raise BlockError, "Then block @ #{range} may contain at most one raises() condition"
|
|
36
|
+
end
|
|
37
|
+
|
|
24
38
|
s(:rspock_then, *spock_children)
|
|
25
39
|
end
|
|
26
40
|
end
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
require 'rspock/ast/node'
|
|
3
|
+
|
|
4
|
+
module RSpock
|
|
5
|
+
module AST
|
|
6
|
+
# Transforms :rspock_binary_statement and :rspock_statement nodes into Minitest assertion calls.
|
|
7
|
+
#
|
|
8
|
+
# Binary statements dispatch to specialized assertions (assert_equal, assert_match, assert_operator).
|
|
9
|
+
# General statements use assert_equal(true/false, expr, source_message) with negation detection.
|
|
10
|
+
class StatementToAssertionTransformation
|
|
11
|
+
include RSpock::AST::NodeBuilder
|
|
12
|
+
|
|
13
|
+
BINARY_DISPATCH = {
|
|
14
|
+
:== => :assert_equal,
|
|
15
|
+
:!= => :refute_equal,
|
|
16
|
+
:=~ => :assert_match,
|
|
17
|
+
:'!~' => :refute_match,
|
|
18
|
+
}.freeze
|
|
19
|
+
|
|
20
|
+
OPERATOR_ASSERTIONS = %i[> < >= <=].freeze
|
|
21
|
+
|
|
22
|
+
def run(node)
|
|
23
|
+
case node.type
|
|
24
|
+
when :rspock_binary_statement
|
|
25
|
+
transform_binary_statement(node)
|
|
26
|
+
when :rspock_statement
|
|
27
|
+
transform_statement(node)
|
|
28
|
+
else
|
|
29
|
+
node
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
private
|
|
34
|
+
|
|
35
|
+
def transform_binary_statement(node)
|
|
36
|
+
lhs = node.lhs
|
|
37
|
+
op = node.operator.children[0]
|
|
38
|
+
rhs = node.rhs
|
|
39
|
+
|
|
40
|
+
if (assertion = BINARY_DISPATCH[op])
|
|
41
|
+
s(:send, nil, assertion, rhs, lhs)
|
|
42
|
+
elsif OPERATOR_ASSERTIONS.include?(op)
|
|
43
|
+
s(:send, nil, :assert_operator, lhs, s(:sym, op), rhs)
|
|
44
|
+
else
|
|
45
|
+
s(:send, nil, :assert_operator, lhs, s(:sym, op), rhs)
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def transform_statement(node)
|
|
50
|
+
expr = node.expression
|
|
51
|
+
source_text = node.source.children[0]
|
|
52
|
+
|
|
53
|
+
if negated?(expr)
|
|
54
|
+
inner = expr.children[0]
|
|
55
|
+
message = "Expected \"#{source_text}\" to be false"
|
|
56
|
+
s(:send, nil, :assert_equal, s(:false), inner, s(:str, message))
|
|
57
|
+
else
|
|
58
|
+
message = "Expected \"#{source_text}\" to be true"
|
|
59
|
+
s(:send, nil, :assert_equal, s(:true), expr, s(:str, message))
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def negated?(node)
|
|
64
|
+
node.type == :send && node.children[1] == :! && node.children.length == 2
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
require 'ast_transform/abstract_transformation'
|
|
3
3
|
require 'rspock/ast/node'
|
|
4
|
-
require 'rspock/ast/
|
|
4
|
+
require 'rspock/ast/statement_to_assertion_transformation'
|
|
5
5
|
require 'rspock/ast/header_nodes_transformation'
|
|
6
6
|
require 'rspock/ast/interaction_to_mocha_mock_transformation'
|
|
7
7
|
require 'rspock/ast/interaction_to_block_identity_assertion_transformation'
|
|
@@ -14,7 +14,7 @@ module RSpock
|
|
|
14
14
|
class TestMethodTransformation < ASTTransform::AbstractTransformation
|
|
15
15
|
def initialize(block_registry, strict: true)
|
|
16
16
|
@parser = Parser::TestMethodParser.new(block_registry, strict: strict)
|
|
17
|
-
@
|
|
17
|
+
@statement_transformation = StatementToAssertionTransformation.new
|
|
18
18
|
end
|
|
19
19
|
|
|
20
20
|
def run(node)
|
|
@@ -59,7 +59,7 @@ module RSpock
|
|
|
59
59
|
interaction_setups << setup
|
|
60
60
|
then_children << assertion unless assertion.equal?(child)
|
|
61
61
|
else
|
|
62
|
-
then_children <<
|
|
62
|
+
then_children << transform_statement_or_passthrough(child)
|
|
63
63
|
end
|
|
64
64
|
end
|
|
65
65
|
|
|
@@ -77,10 +77,19 @@ module RSpock
|
|
|
77
77
|
end
|
|
78
78
|
|
|
79
79
|
def transform_expect_block(expect_node)
|
|
80
|
-
new_children = expect_node.children.map { |child|
|
|
80
|
+
new_children = expect_node.children.map { |child| transform_statement_or_passthrough(child) }
|
|
81
81
|
expect_node.updated(nil, new_children)
|
|
82
82
|
end
|
|
83
83
|
|
|
84
|
+
def transform_statement_or_passthrough(child)
|
|
85
|
+
case child.type
|
|
86
|
+
when :rspock_binary_statement, :rspock_statement
|
|
87
|
+
@statement_transformation.run(child)
|
|
88
|
+
else
|
|
89
|
+
child
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
|
|
84
93
|
# --- Build final Ruby AST ---
|
|
85
94
|
|
|
86
95
|
def build_ruby_ast(method_call, method_args, body_node, where, hoisted_setups)
|
|
@@ -108,16 +117,25 @@ module RSpock
|
|
|
108
117
|
|
|
109
118
|
def build_test_body(body_node, hoisted_setups)
|
|
110
119
|
body_children = []
|
|
120
|
+
blocks = body_node.children
|
|
111
121
|
|
|
112
|
-
|
|
122
|
+
blocks.each_with_index do |block_node, i|
|
|
113
123
|
case block_node.type
|
|
114
124
|
when :rspock_given
|
|
115
125
|
body_children.concat(block_node.children)
|
|
116
126
|
when :rspock_when
|
|
117
127
|
body_children.concat(hoisted_setups)
|
|
118
|
-
|
|
128
|
+
raises_node = find_raises_in_next_then(blocks, i)
|
|
129
|
+
|
|
130
|
+
if raises_node
|
|
131
|
+
body_children << build_assert_raises(block_node, raises_node)
|
|
132
|
+
else
|
|
133
|
+
body_children.concat(block_node.children)
|
|
134
|
+
end
|
|
119
135
|
when :rspock_then, :rspock_expect
|
|
120
|
-
|
|
136
|
+
block_node.children.each do |child|
|
|
137
|
+
body_children << child unless child.type == :rspock_raises
|
|
138
|
+
end
|
|
121
139
|
when :rspock_cleanup
|
|
122
140
|
# handled below as ensure
|
|
123
141
|
end
|
|
@@ -134,6 +152,31 @@ module RSpock
|
|
|
134
152
|
MethodCallToLVarTransformation.new(:_test_index_, :_line_number_).run(ast)
|
|
135
153
|
end
|
|
136
154
|
|
|
155
|
+
# --- Raises condition helpers ---
|
|
156
|
+
|
|
157
|
+
def find_raises_in_next_then(blocks, current_index)
|
|
158
|
+
next_block = blocks[current_index + 1]
|
|
159
|
+
return nil unless next_block&.type == :rspock_then
|
|
160
|
+
|
|
161
|
+
next_block.children.find { |c| c.type == :rspock_raises }
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
def build_assert_raises(when_node, raises_node)
|
|
165
|
+
when_body = when_node.children.length == 1 ? when_node.children[0] : s(:begin, *when_node.children)
|
|
166
|
+
|
|
167
|
+
assert_raises_call = s(:block,
|
|
168
|
+
s(:send, nil, :assert_raises, raises_node.exception_class),
|
|
169
|
+
s(:args),
|
|
170
|
+
when_body
|
|
171
|
+
)
|
|
172
|
+
|
|
173
|
+
if raises_node.capture_name
|
|
174
|
+
s(:lvasgn, raises_node.capture_name, assert_raises_call)
|
|
175
|
+
else
|
|
176
|
+
assert_raises_call
|
|
177
|
+
end
|
|
178
|
+
end
|
|
179
|
+
|
|
137
180
|
# --- Where block helpers ---
|
|
138
181
|
|
|
139
182
|
def build_where_iterator(data_rows)
|
data/lib/rspock/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: rspock
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 2.
|
|
4
|
+
version: 2.5.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Jean-Philippe Duchesne
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-
|
|
11
|
+
date: 2026-03-01 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: bundler
|
|
@@ -207,7 +207,6 @@ files:
|
|
|
207
207
|
- lib/generators/templates/rspock_initializer.rb
|
|
208
208
|
- lib/minitest/rspock_plugin.rb
|
|
209
209
|
- lib/rspock.rb
|
|
210
|
-
- lib/rspock/ast/comparison_to_assertion_transformation.rb
|
|
211
210
|
- lib/rspock/ast/header_nodes_transformation.rb
|
|
212
211
|
- lib/rspock/ast/interaction_to_block_identity_assertion_transformation.rb
|
|
213
212
|
- lib/rspock/ast/interaction_to_mocha_mock_transformation.rb
|
|
@@ -218,10 +217,12 @@ files:
|
|
|
218
217
|
- lib/rspock/ast/parser/expect_block.rb
|
|
219
218
|
- lib/rspock/ast/parser/given_block.rb
|
|
220
219
|
- lib/rspock/ast/parser/interaction_parser.rb
|
|
220
|
+
- lib/rspock/ast/parser/statement_parser.rb
|
|
221
221
|
- lib/rspock/ast/parser/test_method_parser.rb
|
|
222
222
|
- lib/rspock/ast/parser/then_block.rb
|
|
223
223
|
- lib/rspock/ast/parser/when_block.rb
|
|
224
224
|
- lib/rspock/ast/parser/where_block.rb
|
|
225
|
+
- lib/rspock/ast/statement_to_assertion_transformation.rb
|
|
225
226
|
- lib/rspock/ast/test_method_def_transformation.rb
|
|
226
227
|
- lib/rspock/ast/test_method_dstr_transformation.rb
|
|
227
228
|
- lib/rspock/ast/test_method_transformation.rb
|
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
require 'ast_transform/abstract_transformation'
|
|
3
|
-
require 'rspock/ast/method_call_to_lvar_transformation'
|
|
4
|
-
|
|
5
|
-
module RSpock
|
|
6
|
-
module AST
|
|
7
|
-
class ComparisonToAssertionTransformation < ASTTransform::AbstractTransformation
|
|
8
|
-
def initialize(*ignored_method_call_symbols)
|
|
9
|
-
@method_call_transformation = RSpock::AST::MethodCallToLVarTransformation.new(*ignored_method_call_symbols)
|
|
10
|
-
end
|
|
11
|
-
|
|
12
|
-
def on_send(node)
|
|
13
|
-
if node.children.count == 3 && node.children[1] == :== && ignored_method_call_node?(node)
|
|
14
|
-
transform_to_assert_equal(node)
|
|
15
|
-
elsif node.children.count == 3 && node.children[1] == :!= && ignored_method_call_node?(node)
|
|
16
|
-
transform_to_refute_equal(node)
|
|
17
|
-
else
|
|
18
|
-
node.updated(nil, process_all(node))
|
|
19
|
-
end
|
|
20
|
-
end
|
|
21
|
-
|
|
22
|
-
private
|
|
23
|
-
|
|
24
|
-
def ignored_method_call_node?(node)
|
|
25
|
-
return false unless node.is_a?(::Parser::AST::Node)
|
|
26
|
-
|
|
27
|
-
!@method_call_transformation.method_call_node?(node.children[0]) &&
|
|
28
|
-
!@method_call_transformation.method_call_node?(node.children[2])
|
|
29
|
-
end
|
|
30
|
-
|
|
31
|
-
def transform_to_assert_equal(node)
|
|
32
|
-
node.updated(nil, [nil, :assert_equal, node.children[2], node.children[0]])
|
|
33
|
-
end
|
|
34
|
-
|
|
35
|
-
def transform_to_refute_equal(node)
|
|
36
|
-
node.updated(nil, [nil, :refute_equal, node.children[2], node.children[0]])
|
|
37
|
-
end
|
|
38
|
-
end
|
|
39
|
-
end
|
|
40
|
-
end
|