rspock 2.1.0 → 2.3.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/.claude/CLAUDE.md +2 -0
- data/.cursor/rules/base.mdc +35 -0
- data/.cursor/rules/best-practices.mdc +27 -0
- data/Gemfile.lock +11 -5
- data/README.md +90 -7
- data/lib/rspock/ast/comparison_to_assertion_transformation.rb +1 -1
- data/lib/rspock/ast/interaction_to_block_identity_assertion_transformation.rb +30 -0
- data/lib/rspock/ast/interaction_to_mocha_mock_transformation.rb +104 -0
- data/lib/rspock/ast/node.rb +97 -0
- data/lib/rspock/ast/parser/block.rb +88 -0
- data/lib/rspock/ast/parser/cleanup_block.rb +22 -0
- data/lib/rspock/ast/parser/expect_block.rb +26 -0
- data/lib/rspock/ast/parser/given_block.rb +22 -0
- data/lib/rspock/ast/parser/interaction_parser.rb +131 -0
- data/lib/rspock/ast/parser/test_method_parser.rb +103 -0
- data/lib/rspock/ast/parser/then_block.rb +29 -0
- data/lib/rspock/ast/parser/when_block.rb +22 -0
- data/lib/rspock/ast/parser/where_block.rb +94 -0
- data/lib/rspock/ast/test_method_transformation.rb +114 -115
- data/lib/rspock/ast/transformation.rb +16 -24
- data/lib/rspock/backtrace_filter.rb +1 -1
- data/lib/rspock/helpers/block_capture.rb +41 -0
- data/lib/rspock/version.rb +1 -1
- data/rspock.gemspec +1 -0
- metadata +32 -12
- data/lib/rspock/ast/block.rb +0 -95
- data/lib/rspock/ast/cleanup_block.rb +0 -16
- data/lib/rspock/ast/end_block.rb +0 -17
- data/lib/rspock/ast/expect_block.rb +0 -21
- data/lib/rspock/ast/given_block.rb +0 -16
- data/lib/rspock/ast/interaction_transformation.rb +0 -148
- data/lib/rspock/ast/start_block.rb +0 -28
- data/lib/rspock/ast/then_block.rb +0 -34
- data/lib/rspock/ast/when_block.rb +0 -16
- data/lib/rspock/ast/where_block.rb +0 -86
|
@@ -1,37 +1,31 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
require 'ast_transform/abstract_transformation'
|
|
3
|
-
require 'rspock/ast/
|
|
4
|
-
require 'rspock/ast/
|
|
5
|
-
require 'rspock/ast/
|
|
6
|
-
require 'rspock/ast/
|
|
7
|
-
require 'rspock/ast/
|
|
8
|
-
require 'rspock/ast/
|
|
9
|
-
require 'rspock/ast/where_block'
|
|
10
|
-
require 'rspock/ast/end_block'
|
|
3
|
+
require 'rspock/ast/parser/given_block'
|
|
4
|
+
require 'rspock/ast/parser/when_block'
|
|
5
|
+
require 'rspock/ast/parser/then_block'
|
|
6
|
+
require 'rspock/ast/parser/expect_block'
|
|
7
|
+
require 'rspock/ast/parser/cleanup_block'
|
|
8
|
+
require 'rspock/ast/parser/where_block'
|
|
11
9
|
require 'rspock/ast/test_method_transformation'
|
|
12
10
|
|
|
13
11
|
module RSpock
|
|
14
12
|
module AST
|
|
15
13
|
class Transformation < ASTTransform::AbstractTransformation
|
|
16
|
-
|
|
17
|
-
Given:
|
|
18
|
-
When:
|
|
19
|
-
Then:
|
|
20
|
-
Expect:
|
|
21
|
-
Cleanup:
|
|
22
|
-
Where:
|
|
14
|
+
DEFAULT_BLOCK_REGISTRY = {
|
|
15
|
+
Given: Parser::GivenBlock,
|
|
16
|
+
When: Parser::WhenBlock,
|
|
17
|
+
Then: Parser::ThenBlock,
|
|
18
|
+
Expect: Parser::ExpectBlock,
|
|
19
|
+
Cleanup: Parser::CleanupBlock,
|
|
20
|
+
Where: Parser::WhereBlock,
|
|
23
21
|
}.freeze
|
|
24
22
|
|
|
25
23
|
def initialize(
|
|
26
|
-
|
|
27
|
-
end_block_class: EndBlock,
|
|
28
|
-
source_map: DefaultSourceMap,
|
|
24
|
+
block_registry: DEFAULT_BLOCK_REGISTRY,
|
|
29
25
|
strict: true
|
|
30
26
|
)
|
|
31
27
|
super()
|
|
32
|
-
@
|
|
33
|
-
@source_map = source_map
|
|
34
|
-
@end_block_class = end_block_class
|
|
28
|
+
@block_registry = block_registry
|
|
35
29
|
@strict = strict
|
|
36
30
|
end
|
|
37
31
|
|
|
@@ -93,9 +87,7 @@ module RSpock
|
|
|
93
87
|
end
|
|
94
88
|
|
|
95
89
|
TestMethodTransformation.new(
|
|
96
|
-
@
|
|
97
|
-
@start_block_class,
|
|
98
|
-
@end_block_class,
|
|
90
|
+
@block_registry,
|
|
99
91
|
strict: @strict
|
|
100
92
|
).run(node)
|
|
101
93
|
end
|
|
@@ -33,7 +33,7 @@ module RSpock
|
|
|
33
33
|
return location unless source_map
|
|
34
34
|
|
|
35
35
|
line_number = source_map.line(lineno) || '?'
|
|
36
|
-
location.
|
|
36
|
+
location.sub("#{file_path}:#{lineno}", "#{source_map.source_file_path}:#{line_number}")
|
|
37
37
|
end
|
|
38
38
|
|
|
39
39
|
private
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RSpock
|
|
4
|
+
module Helpers
|
|
5
|
+
module BlockCapture
|
|
6
|
+
# Installs a block-capture wrapper on +obj+ for +method_name+.
|
|
7
|
+
# Must be called AFTER Mocha's expects/stubs so the wrapper sits
|
|
8
|
+
# in front of whatever Mocha installed.
|
|
9
|
+
#
|
|
10
|
+
# Returns a lambda that, when called, returns the captured block
|
|
11
|
+
# (or nil if no block was passed).
|
|
12
|
+
def self.capture(obj, method_name)
|
|
13
|
+
state = { captured: nil }
|
|
14
|
+
|
|
15
|
+
if obj.respond_to?(method_name, true)
|
|
16
|
+
# Real objects or objects where Mocha defined the method on
|
|
17
|
+
# the singleton class. Prepend a module so we intercept the
|
|
18
|
+
# call before Mocha's stub (prepend wins over define_singleton_method).
|
|
19
|
+
s = state
|
|
20
|
+
capture_mod = Module.new do
|
|
21
|
+
define_method(method_name) do |*args, **kwargs, &blk|
|
|
22
|
+
s[:captured] = blk
|
|
23
|
+
super(*args, **kwargs, &blk)
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
obj.singleton_class.prepend(capture_mod)
|
|
27
|
+
else
|
|
28
|
+
# Mock objects where the method goes through method_missing.
|
|
29
|
+
original_mm = obj.method(:method_missing)
|
|
30
|
+
s = state
|
|
31
|
+
obj.define_singleton_method(:method_missing) do |name, *args, **kwargs, &blk|
|
|
32
|
+
s[:captured] = blk if name == method_name
|
|
33
|
+
original_mm.call(name, *args, **kwargs, &blk)
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
-> { state[:captured] }
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
data/lib/rspock/version.rb
CHANGED
data/rspock.gemspec
CHANGED
|
@@ -26,6 +26,7 @@ Gem::Specification.new do |spec|
|
|
|
26
26
|
spec.add_development_dependency "minitest", "~> 5.14"
|
|
27
27
|
spec.add_development_dependency "minitest-reporters", "~> 1.4"
|
|
28
28
|
spec.add_development_dependency "pry", ">= 0.14"
|
|
29
|
+
spec.add_development_dependency "pry-byebug", "~> 3.9"
|
|
29
30
|
spec.add_development_dependency "rake", "~> 13.0"
|
|
30
31
|
spec.add_development_dependency "simplecov", "~> 0.22"
|
|
31
32
|
|
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.3.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-02-
|
|
11
|
+
date: 2026-02-27 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: bundler
|
|
@@ -66,6 +66,20 @@ dependencies:
|
|
|
66
66
|
- - ">="
|
|
67
67
|
- !ruby/object:Gem::Version
|
|
68
68
|
version: '0.14'
|
|
69
|
+
- !ruby/object:Gem::Dependency
|
|
70
|
+
name: pry-byebug
|
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
|
72
|
+
requirements:
|
|
73
|
+
- - "~>"
|
|
74
|
+
- !ruby/object:Gem::Version
|
|
75
|
+
version: '3.9'
|
|
76
|
+
type: :development
|
|
77
|
+
prerelease: false
|
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
79
|
+
requirements:
|
|
80
|
+
- - "~>"
|
|
81
|
+
- !ruby/object:Gem::Version
|
|
82
|
+
version: '3.9'
|
|
69
83
|
- !ruby/object:Gem::Dependency
|
|
70
84
|
name: rake
|
|
71
85
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -171,6 +185,9 @@ executables: []
|
|
|
171
185
|
extensions: []
|
|
172
186
|
extra_rdoc_files: []
|
|
173
187
|
files:
|
|
188
|
+
- ".claude/CLAUDE.md"
|
|
189
|
+
- ".cursor/rules/base.mdc"
|
|
190
|
+
- ".cursor/rules/best-practices.mdc"
|
|
174
191
|
- ".github/workflows/ci.yml"
|
|
175
192
|
- ".github/workflows/release.yml"
|
|
176
193
|
- ".gitignore"
|
|
@@ -190,25 +207,28 @@ files:
|
|
|
190
207
|
- lib/generators/templates/rspock_initializer.rb
|
|
191
208
|
- lib/minitest/rspock_plugin.rb
|
|
192
209
|
- lib/rspock.rb
|
|
193
|
-
- lib/rspock/ast/block.rb
|
|
194
|
-
- lib/rspock/ast/cleanup_block.rb
|
|
195
210
|
- lib/rspock/ast/comparison_to_assertion_transformation.rb
|
|
196
|
-
- lib/rspock/ast/end_block.rb
|
|
197
|
-
- lib/rspock/ast/expect_block.rb
|
|
198
|
-
- lib/rspock/ast/given_block.rb
|
|
199
211
|
- lib/rspock/ast/header_nodes_transformation.rb
|
|
200
|
-
- lib/rspock/ast/
|
|
212
|
+
- lib/rspock/ast/interaction_to_block_identity_assertion_transformation.rb
|
|
213
|
+
- lib/rspock/ast/interaction_to_mocha_mock_transformation.rb
|
|
201
214
|
- lib/rspock/ast/method_call_to_lvar_transformation.rb
|
|
202
|
-
- lib/rspock/ast/
|
|
215
|
+
- lib/rspock/ast/node.rb
|
|
216
|
+
- lib/rspock/ast/parser/block.rb
|
|
217
|
+
- lib/rspock/ast/parser/cleanup_block.rb
|
|
218
|
+
- lib/rspock/ast/parser/expect_block.rb
|
|
219
|
+
- lib/rspock/ast/parser/given_block.rb
|
|
220
|
+
- lib/rspock/ast/parser/interaction_parser.rb
|
|
221
|
+
- lib/rspock/ast/parser/test_method_parser.rb
|
|
222
|
+
- lib/rspock/ast/parser/then_block.rb
|
|
223
|
+
- lib/rspock/ast/parser/when_block.rb
|
|
224
|
+
- lib/rspock/ast/parser/where_block.rb
|
|
203
225
|
- lib/rspock/ast/test_method_def_transformation.rb
|
|
204
226
|
- lib/rspock/ast/test_method_dstr_transformation.rb
|
|
205
227
|
- lib/rspock/ast/test_method_transformation.rb
|
|
206
|
-
- lib/rspock/ast/then_block.rb
|
|
207
228
|
- lib/rspock/ast/transformation.rb
|
|
208
|
-
- lib/rspock/ast/when_block.rb
|
|
209
|
-
- lib/rspock/ast/where_block.rb
|
|
210
229
|
- lib/rspock/backtrace_filter.rb
|
|
211
230
|
- lib/rspock/declarative.rb
|
|
231
|
+
- lib/rspock/helpers/block_capture.rb
|
|
212
232
|
- lib/rspock/minitest/backtrace_filter.rb
|
|
213
233
|
- lib/rspock/railtie.rb
|
|
214
234
|
- lib/rspock/tasks/rspock.rake
|
data/lib/rspock/ast/block.rb
DELETED
|
@@ -1,95 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
module RSpock
|
|
3
|
-
module AST
|
|
4
|
-
class BlockError < StandardError; end
|
|
5
|
-
|
|
6
|
-
class Block
|
|
7
|
-
# Constructs a new Block.
|
|
8
|
-
#
|
|
9
|
-
# @param type [Symbol] The Block type.
|
|
10
|
-
# @param node [Parser::AST::Node] The node associated to this Block.
|
|
11
|
-
def initialize(type, node)
|
|
12
|
-
@type = type
|
|
13
|
-
@node = node
|
|
14
|
-
@children = []
|
|
15
|
-
@node_container = true
|
|
16
|
-
end
|
|
17
|
-
|
|
18
|
-
attr_reader :type, :node
|
|
19
|
-
|
|
20
|
-
# Adds the given +child_node+ to this Block.
|
|
21
|
-
#
|
|
22
|
-
# @param child_node [Parser::AST::Node] The node to be added.
|
|
23
|
-
#
|
|
24
|
-
# @raise [BlockError] if this Block cannot contain other nodes.
|
|
25
|
-
def <<(child_node)
|
|
26
|
-
raise BlockError, succession_error_msg unless node_container?
|
|
27
|
-
|
|
28
|
-
@children << child_node
|
|
29
|
-
end
|
|
30
|
-
|
|
31
|
-
# Adds the given +child_node+ to the beginning of this Block.
|
|
32
|
-
#
|
|
33
|
-
# @param child_node [Parser::AST::Node] The node to be added.
|
|
34
|
-
#
|
|
35
|
-
# @raise [BlockError] if this Block cannot contain other nodes.
|
|
36
|
-
def unshift(child_node)
|
|
37
|
-
raise BlockError, succession_error_msg unless node_container?
|
|
38
|
-
|
|
39
|
-
@children.unshift(child_node)
|
|
40
|
-
end
|
|
41
|
-
|
|
42
|
-
# Checks whether this Block can contain other nodes.
|
|
43
|
-
#
|
|
44
|
-
# @return [Boolean] True if this Block can contain other nodes, false otherwise.
|
|
45
|
-
def node_container?
|
|
46
|
-
@node_container
|
|
47
|
-
end
|
|
48
|
-
|
|
49
|
-
# Sets whether this Block can contain other nodes.
|
|
50
|
-
#
|
|
51
|
-
# @param value [Boolean] True if this Block can contain other nodes, false otherwise.
|
|
52
|
-
def node_container=(value)
|
|
53
|
-
@node_container = value
|
|
54
|
-
end
|
|
55
|
-
|
|
56
|
-
# Retrieves the Parser::Source::Range for this Block.
|
|
57
|
-
#
|
|
58
|
-
# @return [Parser::Source::Range] The range.
|
|
59
|
-
def range
|
|
60
|
-
node&.loc&.expression || "?"
|
|
61
|
-
end
|
|
62
|
-
|
|
63
|
-
# Retrieves the valid successors for this Block.
|
|
64
|
-
# Note: Defaults to [:End].
|
|
65
|
-
#
|
|
66
|
-
# @return [Array<Symbol>] This Block's successors.
|
|
67
|
-
def successors
|
|
68
|
-
@successors ||= [:End].freeze
|
|
69
|
-
end
|
|
70
|
-
|
|
71
|
-
# Retrieves the duped array of children AST nodes for this Block.
|
|
72
|
-
#
|
|
73
|
-
# @return [Array<Parser::AST::Node>] The children nodes.
|
|
74
|
-
def children
|
|
75
|
-
@children.dup
|
|
76
|
-
end
|
|
77
|
-
|
|
78
|
-
# Checks whether or not the given +block+ is a valid successor for this Block.
|
|
79
|
-
#
|
|
80
|
-
# @param block [Block] The candidate successor.
|
|
81
|
-
#
|
|
82
|
-
# @return [Boolean] True if the given block is a valid successor, false otherwise.
|
|
83
|
-
def valid_successor?(block)
|
|
84
|
-
successors.include?(block.type)
|
|
85
|
-
end
|
|
86
|
-
|
|
87
|
-
# Retrieves the error message for succession errors.
|
|
88
|
-
#
|
|
89
|
-
# @return [String] The error message.
|
|
90
|
-
def succession_error_msg
|
|
91
|
-
"Block #{type} @ #{range} must be followed by one of these Blocks: #{successors}"
|
|
92
|
-
end
|
|
93
|
-
end
|
|
94
|
-
end
|
|
95
|
-
end
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
require 'rspock/ast/block'
|
|
3
|
-
|
|
4
|
-
module RSpock
|
|
5
|
-
module AST
|
|
6
|
-
class CleanupBlock < Block
|
|
7
|
-
def initialize(node)
|
|
8
|
-
super(:Cleanup, node)
|
|
9
|
-
end
|
|
10
|
-
|
|
11
|
-
def successors
|
|
12
|
-
@successors ||= [:Where, :End].freeze
|
|
13
|
-
end
|
|
14
|
-
end
|
|
15
|
-
end
|
|
16
|
-
end
|
data/lib/rspock/ast/end_block.rb
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
require 'rspock/ast/block'
|
|
3
|
-
|
|
4
|
-
module RSpock
|
|
5
|
-
module AST
|
|
6
|
-
class EndBlock < Block
|
|
7
|
-
def initialize
|
|
8
|
-
super(:End, nil)
|
|
9
|
-
@node_container = false
|
|
10
|
-
end
|
|
11
|
-
|
|
12
|
-
def successors
|
|
13
|
-
@successors ||= [].freeze
|
|
14
|
-
end
|
|
15
|
-
end
|
|
16
|
-
end
|
|
17
|
-
end
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
require 'rspock/ast/block'
|
|
3
|
-
require 'rspock/ast/comparison_to_assertion_transformation'
|
|
4
|
-
|
|
5
|
-
module RSpock
|
|
6
|
-
module AST
|
|
7
|
-
class ExpectBlock < Block
|
|
8
|
-
def initialize(node)
|
|
9
|
-
super(:Expect, node)
|
|
10
|
-
end
|
|
11
|
-
|
|
12
|
-
def successors
|
|
13
|
-
@successors ||= [:Cleanup, :Where, :End].freeze
|
|
14
|
-
end
|
|
15
|
-
|
|
16
|
-
def children
|
|
17
|
-
super.map { |child| ComparisonToAssertionTransformation.new(:_test_index_, :_line_number_).run(child) }
|
|
18
|
-
end
|
|
19
|
-
end
|
|
20
|
-
end
|
|
21
|
-
end
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
require 'rspock/ast/block'
|
|
3
|
-
|
|
4
|
-
module RSpock
|
|
5
|
-
module AST
|
|
6
|
-
class GivenBlock < Block
|
|
7
|
-
def initialize(node)
|
|
8
|
-
super(:Given, node)
|
|
9
|
-
end
|
|
10
|
-
|
|
11
|
-
def successors
|
|
12
|
-
@successors ||= [:When, :Expect].freeze
|
|
13
|
-
end
|
|
14
|
-
end
|
|
15
|
-
end
|
|
16
|
-
end
|
|
@@ -1,148 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
require 'ast_transform/abstract_transformation'
|
|
3
|
-
|
|
4
|
-
module RSpock
|
|
5
|
-
module AST
|
|
6
|
-
class InteractionTransformation < ASTTransform::AbstractTransformation
|
|
7
|
-
class InteractionError < RuntimeError; end
|
|
8
|
-
|
|
9
|
-
def run(node)
|
|
10
|
-
return node unless interaction_node?(node)
|
|
11
|
-
|
|
12
|
-
parse_node(node)
|
|
13
|
-
transform_node
|
|
14
|
-
end
|
|
15
|
-
|
|
16
|
-
def interaction_node?(node)
|
|
17
|
-
return false if node.nil?
|
|
18
|
-
|
|
19
|
-
node.type == :send && node.children[1] == :*
|
|
20
|
-
end
|
|
21
|
-
|
|
22
|
-
private
|
|
23
|
-
|
|
24
|
-
ALLOWED_NODES = [:send, :lvar, :int]
|
|
25
|
-
private_constant(:ALLOWED_NODES)
|
|
26
|
-
|
|
27
|
-
def transform_node
|
|
28
|
-
result = chain_call(@receiver_node, :expects, s(:sym, @message))
|
|
29
|
-
result = chain_call(result, :with, *@arg_nodes) unless @arg_nodes.empty?
|
|
30
|
-
|
|
31
|
-
if any_matcher_node?(@times_node)
|
|
32
|
-
result = chain_call(result, :at_least, s(:int, 0))
|
|
33
|
-
elsif ALLOWED_NODES.include?(@times_node.type)
|
|
34
|
-
result = chain_call(result, :times, @times_node)
|
|
35
|
-
elsif @times_node.type == :begin && @times_node.children[0]&.type == :irange
|
|
36
|
-
min_node, max_node = @times_node.children[0].children
|
|
37
|
-
|
|
38
|
-
result = transform_irange_node(result, min_node, max_node)
|
|
39
|
-
elsif @times_node.type == :begin && @times_node.children[0]&.type == :erange
|
|
40
|
-
min_node, max_node = @times_node.children[0].children
|
|
41
|
-
max_node = chain_call(max_node, :-, s(:int, 1))
|
|
42
|
-
|
|
43
|
-
result = transform_erange_node(result, min_node, max_node)
|
|
44
|
-
else
|
|
45
|
-
raise ArgumentError, "Unrecognized times constraint in interaction: #{@times_node&.loc&.expression || "?"}"
|
|
46
|
-
end
|
|
47
|
-
|
|
48
|
-
result
|
|
49
|
-
end
|
|
50
|
-
|
|
51
|
-
def chain_call(receiver_node, method_name, *arg_nodes)
|
|
52
|
-
s(:send, receiver_node, method_name, *arg_nodes)
|
|
53
|
-
end
|
|
54
|
-
|
|
55
|
-
def transform_irange_node(receiver_node, min_node, max_node)
|
|
56
|
-
result = receiver_node
|
|
57
|
-
|
|
58
|
-
if any_matcher_node?(min_node) && any_matcher_node?(max_node)
|
|
59
|
-
result = chain_call(result, :at_least, s(:int, 0))
|
|
60
|
-
elsif !any_matcher_node?(min_node) && any_matcher_node?(max_node)
|
|
61
|
-
result = chain_call(result, :at_least, min_node)
|
|
62
|
-
elsif any_matcher_node?(min_node) && !any_matcher_node?(max_node)
|
|
63
|
-
result = chain_call(result, :at_least, s(:int, 0))
|
|
64
|
-
result = chain_call(result, :at_most, max_node)
|
|
65
|
-
elsif !any_matcher_node?(min_node) && !any_matcher_node?(max_node)
|
|
66
|
-
result = chain_call(result, :at_least, min_node)
|
|
67
|
-
result = chain_call(result, :at_most, max_node)
|
|
68
|
-
end
|
|
69
|
-
|
|
70
|
-
result
|
|
71
|
-
end
|
|
72
|
-
|
|
73
|
-
def transform_erange_node(receiver_node, min_node, max_node)
|
|
74
|
-
result = receiver_node
|
|
75
|
-
|
|
76
|
-
if any_matcher_node?(min_node) && any_matcher_node?(max_node.children[0])
|
|
77
|
-
result = chain_call(result, :at_least, s(:int, 0))
|
|
78
|
-
elsif !any_matcher_node?(min_node) && any_matcher_node?(max_node.children[0])
|
|
79
|
-
result = chain_call(result, :at_least, min_node)
|
|
80
|
-
elsif any_matcher_node?(min_node) && !any_matcher_node?(max_node.children[0])
|
|
81
|
-
result = chain_call(result, :at_least, s(:int, 0))
|
|
82
|
-
result = chain_call(result, :at_most, max_node)
|
|
83
|
-
elsif !any_matcher_node?(min_node) && !any_matcher_node?(max_node.children[0])
|
|
84
|
-
result = chain_call(result, :at_least, min_node)
|
|
85
|
-
result = chain_call(result, :at_most, max_node)
|
|
86
|
-
end
|
|
87
|
-
|
|
88
|
-
result
|
|
89
|
-
end
|
|
90
|
-
|
|
91
|
-
def any_matcher_node?(node)
|
|
92
|
-
node.type == :send && node.children[0].nil? && node.children[1] == :_
|
|
93
|
-
end
|
|
94
|
-
|
|
95
|
-
def parse_node(node)
|
|
96
|
-
parse_lhs(node.children[0])
|
|
97
|
-
parse_rhs(node.children[2])
|
|
98
|
-
end
|
|
99
|
-
|
|
100
|
-
def parse_lhs(node)
|
|
101
|
-
@times_node = node
|
|
102
|
-
|
|
103
|
-
case @times_node.type
|
|
104
|
-
when *ALLOWED_NODES
|
|
105
|
-
# OK
|
|
106
|
-
when :begin
|
|
107
|
-
if node.children.count > 1
|
|
108
|
-
raise_lhs_error(node, msg_prefix: "Left-hand side of ", msg_suffix: " or a range in parentheses")
|
|
109
|
-
end
|
|
110
|
-
case node.children[0].type
|
|
111
|
-
when :irange, :erange
|
|
112
|
-
unless ALLOWED_NODES.include?(node.children[0].children[0].type)
|
|
113
|
-
raise_lhs_error(node.children[0].children[0], msg_prefix: "Minimum range of ")
|
|
114
|
-
end
|
|
115
|
-
unless ALLOWED_NODES.include?(node.children[0].children[1].type)
|
|
116
|
-
raise_lhs_error(node.children[0].children[1], msg_prefix: "Maximum range of ")
|
|
117
|
-
end
|
|
118
|
-
else
|
|
119
|
-
raise_lhs_error(node, msg_prefix: "Left-hand side of ", msg_suffix: " or a range in parentheses")
|
|
120
|
-
end
|
|
121
|
-
else
|
|
122
|
-
raise_lhs_error(node, msg_prefix: "Left-hand side of ", msg_suffix: " or a range in parentheses")
|
|
123
|
-
end
|
|
124
|
-
end
|
|
125
|
-
|
|
126
|
-
def parse_rhs(node)
|
|
127
|
-
if node.type != :send
|
|
128
|
-
raise InteractionError, "Right-hand side of Interaction @ #{range(node)} must be a :send node."
|
|
129
|
-
end
|
|
130
|
-
|
|
131
|
-
@receiver_node, @message, *@arg_nodes = node.children
|
|
132
|
-
|
|
133
|
-
if @receiver_node.nil?
|
|
134
|
-
raise InteractionError, "Right-hand side of Interaction @ #{range(node)} must have a receiver."
|
|
135
|
-
end
|
|
136
|
-
end
|
|
137
|
-
|
|
138
|
-
def range(node)
|
|
139
|
-
node&.loc&.expression || "?"
|
|
140
|
-
end
|
|
141
|
-
|
|
142
|
-
def raise_lhs_error(node, msg_prefix: "", msg_suffix: "")
|
|
143
|
-
raise InteractionError, "#{msg_prefix}Interaction @ #{range(node)} must be one of "\
|
|
144
|
-
"#{ALLOWED_NODES}#{msg_suffix}."
|
|
145
|
-
end
|
|
146
|
-
end
|
|
147
|
-
end
|
|
148
|
-
end
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
require 'rspock/ast/block'
|
|
3
|
-
|
|
4
|
-
module RSpock
|
|
5
|
-
module AST
|
|
6
|
-
class StartBlock < Block
|
|
7
|
-
def initialize(node)
|
|
8
|
-
super(:Start, node)
|
|
9
|
-
@node_container = false
|
|
10
|
-
end
|
|
11
|
-
|
|
12
|
-
def successors
|
|
13
|
-
if @children.empty?
|
|
14
|
-
SUCCESSORS_WITHOUT_CHILDREN
|
|
15
|
-
else
|
|
16
|
-
SUCCESSORS_WITH_CHILDREN
|
|
17
|
-
end
|
|
18
|
-
end
|
|
19
|
-
|
|
20
|
-
def succession_error_msg
|
|
21
|
-
"Test method @ #{range} must start with one of these Blocks: #{successors}"
|
|
22
|
-
end
|
|
23
|
-
|
|
24
|
-
SUCCESSORS_WITHOUT_CHILDREN = [:Given, :When, :Expect].freeze
|
|
25
|
-
SUCCESSORS_WITH_CHILDREN = [:End].freeze
|
|
26
|
-
end
|
|
27
|
-
end
|
|
28
|
-
end
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
require 'rspock/ast/block'
|
|
3
|
-
require 'rspock/ast/comparison_to_assertion_transformation'
|
|
4
|
-
require 'rspock/ast/interaction_transformation'
|
|
5
|
-
|
|
6
|
-
module RSpock
|
|
7
|
-
module AST
|
|
8
|
-
class ThenBlock < Block
|
|
9
|
-
def initialize(node)
|
|
10
|
-
super(:Then, node)
|
|
11
|
-
end
|
|
12
|
-
|
|
13
|
-
def successors
|
|
14
|
-
@successors ||= [:Cleanup, :Where, :End].freeze
|
|
15
|
-
end
|
|
16
|
-
|
|
17
|
-
def children
|
|
18
|
-
super.reject { |child| interaction_transformation.interaction_node?(child) }
|
|
19
|
-
.map { |child| ComparisonToAssertionTransformation.new(:_test_index_, :_line_number_).run(child) }
|
|
20
|
-
end
|
|
21
|
-
|
|
22
|
-
def interactions
|
|
23
|
-
@children.select { |child| interaction_transformation.interaction_node?(child) }
|
|
24
|
-
.map { |child| interaction_transformation.run(child) }
|
|
25
|
-
end
|
|
26
|
-
|
|
27
|
-
private
|
|
28
|
-
|
|
29
|
-
def interaction_transformation
|
|
30
|
-
@interaction_transformation ||= RSpock::AST::InteractionTransformation.new
|
|
31
|
-
end
|
|
32
|
-
end
|
|
33
|
-
end
|
|
34
|
-
end
|