brine-dsl 0.11.0 → 0.12.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/.ruby-version +1 -1
- data/Gemfile.lock +38 -19
- data/brine-dsl.gemspec +8 -8
- data/lib/brine.rb +12 -25
- data/lib/brine/assertions.rb +123 -0
- data/lib/brine/cleaning_up.rb +28 -18
- data/lib/brine/client_building.rb +22 -22
- data/lib/brine/coercing.rb +15 -15
- data/lib/brine/hooks.rb +1 -1
- data/lib/brine/mustache_expanding.rb +54 -23
- data/lib/brine/performing.rb +51 -25
- data/lib/brine/requesting.rb +97 -26
- data/lib/brine/selecting.rb +239 -35
- data/lib/brine/test_steps.rb +16 -19
- data/lib/brine/transforming.rb +30 -24
- data/lib/brine/type_checking.rb +25 -14
- metadata +7 -14
- data/lib/brine/rest_steps.rb +0 -119
- data/lib/brine/step_definitions/assertions.rb +0 -50
- data/lib/brine/step_definitions/assignment.rb +0 -42
- data/lib/brine/step_definitions/cleanup.rb +0 -4
- data/lib/brine/step_definitions/perform.rb +0 -16
- data/lib/brine/step_definitions/request_construction.rb +0 -28
- data/lib/brine/step_definitions/selection.rb +0 -49
- data/lib/brine/util.rb +0 -46
data/lib/brine/selecting.rb
CHANGED
@@ -1,14 +1,13 @@
|
|
1
1
|
##
|
2
2
|
# @file selecting.rb
|
3
|
-
#
|
3
|
+
# Support selection of one or more values to be used by Brine (normally for assertion).
|
4
4
|
##
|
5
|
-
|
6
5
|
require 'brine/transforming.rb'
|
7
6
|
|
8
7
|
module Brine
|
9
8
|
|
10
9
|
##
|
11
|
-
#
|
10
|
+
# Provide assorted means to select particular values out of objects/graphs.
|
12
11
|
##
|
13
12
|
module Selecting
|
14
13
|
|
@@ -17,7 +16,7 @@ module Brine
|
|
17
16
|
require 'jsonpath'
|
18
17
|
|
19
18
|
##
|
20
|
-
#
|
19
|
+
# Allow selection of one or more values.
|
21
20
|
# This Selector will test whether the targeted value itself satisfies the assertion.
|
22
21
|
#
|
23
22
|
# RSpec is used within this implementation to perform assertions.
|
@@ -29,15 +28,15 @@ module Brine
|
|
29
28
|
include RSpec::Matchers
|
30
29
|
|
31
30
|
##
|
32
|
-
# [Coercer]
|
31
|
+
# [Coercer] Expose the Coercer that may modify values prior to performing assertions.
|
33
32
|
##
|
34
33
|
attr_accessor :coercer
|
35
34
|
|
36
35
|
##
|
37
36
|
# Construct a selector to perform assertions against a provided target.
|
38
37
|
#
|
39
|
-
# @param [Object]
|
40
|
-
# @param [Boolean]
|
38
|
+
# @param target [Object] Provide the value against which assertions will be performed.
|
39
|
+
# @param negated [Boolean] Specify whether the assertions from this selector should be negated.
|
41
40
|
# This is deprecated and should instead be passed to #assert_that.
|
42
41
|
##
|
43
42
|
def initialize(target, negated=false)
|
@@ -49,10 +48,11 @@ module Brine
|
|
49
48
|
# Optionally perform some modification to the RSpec matcher prior to assertion.
|
50
49
|
#
|
51
50
|
# This is designed to allow subclassess to be able to modify the way
|
52
|
-
# in which matchers are applied against the values.
|
51
|
+
# in which matchers are applied against the values.
|
52
|
+
# The default implementation is a passthrough.
|
53
53
|
#
|
54
|
-
# @param [RSpec::Matcher]
|
55
|
-
# @return [RSpec::Matcher]
|
54
|
+
# @param matcher [RSpec::Matcher] Relay the matcher originally passed to #assert_that.
|
55
|
+
# @return [RSpec::Matcher] Return the Matcher to use while performing the assertion.
|
56
56
|
##
|
57
57
|
def filter_matcher(matcher)
|
58
58
|
matcher
|
@@ -63,16 +63,17 @@ module Brine
|
|
63
63
|
#
|
64
64
|
# The values will be coerced prior to evaluation.
|
65
65
|
#
|
66
|
-
# @param [Object]
|
66
|
+
# @param value [Object] Specify the value/parameter against which the target will be compared.
|
67
67
|
# In cases where no parameter is needed for comparison, this may be nil.
|
68
|
-
# @param [
|
69
|
-
# @param [
|
68
|
+
# @param binding [Object] Provide the binding environment which will be used to expand any templates
|
69
|
+
# @param negated [Boolean] Specify whether the assertion should be expected to _fail_, if false it should pass.
|
70
|
+
# @param [Block] Define the logic which will be passed a coerced copy of `value` and which should return an
|
70
71
|
# RSpec matcher which will be evaluated against the coerced target value.
|
71
72
|
##
|
72
|
-
def assert_that(value, negated=nil)
|
73
|
+
def assert_that(value, binding, negated=nil)
|
73
74
|
# shim while moving negation to assertions.
|
74
75
|
negated = @negated if negated.nil?
|
75
|
-
target, value = coercer.coerce(expand(@target), expand(value))
|
76
|
+
target, value = coercer.coerce(expand(@target, binding), expand(value, binding))
|
76
77
|
message = negated ? :to_not : :to
|
77
78
|
matcher = filter_matcher(yield(value))
|
78
79
|
expect(target).send(message, matcher)
|
@@ -81,7 +82,7 @@ module Brine
|
|
81
82
|
end
|
82
83
|
|
83
84
|
##
|
84
|
-
#
|
85
|
+
# Define a Selector which will test whether any of the children of the targeted value satisfy the assertion.
|
85
86
|
##
|
86
87
|
class AnySelector < Selector
|
87
88
|
def filter_matcher(matcher)
|
@@ -90,7 +91,7 @@ module Brine
|
|
90
91
|
end
|
91
92
|
|
92
93
|
##
|
93
|
-
#
|
94
|
+
# Define a Selector which will test whether all of the children of the targeted value satisfy the assertion.
|
94
95
|
##
|
95
96
|
class AllSelector < Selector
|
96
97
|
def filter_matcher(matcher)
|
@@ -101,8 +102,8 @@ module Brine
|
|
101
102
|
##
|
102
103
|
# Activate a Selector for the provided target.
|
103
104
|
#
|
104
|
-
# @param [Object]
|
105
|
-
# @param [Boolean]
|
105
|
+
# @param target [Object] Provide the value which the Selector should target.
|
106
|
+
# @param negated [Boolean] Specify whether the assertions should be expected to fail (DEPRECATED).
|
106
107
|
##
|
107
108
|
def select(target, negated=nil)
|
108
109
|
use_selector(Selector.new(target, negated))
|
@@ -111,8 +112,8 @@ module Brine
|
|
111
112
|
##
|
112
113
|
# Activate a Selector for any of the children of the provided target.
|
113
114
|
#
|
114
|
-
# @param [Object]
|
115
|
-
# @param [Boolean]
|
115
|
+
# @param target [Object] Provide the value which the Selector should target.
|
116
|
+
# @param negated [Boolean] Specify whether the assertions should be expected to fail (DEPRECATED).
|
116
117
|
##
|
117
118
|
def select_any(target, negated=nil)
|
118
119
|
use_selector(AnySelector.new(target, negated))
|
@@ -121,8 +122,8 @@ module Brine
|
|
121
122
|
##
|
122
123
|
# Activate a Selector for all of the children of the provided target.
|
123
124
|
#
|
124
|
-
# @param [Object]
|
125
|
-
# @param [Boolean]
|
125
|
+
# @param target [Object] Provide the value which the Selector should target.
|
126
|
+
# @param negated [Boolean] Specify whether the assertions should be expected to fail (DEPRECATED).
|
126
127
|
##
|
127
128
|
def select_all(target, negated=nil)
|
128
129
|
use_selector(AllSelector.new(target, negated))
|
@@ -131,7 +132,7 @@ module Brine
|
|
131
132
|
##
|
132
133
|
# Configure selector and make it the active Selector.
|
133
134
|
#
|
134
|
-
# @param [Selector]
|
135
|
+
# @param selector [Selector] Provide the selector which will be activated.
|
135
136
|
##
|
136
137
|
def use_selector(selector)
|
137
138
|
selector.coercer = coercer
|
@@ -139,32 +140,235 @@ module Brine
|
|
139
140
|
end
|
140
141
|
|
141
142
|
##
|
142
|
-
#
|
143
|
+
# Return the currently active Selector.
|
143
144
|
#
|
144
|
-
# @return
|
145
|
+
# @return [Selector] Return the Selector as set by #use_selector.
|
145
146
|
##
|
146
147
|
def selector
|
147
148
|
@selector
|
148
149
|
end
|
149
150
|
|
151
|
+
##
|
152
|
+
# Capture a path for which the value will be retrieved from a root.
|
153
|
+
##
|
154
|
+
class Traversal
|
155
|
+
|
156
|
+
##
|
157
|
+
# Return a traversal for the provided path.
|
158
|
+
#
|
159
|
+
# @param path [String] Define the JSONPath for the location of the value(s) to retrieve.
|
160
|
+
# @param is_plural [Boolean] Specify whether a collection should be returned for possibly multiple values,
|
161
|
+
# false if only a single value should be expected/returned.
|
162
|
+
##
|
163
|
+
def initialize(path, is_plural)
|
164
|
+
@path = path
|
165
|
+
@message = is_plural ? :on : :first
|
166
|
+
end
|
167
|
+
|
168
|
+
##
|
169
|
+
# Return values out of the provided root based on the traversal definition.
|
170
|
+
#
|
171
|
+
# @param root [String] Provide the JSON structure from which the value will be retrieved.
|
172
|
+
# @return [Object] Return the value or values (if is_plural) at the defined path or nil if none found.
|
173
|
+
##
|
174
|
+
def visit(root)
|
175
|
+
!@path ? root : JsonPath.new("$.#{@path}").send(@message, root)
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
##
|
180
|
+
# Return a Traversal based on the provided arguments.
|
181
|
+
#
|
182
|
+
# This primarily exists as the exported interface to retrieve a Traversal instance.
|
183
|
+
#
|
184
|
+
# @param path [String] Define the JSONPath to which the traversal should descend.
|
185
|
+
# @param is_plural [Object] Specify whether to produce a traversal for a collection of all matches:
|
186
|
+
# nil will target only a single match, any non-nil value will target all matches.
|
187
|
+
# @return [Traversal] Return a Traversal configured based on the provided arguments.
|
188
|
+
##
|
189
|
+
def traversal(path, is_plural)
|
190
|
+
Traversal.new(path, !is_plural.nil?)
|
191
|
+
end
|
150
192
|
end
|
151
193
|
|
152
194
|
# Mix the Selecting module functionality into the main Brine module.
|
153
195
|
include Selecting
|
196
|
+
|
154
197
|
end
|
155
198
|
|
199
|
+
##
|
200
|
+
# Retrieve the requested attribute from the current response.
|
156
201
|
#
|
157
|
-
#
|
202
|
+
# @param attribute [String] Specify the attribute to return from the response.
|
203
|
+
# @return [Object] Return the value of the attribute for the current response.
|
204
|
+
##
|
205
|
+
def response_attribute(attribute)
|
206
|
+
response.send(attribute.to_sym)
|
207
|
+
end
|
208
|
+
|
209
|
+
|
210
|
+
##
|
211
|
+
# Extract assertion step text.
|
212
|
+
##
|
213
|
+
ParameterType(
|
214
|
+
name: 'assertion',
|
215
|
+
regexp: /.*[^:]/,
|
216
|
+
transformer: -> (input) { input }
|
217
|
+
)
|
218
|
+
|
219
|
+
##
|
220
|
+
# Extract the text for a standard HTTP method.
|
221
|
+
##
|
222
|
+
ParameterType(
|
223
|
+
name: 'http_method',
|
224
|
+
regexp: /(DELETE|GET|HEAD|OPTIONS|PATCH|POST|PUT)/,
|
225
|
+
transformer: -> (input) { input }
|
226
|
+
)
|
227
|
+
|
228
|
+
##
|
229
|
+
# Indicate whether not is present.
|
230
|
+
##
|
231
|
+
ParameterType(
|
232
|
+
name: 'maybe_not',
|
233
|
+
regexp: /( not)?/,
|
234
|
+
transformer: -> (input=nil) { !input.nil? }
|
235
|
+
)
|
236
|
+
|
237
|
+
##
|
238
|
+
# Extract the text for a supported response attribute.
|
239
|
+
##
|
240
|
+
ParameterType(
|
241
|
+
name: 'response_attribute',
|
242
|
+
regexp: /(status|headers|body)/,
|
243
|
+
transformer: -> (input) { input }
|
244
|
+
)
|
245
|
+
|
246
|
+
# Messing with the parameters is due to cucumber expressions
|
247
|
+
# not working properly...that should be chased down.
|
248
|
+
|
249
|
+
##
|
250
|
+
# Produce a Traversal for one or all elements at the specified path.
|
251
|
+
##
|
252
|
+
ParameterType(
|
253
|
+
name: 'traversal',
|
254
|
+
regexp: /(?: child(ren)? `([^`]*)`)?/,
|
255
|
+
transformer: -> (plural_or_path=nil, path=nil) {
|
256
|
+
if path.nil?
|
257
|
+
path=plural_or_path
|
258
|
+
plural_or_path=nil
|
259
|
+
end
|
260
|
+
traversal(path, plural_or_path)
|
261
|
+
}
|
262
|
+
)
|
263
|
+
|
264
|
+
##
|
265
|
+
# Select a directly provided value.
|
158
266
|
#
|
159
|
-
|
160
|
-
|
161
|
-
|
267
|
+
# @param value [Object] Provide the value for which the assertion will be evaluated.
|
268
|
+
# @param negated [Object] Indicate whether the assertion should be exected to fail.
|
269
|
+
# @param assertion [String] Provide the assertion step to evaluate.
|
270
|
+
##
|
271
|
+
Then('the value of {grave_param} is{maybe_not} {assertion}') do |value, negated, assertion|
|
272
|
+
select(value, negated)
|
162
273
|
# Stringify in case the assertion clause is a template.
|
163
|
-
step "it is #{assertion
|
274
|
+
step "it is #{assertion}"
|
275
|
+
end
|
276
|
+
|
277
|
+
##
|
278
|
+
# Evaluate an assertion against a response attribute.
|
279
|
+
#
|
280
|
+
# @param attribute [String] Indicate from which response attribute the value will be selected.
|
281
|
+
# @param traversal [Traversal] Provide a Traversal to retrieve the value from the attribute.
|
282
|
+
# @param negated [Boolean] Specify whether the assertion should be expected to fail (DEPRECATED).
|
283
|
+
# @param assertion [Object] Provide the assertion step to evaluate.
|
284
|
+
##
|
285
|
+
Then('the value of the response {response_attribute}{traversal} is{maybe_not} {assertion}') do
|
286
|
+
|attribute, traversal, negated, assertion|
|
287
|
+
perform do
|
288
|
+
select(traversal.visit(response_attribute(attribute)), negated)
|
289
|
+
step "it is #{assertion}"
|
290
|
+
end
|
291
|
+
end
|
292
|
+
|
293
|
+
|
294
|
+
##
|
295
|
+
# Evaluate an assertion against a response attribute and provide a docstring parameter.
|
296
|
+
#
|
297
|
+
# @param attribute [String] Indicate from which response attribute the value will be selected.
|
298
|
+
# @param traversal [Traversal] Provide a Traversal to retrieve the value from the attribute.
|
299
|
+
# @param negated [Boolean] Specify whether the assertion should be expected to fail (DEPRECATED).
|
300
|
+
# @param assertion [Object] Provide the assertion step to evaluate.
|
301
|
+
# @param multi [String] Pass a docstring parameter which will be forwarded to the assertion.
|
302
|
+
##
|
303
|
+
Then('the value of the response {response_attribute}{traversal} is{maybe_not} {assertion}:') do
|
304
|
+
|attribute, traversal, negated, assertion, multi|
|
305
|
+
perform do
|
306
|
+
select(traversal.visit(response_attribute(attribute)), negated)
|
307
|
+
step "it is #{assertion}:", multi
|
308
|
+
end
|
309
|
+
end
|
310
|
+
|
311
|
+
##
|
312
|
+
# Evaluate an assertion against at least one matched value from the response attribute.
|
313
|
+
#
|
314
|
+
# @param attribute [String] Indicate from which response attribute the values will be selected.
|
315
|
+
# @param traversal [Traversal] Provide a Traversal to retrieve values from the attribute.
|
316
|
+
# @param negated [Boolean] Specify whether the assertion should be expected to fail (DEPRECATED).
|
317
|
+
# @param assertion [Object] Provide the assertion step to evaluate.
|
318
|
+
##
|
319
|
+
Then('the value of the response {response_attribute}{traversal} does{maybe_not} have any element that is {assertion}') do
|
320
|
+
|attribute, traversal, negated, assertion|
|
321
|
+
perform do
|
322
|
+
select_any(traversal.visit(response_attribute(attribute)), negated)
|
323
|
+
step "it is #{assertion}"
|
324
|
+
end
|
325
|
+
end
|
326
|
+
|
327
|
+
##
|
328
|
+
# Evaluate an assertion against at least one matched value from the response attribute and provide a docstring parameter.
|
329
|
+
#
|
330
|
+
# @param attribute [String] Indicate from which response attribute the values will be selected.
|
331
|
+
# @param traversal [Traversal] Provide a Traversal to retrieve values from the attribute.
|
332
|
+
# @param negated [Boolean] Specify whether the assertion should be expected to fail (DEPRECATED).
|
333
|
+
# @param assertion [Object] Provide the assertion step to evaluate.
|
334
|
+
# @param multi [String] Pass a docstring parameter which will be forwarded to the assertion.
|
335
|
+
##
|
336
|
+
Then('the value of the response {response_attribute}{traversal} does{maybe_not} have any element that is {assertion}:') do
|
337
|
+
|attribute, traversal, negated, assertion, multi|
|
338
|
+
perform do
|
339
|
+
select_any(traversal.visit(response_attribute(attribute)), negated)
|
340
|
+
step "it is #{assertion}", multi
|
341
|
+
end
|
342
|
+
end
|
343
|
+
|
344
|
+
##
|
345
|
+
# Evaluate an assertion against ALL matched value from the response attribute.
|
346
|
+
#
|
347
|
+
# @param attribute [String] Indicate from which response attribute the values will be selected.
|
348
|
+
# @param traversal [Traversal] Provide a Traversal to retrieve values from the attribute.
|
349
|
+
# @param assertion [Object] Provide the assertion step to evaluate.
|
350
|
+
##
|
351
|
+
Then('the value of the response {response_attribute}{traversal} has elements which are all {assertion}') do
|
352
|
+
|attribute, traversal, assertion|
|
353
|
+
perform do
|
354
|
+
select_all(traversal.visit(response_attribute(attribute)), false)
|
355
|
+
step "it is #{assertion}"
|
356
|
+
end
|
164
357
|
end
|
165
358
|
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
359
|
+
##
|
360
|
+
# Evaluate an assertion against ALL matched value from the response attribute and provide a docstring argument.
|
361
|
+
#
|
362
|
+
# @param attribute [String] Indicate from which response attribute the values will be selected.
|
363
|
+
# @param traversal [Traversal] Provide a Traversal to retrieve values from the attribute.
|
364
|
+
# @param assertion [Object] Provide the assertion step to evaluate.
|
365
|
+
# @param multi [String] Pass a docstring parameter which will be forwarded to the assertion.
|
366
|
+
##
|
367
|
+
Then('the value of the response {response_attribute}{traversal} has elements which are all {assertion}:') do
|
368
|
+
|attribute, traversal, assertion, multi|
|
369
|
+
perform do
|
370
|
+
select_all(traversal.visit(response_attribute(attribute)), false)
|
371
|
+
step "it is #{assertion}", multi.to_json
|
372
|
+
end
|
170
373
|
end
|
374
|
+
|
data/lib/brine/test_steps.rb
CHANGED
@@ -3,7 +3,8 @@ require 'rspec'
|
|
3
3
|
# Steps used to test this library
|
4
4
|
# Not loaded by default (except in the tests)
|
5
5
|
#
|
6
|
-
|
6
|
+
|
7
|
+
require 'brine/selecting'
|
7
8
|
|
8
9
|
ENV['BRINE_DURATION_SECONDS_short'] = '3'
|
9
10
|
ENV['BRINE_DURATION_SECONDS_long'] = '6'
|
@@ -105,60 +106,56 @@ Before do
|
|
105
106
|
end
|
106
107
|
end
|
107
108
|
|
108
|
-
Given(
|
109
|
+
Given('expected response status of {grave_param}') do |status|
|
109
110
|
stub.response.status = status
|
110
111
|
end
|
111
112
|
|
112
|
-
Given(
|
113
|
+
Given('expected response status sequence of {grave_param}') do |seq|
|
113
114
|
@stub = ResponseStatusSequenceStubBuilder.new(stub, seq)
|
114
115
|
end
|
115
116
|
|
116
|
-
Given(
|
117
|
+
Given('expected request body:') do |body|
|
117
118
|
stub.request.body = body
|
118
119
|
end
|
119
120
|
|
120
|
-
Given(
|
121
|
-
stub.request.headers = headers
|
121
|
+
Given('expected request headers:') do |headers|
|
122
|
+
stub.request.headers = transformed_parameter(headers)
|
122
123
|
end
|
123
124
|
|
124
|
-
Given(
|
125
|
+
Given('expected {http_method} sent to {grave_param}') do |method, path|
|
125
126
|
stub.request.method = method
|
126
127
|
stub.request.path = path
|
127
128
|
build_stub
|
128
129
|
end
|
129
130
|
|
130
|
-
When(
|
131
|
+
When('the response body is assigned:') do |input|
|
131
132
|
@response ||= StubResponse.new
|
132
|
-
@response.body = input
|
133
|
+
@response.body = transformed_parameter(input)
|
133
134
|
end
|
134
135
|
|
135
|
-
When(
|
136
|
+
When('the response headers is assigned {grave_param}') do |input|
|
136
137
|
@response ||= StubResponse.new
|
137
138
|
@response.headers = input
|
138
139
|
end
|
139
140
|
|
140
|
-
When(
|
141
|
+
When('the response body is assigned {grave_param}') do |input|
|
141
142
|
@response ||= StubResponse.new
|
142
143
|
@response.body = input
|
143
144
|
end
|
144
145
|
|
145
|
-
When(
|
146
|
-
replaced_with('When', 'the response body is assigned:', '1.0.0', input.to_json)
|
147
|
-
end
|
148
|
-
|
149
|
-
When /^the response status is assigned `([^`]*)`$/ do |status|
|
146
|
+
When('the response status is assigned {grave_param}') do |status|
|
150
147
|
@response ||= StubResponse.new
|
151
148
|
@response.status = status.to_i # this coercion isn't needed but is a guarantee
|
152
149
|
end
|
153
150
|
|
154
|
-
When
|
151
|
+
When('the response is delayed {int} seconds') do |seconds|
|
155
152
|
@response = DelayedStubResponse.new(seconds)
|
156
153
|
end
|
157
154
|
|
158
|
-
Then(
|
155
|
+
Then('the response body as JSON is:') do |text|
|
159
156
|
expect(response.body.to_json).to eq text
|
160
157
|
end
|
161
158
|
|
162
|
-
Then(
|
159
|
+
Then('expected calls are verified') do
|
163
160
|
$stubs.verify_stubbed_calls
|
164
161
|
end
|