brine-dsl 0.9.0 → 0.10.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile.lock +1 -1
- data/Rakefile +1 -1
- data/brine-dsl.gemspec +1 -2
- data/lib/brine/cleaning_up.rb +120 -0
- data/lib/brine/client_building.rb +123 -0
- data/lib/brine/coercing.rb +91 -0
- data/lib/brine/hooks.rb +8 -1
- data/lib/brine/mustache_expanding.rb +51 -0
- data/lib/brine/performing.rb +159 -0
- data/lib/brine/requesting.rb +171 -0
- data/lib/brine/rest_steps.rb +1 -1
- data/lib/brine/selecting.rb +166 -0
- data/lib/brine/step_definitions/assertions.rb +15 -15
- data/lib/brine/step_definitions/assignment.rb +5 -4
- data/lib/brine/step_definitions/cleanup.rb +2 -2
- data/lib/brine/step_definitions/perform.rb +16 -0
- data/lib/brine/step_definitions/request_construction.rb +12 -8
- data/lib/brine/step_definitions/selection.rb +24 -12
- data/lib/brine/test_steps.rb +20 -0
- data/lib/brine/transforms.rb +12 -4
- data/lib/brine/type_checking.rb +88 -0
- data/lib/brine/util.rb +1 -0
- data/lib/brine.rb +13 -20
- metadata +12 -9
- data/lib/brine/cleaner_upper.rb +0 -101
- data/lib/brine/coercer.rb +0 -68
- data/lib/brine/mustache_binder.rb +0 -25
- data/lib/brine/requester.rb +0 -161
- data/lib/brine/selector.rb +0 -66
- data/lib/brine/type_checks.rb +0 -36
@@ -0,0 +1,171 @@
|
|
1
|
+
##
|
2
|
+
# @file requesting.rb
|
3
|
+
# Request construction and response storage.
|
4
|
+
##
|
5
|
+
|
6
|
+
module Brine
|
7
|
+
|
8
|
+
##
|
9
|
+
# Module in charge of constructing requests and saving responses.
|
10
|
+
##
|
11
|
+
module Requesting
|
12
|
+
require 'faraday_middleware'
|
13
|
+
require 'brine/client_building'
|
14
|
+
|
15
|
+
##
|
16
|
+
# The root url to which Brine will send requests.
|
17
|
+
#
|
18
|
+
# This will normally be the value of ENV['BRINE_ROOT_URL'],
|
19
|
+
# and that value should be directly usable after older
|
20
|
+
# ENV['ROOT_URL'] is end-of-lifed (at which point this can be removed).
|
21
|
+
#
|
22
|
+
# @brine_root_url is used if set to allow setting this value more programmatically.
|
23
|
+
#
|
24
|
+
# @return [String] The root URL to use or nil if none is provided.
|
25
|
+
##
|
26
|
+
def brine_root_url
|
27
|
+
if @brine_root_url
|
28
|
+
@brine_root_url
|
29
|
+
elsif ENV['BRINE_ROOT_URL']
|
30
|
+
ENV['BRINE_ROOT_URL']
|
31
|
+
elsif ENV['ROOT_URL']
|
32
|
+
deprecation_message('1.0', 'ROOT_URL is deprecated, replace with BRINE_ROOT_URL') if ENV['ROOT_URL']
|
33
|
+
ENV['ROOT_URL']
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
##
|
38
|
+
# Normalize an HTTP method for the HTTP client library (to a lowercased symbol).
|
39
|
+
#
|
40
|
+
# @param [String] method A text representation of the HTTP method.
|
41
|
+
# @return [Symbol] A representation of `method` usable by the HTTP client library.
|
42
|
+
##
|
43
|
+
def parse_method(method)
|
44
|
+
method.downcase.to_sym
|
45
|
+
end
|
46
|
+
|
47
|
+
##
|
48
|
+
# Set the client which will be used to send HTTP requests.
|
49
|
+
#
|
50
|
+
# This will generally be a connection as created by the ClientBuilding module.
|
51
|
+
#
|
52
|
+
# @param [Faraday::Connection, #run_request] client The client which will be used to issue HTTP requests.
|
53
|
+
##
|
54
|
+
def set_client(client)
|
55
|
+
@client = client
|
56
|
+
end
|
57
|
+
|
58
|
+
##
|
59
|
+
# The currently active client which will be used to issue HTTP calls.
|
60
|
+
#
|
61
|
+
# This will be initialized as neded on first access
|
62
|
+
# to a default client constructed by the ClientBuilding module.
|
63
|
+
#
|
64
|
+
# @return [Faraday::Connection, #run_request] The currently active client object.
|
65
|
+
##
|
66
|
+
def client
|
67
|
+
@client ||= client_for_host(brine_root_url)
|
68
|
+
end
|
69
|
+
|
70
|
+
##
|
71
|
+
# Clear any previously built request state and set defaults.
|
72
|
+
#
|
73
|
+
# This should be called upon request completion or when constructing a new
|
74
|
+
# request so no extant state inadvertently pollutes the new construction.
|
75
|
+
##
|
76
|
+
def reset_request
|
77
|
+
@params = @headers = @body = nil
|
78
|
+
end
|
79
|
+
|
80
|
+
##
|
81
|
+
# Store the provided body in the request being built.
|
82
|
+
#
|
83
|
+
# This will override any previous body value.
|
84
|
+
#
|
85
|
+
# @param [Object] The new data to be placed in the request body.
|
86
|
+
##
|
87
|
+
def set_request_body(obj)
|
88
|
+
@body = obj
|
89
|
+
end
|
90
|
+
|
91
|
+
##
|
92
|
+
# Send a `method` request to `url` including any current builder options and store response.
|
93
|
+
#
|
94
|
+
# The response will be available as the `#response` property.
|
95
|
+
#
|
96
|
+
# For requests such as simple GETs that only require a url this method may
|
97
|
+
# be self-contained; for more complex requests the values collected through
|
98
|
+
# request building are likely required. Any data present from the builder
|
99
|
+
# methods will always be inclued in the request and therefore such data should
|
100
|
+
# be cleared using `#reset_request` if it is not desired.
|
101
|
+
#
|
102
|
+
# @param [Symbol] method The client friendly representation of the HTTP method for the request
|
103
|
+
# (@see #parse_method).
|
104
|
+
# @param [String] url The url to which the request will be sent.
|
105
|
+
##
|
106
|
+
def send_request(method, url)
|
107
|
+
@response = client.run_request(method, url, @body, headers) do |req|
|
108
|
+
req.params = request_params
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
##
|
113
|
+
# The response for the last sent request.
|
114
|
+
#
|
115
|
+
# @return [Faraday::Response] The most recent response.
|
116
|
+
##
|
117
|
+
def response
|
118
|
+
@response
|
119
|
+
end
|
120
|
+
|
121
|
+
##
|
122
|
+
# The headers for the request currently being built.
|
123
|
+
#
|
124
|
+
# Will be initialized as needed on first access,
|
125
|
+
# with a default specifying JSON content-type.
|
126
|
+
#
|
127
|
+
# @return [Hash] The headers to use for the constructed request.
|
128
|
+
##
|
129
|
+
def headers
|
130
|
+
@headers ||= {content_type: 'application/json'}
|
131
|
+
end
|
132
|
+
|
133
|
+
##
|
134
|
+
# Set the specified header to the provided value.
|
135
|
+
#
|
136
|
+
# @param [String] k The name of the header whose value will be set.
|
137
|
+
# @param [Object] v The value to set for the specified header.
|
138
|
+
# This should normally be a String, but some other types may work.
|
139
|
+
##
|
140
|
+
def set_header(k, v)
|
141
|
+
headers[k] = v
|
142
|
+
end
|
143
|
+
|
144
|
+
##
|
145
|
+
# The query parameters which will be attached to the constructeed request.
|
146
|
+
#
|
147
|
+
# Will be initialized to an empty hash as needed upon first access.
|
148
|
+
#
|
149
|
+
# @return [Hash] The query parameters to use for request construction.
|
150
|
+
##
|
151
|
+
def request_params
|
152
|
+
@request_params ||= {}
|
153
|
+
end
|
154
|
+
|
155
|
+
##
|
156
|
+
# Assign the provided value to the specified request query parameter.
|
157
|
+
#
|
158
|
+
# @param [String] k The name of the query parameter whose value is being assigned.
|
159
|
+
# @param [Object] v The value to assign the query parameter (normally a String).
|
160
|
+
##
|
161
|
+
def set_request_param(k, v)
|
162
|
+
request_params[k] = v
|
163
|
+
end
|
164
|
+
|
165
|
+
end
|
166
|
+
|
167
|
+
##
|
168
|
+
# Mix the Requesting module functionality into the main Brine module.
|
169
|
+
##
|
170
|
+
include Requesting
|
171
|
+
end
|
data/lib/brine/rest_steps.rb
CHANGED
@@ -0,0 +1,166 @@
|
|
1
|
+
##
|
2
|
+
# @file selecting.rb
|
3
|
+
# Selection of one or more values to be used by Brine (normally for assertion).
|
4
|
+
##
|
5
|
+
|
6
|
+
module Brine
|
7
|
+
|
8
|
+
##
|
9
|
+
# A module providing assorted means to select particular values out of objects/graphs.
|
10
|
+
##
|
11
|
+
module Selecting
|
12
|
+
|
13
|
+
require 'brine/coercing'
|
14
|
+
require 'rspec/expectations'
|
15
|
+
require 'jsonpath'
|
16
|
+
|
17
|
+
##
|
18
|
+
# An object responsible for selecting one or more values.
|
19
|
+
# This Selector will test whether the targeted value itself satisfies the assertion.
|
20
|
+
#
|
21
|
+
# RSpec is used within this implementation to perform assertions.
|
22
|
+
# The Selector ultimately perform this assertion by accepting an RSpec matcher
|
23
|
+
# which it applied against the targeted value.
|
24
|
+
##
|
25
|
+
class Selector
|
26
|
+
include RSpec::Matchers
|
27
|
+
|
28
|
+
##
|
29
|
+
# [Coercer] The Coercer that may modify values prior to performing assertions.
|
30
|
+
##
|
31
|
+
attr_accessor :coercer
|
32
|
+
|
33
|
+
##
|
34
|
+
# Construct a selector to perform assertions against a provided target.
|
35
|
+
#
|
36
|
+
# @param [Object] taret The value against which assertions will be performed.
|
37
|
+
# @param [Boolean] negated Whether the assertions from this selector should be negated.
|
38
|
+
# This is deprecated and should instead be passed to #assert_that.
|
39
|
+
##
|
40
|
+
def initialize(target, negated=false)
|
41
|
+
@target = target
|
42
|
+
@negated = negated
|
43
|
+
end
|
44
|
+
|
45
|
+
##
|
46
|
+
# Optionally perform some modification to the RSpec matcher prior to assertion.
|
47
|
+
#
|
48
|
+
# This is designed to allow subclassess to be able to modify the way
|
49
|
+
# in which matchers are applied against the values. This method is a pass-through in this class.
|
50
|
+
#
|
51
|
+
# @param [RSpec::Matcher] matcher The matcher originally passed to #assert_that.
|
52
|
+
# @return [RSpec::Matcher] The Matcher to be used while performing the assertion.
|
53
|
+
##
|
54
|
+
def filter_matcher(matcher)
|
55
|
+
matcher
|
56
|
+
end
|
57
|
+
|
58
|
+
##
|
59
|
+
# Perform the provided assertion against the instance target.
|
60
|
+
#
|
61
|
+
# The values will be coerced prior to evaluation.
|
62
|
+
#
|
63
|
+
# @param [Object] value The value/parameter against which the target will be compared.
|
64
|
+
# In cases where no parameter is needed for comparison, this may be nil.
|
65
|
+
# @param [Boolean] negated If true the assertion should be expected to _fail_, otherwise it should pass.
|
66
|
+
# @param [Block] A block which will be passed a coerced copy of `value` and which should return an
|
67
|
+
# RSpec matcher which will be evaluated against the coerced target value.
|
68
|
+
##
|
69
|
+
def assert_that(value, negated=nil)
|
70
|
+
# shim while moving negation to assertions.
|
71
|
+
negated = @negated if negated.nil?
|
72
|
+
target, value = coercer.coerce(@target, value)
|
73
|
+
message = negated ? :to_not : :to
|
74
|
+
matcher = filter_matcher(yield(value))
|
75
|
+
expect(target).send(message, matcher)
|
76
|
+
end
|
77
|
+
|
78
|
+
end
|
79
|
+
|
80
|
+
##
|
81
|
+
# A Selector which will test whether any of the children of the targeted value satisfy the assertion.
|
82
|
+
##
|
83
|
+
class AnySelector < Selector
|
84
|
+
def filter_matcher(matcher)
|
85
|
+
include(matcher)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
##
|
90
|
+
# A Selector which will test whether all of the children of the targeted value satisfy the assertion.
|
91
|
+
##
|
92
|
+
class AllSelector < Selector
|
93
|
+
def filter_matcher(matcher)
|
94
|
+
all(matcher)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
##
|
99
|
+
# Activate a Selector for the provided target.
|
100
|
+
#
|
101
|
+
# @param [Object] target The value which the Selector should target.
|
102
|
+
# @param [Boolean] negated DEPRECATED If true the assertions should be expected to fail.
|
103
|
+
##
|
104
|
+
def select(target, negated=nil)
|
105
|
+
use_selector(Selector.new(target, negated))
|
106
|
+
end
|
107
|
+
|
108
|
+
##
|
109
|
+
# Activate a Selector for any of the children of the provided target.
|
110
|
+
#
|
111
|
+
# @param [Object] target The value which the Selector should target.
|
112
|
+
# @param [Boolean] negated DEPRECATED If true the assertions should be expected to fail.
|
113
|
+
##
|
114
|
+
def select_any(target, negated=nil)
|
115
|
+
use_selector(AnySelector.new(target, negated))
|
116
|
+
end
|
117
|
+
|
118
|
+
##
|
119
|
+
# Activate a Selector for all of the children of the provided target.
|
120
|
+
#
|
121
|
+
# @param [Object] target The value which the Selector should target.
|
122
|
+
# @param [Boolean] negated DEPRECATED If true the assertions should be expected to fail.
|
123
|
+
##
|
124
|
+
def select_all(target, negated=nil)
|
125
|
+
use_selector(AllSelector.new(target, negated))
|
126
|
+
end
|
127
|
+
|
128
|
+
##
|
129
|
+
# Configure selector and make it the active Selector.
|
130
|
+
#
|
131
|
+
# @param [Selector] selector The selector which will be activated.
|
132
|
+
##
|
133
|
+
def use_selector(selector)
|
134
|
+
selector.coercer = coercer
|
135
|
+
@selector = selector
|
136
|
+
end
|
137
|
+
|
138
|
+
##
|
139
|
+
# The currently active Selector.
|
140
|
+
#
|
141
|
+
# @return The Selector as set by #use_selector
|
142
|
+
##
|
143
|
+
def selector
|
144
|
+
@selector
|
145
|
+
end
|
146
|
+
|
147
|
+
end
|
148
|
+
|
149
|
+
# Mix the Selecting module functionality into the main Brine module.
|
150
|
+
include Selecting
|
151
|
+
end
|
152
|
+
|
153
|
+
#
|
154
|
+
# Steps
|
155
|
+
#
|
156
|
+
RESPONSE_ATTRIBUTES='(status|headers|body)'
|
157
|
+
Then(/^the value of `([^`]*)` is( not)? (.*)$/) do |value, negated, assertion|
|
158
|
+
select(value, (!negated.nil?))
|
159
|
+
step "it is #{assertion}"
|
160
|
+
end
|
161
|
+
|
162
|
+
def dig_from_response(attribute, path=nil, plural=false)
|
163
|
+
root = response.send(attribute.to_sym)
|
164
|
+
return root if !path
|
165
|
+
JsonPath.new("$.#{path}").send(plural ? :on : :first, root)
|
166
|
+
end
|
@@ -1,50 +1,50 @@
|
|
1
1
|
# assertions.rb - General assertions to be used with a Selector
|
2
2
|
|
3
3
|
Then(/^it is equal to `([^`]*)`$/) do |value|
|
4
|
-
selector.assert_that(value) {|v| eq v}
|
4
|
+
perform { selector.assert_that(value) {|v| eq v} }
|
5
5
|
end
|
6
6
|
Then(/^it is equal to:$/) do |value|
|
7
|
-
selector.assert_that(value) {|v| eq v}
|
7
|
+
perform { selector.assert_that(value) {|v| eq v} }
|
8
8
|
end
|
9
9
|
Then(/^it is matching `([^`]*)`$/) do |value|
|
10
|
-
selector.assert_that(value) {|v| match v}
|
10
|
+
perform { selector.assert_that(value) {|v| match v} }
|
11
11
|
end
|
12
12
|
Then (/^it is matching:$/) do |value|
|
13
|
-
selector.assert_that(value) {|v| match v}
|
13
|
+
perform { selector.assert_that(value) {|v| match v} }
|
14
14
|
end
|
15
15
|
Then(/^it is greater than `([^`]*)`$/) do |value|
|
16
|
-
selector.assert_that(value) {|v| be > v}
|
16
|
+
perform { selector.assert_that(value) {|v| be > v} }
|
17
17
|
end
|
18
18
|
Then(/^it is greater than or equal to `([^`]*)`$/) do |value|
|
19
|
-
selector.assert_that(value) {|v| be >= v}
|
19
|
+
perform { selector.assert_that(value) {|v| be >= v} }
|
20
20
|
end
|
21
21
|
Then(/^it is less than `([^`]*)`$/) do |value|
|
22
|
-
selector.assert_that(value) {|v| be < v}
|
22
|
+
perform { selector.assert_that(value) {|v| be < v} }
|
23
23
|
end
|
24
24
|
Then(/^it is less than or equal to `([^`]*)`$/) do |value|
|
25
|
-
selector.assert_that(value) {|v| be <= v}
|
25
|
+
perform { selector.assert_that(value) {|v| be <= v} }
|
26
26
|
end
|
27
27
|
|
28
28
|
# Be a little smarter than default.
|
29
29
|
Then(/^it is empty$/) do
|
30
|
-
selector.assert_that(nil) do
|
30
|
+
perform { selector.assert_that(nil) do
|
31
31
|
satisfy{|i| i.nil? || (i.respond_to?(:empty?) && i.empty?) }
|
32
|
-
end
|
32
|
+
end }
|
33
33
|
end
|
34
34
|
|
35
35
|
Then(/^it is including `([^`]*)`$/) do |value|
|
36
|
-
selector.assert_that(value) {|v| include v }
|
36
|
+
perform { selector.assert_that(value) {|v| include v } }
|
37
37
|
end
|
38
38
|
Then(/^it is including:$/) do |value|
|
39
|
-
selector.assert_that(value) {|v| include v }
|
39
|
+
perform { selector.assert_that(value) {|v| include v } }
|
40
40
|
end
|
41
41
|
|
42
42
|
Then(/^it is a valid `([^`]*)`$/) do |type|
|
43
|
-
selector.assert_that(type) {|t| type_check_for(t) }
|
43
|
+
perform { selector.assert_that(type) {|t| type_check_for(t) } }
|
44
44
|
end
|
45
45
|
|
46
46
|
Then(/^it is of length `([^`]*)`$/) do |length|
|
47
|
-
selector.assert_that(length) do |l|
|
47
|
+
perform { selector.assert_that(length) do |l|
|
48
48
|
satisfy{|i| i.respond_to?(:length) && i.length == l}
|
49
|
-
end
|
49
|
+
end }
|
50
50
|
end
|
@@ -1,6 +1,7 @@
|
|
1
1
|
##
|
2
2
|
# @file assignment.rb
|
3
3
|
# Assignment related steps.
|
4
|
+
##
|
4
5
|
|
5
6
|
##
|
6
7
|
# Assign the provided parameter.
|
@@ -8,7 +9,7 @@
|
|
8
9
|
# @param name - The identifier to which the value will be bound.
|
9
10
|
# @param value - The value to bind to the identifier.
|
10
11
|
When(/^`([^`]*)` is assigned `([^`]*)`$/) do |name, value|
|
11
|
-
bind(name, value)
|
12
|
+
perform { bind(name, value) }
|
12
13
|
end
|
13
14
|
|
14
15
|
##
|
@@ -16,7 +17,7 @@ end
|
|
16
17
|
#
|
17
18
|
# @param name - The identifier to which a random string will be bound.
|
18
19
|
When(/^`([^`]*)` is assigned a random string$/) do |name|
|
19
|
-
bind(name, SecureRandom.uuid)
|
20
|
+
perform { bind(name, SecureRandom.uuid) }
|
20
21
|
end
|
21
22
|
|
22
23
|
##
|
@@ -24,7 +25,7 @@ end
|
|
24
25
|
#
|
25
26
|
# @param name - The identifier to which the current timestamp will be bound.
|
26
27
|
When(/^`([^`]*)` is assigned a timestamp$/) do |name|
|
27
|
-
bind(name, DateTime.now)
|
28
|
+
perform { bind(name, DateTime.now) }
|
28
29
|
end
|
29
30
|
|
30
31
|
##
|
@@ -37,5 +38,5 @@ end
|
|
37
38
|
# whether to extract a single match or a collection of all matching.
|
38
39
|
When(/^`([^`]*)` is assigned the response #{RESPONSE_ATTRIBUTES}(?: child(ren)? `([^`]*)`)?$/) do
|
39
40
|
|name, attribute, plural, path|
|
40
|
-
bind(name, dig_from_response(attribute, path, !plural.nil?))
|
41
|
+
perform { bind(name, dig_from_response(attribute, path, !plural.nil?)) }
|
41
42
|
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
##
|
2
|
+
# @file perform.rb
|
3
|
+
# Steps related to performing actions
|
4
|
+
##
|
5
|
+
|
6
|
+
When /^actions are defined such that$/ do
|
7
|
+
collect_actions
|
8
|
+
end
|
9
|
+
|
10
|
+
Then /^the actions are( not)? successful within a `([^`]*)` period$/ do |negated, period|
|
11
|
+
method = negated ? :to : :to_not
|
12
|
+
expect { poll_for(retrieve_duration(period)) {
|
13
|
+
performer.evaluate
|
14
|
+
} }.send(method, raise_error)
|
15
|
+
reset_performer
|
16
|
+
end
|
@@ -1,24 +1,28 @@
|
|
1
1
|
# request_construction.rb - Build and send requests
|
2
2
|
|
3
3
|
When(/^the request body is assigned:$/) do |input|
|
4
|
-
set_request_body(input)
|
4
|
+
perform { set_request_body(input) }
|
5
5
|
end
|
6
6
|
|
7
7
|
When(/^the request query parameter `([^`]*)` is assigned `([^`]*)`$/) do |param, value|
|
8
|
-
|
8
|
+
perform { set_request_param(param, value) }
|
9
9
|
end
|
10
10
|
|
11
11
|
When(/^the request header `([^`]*)` is assigned `([^`]*)`$/) do |header, value|
|
12
|
-
|
12
|
+
perform { set_header(header, value) }
|
13
13
|
end
|
14
14
|
|
15
15
|
When(/^the request credentials are set for basic auth user `([^`]*)` and password `([^`]*)`$/) do |user, password|
|
16
|
-
|
17
|
-
|
16
|
+
perform do
|
17
|
+
base64_combined = Base64.strict_encode64("#{user}:#{password}")
|
18
|
+
set_header('Authorization', "Basic #{base64_combined}")
|
19
|
+
end
|
18
20
|
end
|
19
21
|
|
20
22
|
When(/^an? (GET|POST|PATCH|PUT|DELETE|HEAD|OPTIONS) is sent to `([^`]*)`$/) do |method, url|
|
21
|
-
|
22
|
-
|
23
|
-
|
23
|
+
perform do
|
24
|
+
send_request(parse_method(method), URI.escape(url))
|
25
|
+
bind('response', response)
|
26
|
+
reset_request
|
27
|
+
end
|
24
28
|
end
|
@@ -1,37 +1,49 @@
|
|
1
1
|
#RESPONSE_ATTRIBUTES='(status|headers|body)'
|
2
2
|
Then(/^the value of the response #{RESPONSE_ATTRIBUTES}(?: child(ren)? `([^`]*)`)? is( not)? (.*)(?<!:)$/) do
|
3
3
|
|attribute, plural, path, negated, assertion|
|
4
|
-
|
5
|
-
|
4
|
+
perform do
|
5
|
+
select(dig_from_response(attribute, path, !plural.nil?), (!negated.nil?))
|
6
|
+
step "it is #{assertion}"
|
7
|
+
end
|
6
8
|
end
|
7
9
|
|
8
10
|
Then(/^the value of the response #{RESPONSE_ATTRIBUTES}(?: child(ren)? `([^`]*)`)? is( not)? (.*)(?<=:)$/) do
|
9
11
|
|attribute, plural, path, negated, assertion, multi|
|
10
|
-
|
11
|
-
|
12
|
+
perform do
|
13
|
+
select(dig_from_response(attribute, path, !plural.nil?), (!negated.nil?))
|
14
|
+
step "it is #{assertion}", multi.to_json
|
15
|
+
end
|
12
16
|
end
|
13
17
|
|
14
18
|
Then(/^the value of the response #{RESPONSE_ATTRIBUTES}(?: child(ren)? `([^`]*)`)? does( not)? have any element that is (.*)(?<!:)$/) do
|
15
19
|
|attribute, plural, path, negated, assertion|
|
16
|
-
|
17
|
-
|
20
|
+
perform do
|
21
|
+
select_any(dig_from_response(attribute, path, !plural.nil?), (!negated.nil?))
|
22
|
+
step "it is #{assertion}"
|
23
|
+
end
|
18
24
|
end
|
19
25
|
|
20
26
|
Then(/^the value of the response #{RESPONSE_ATTRIBUTES}(?: child(ren)? `([^`]*)`)? does( not)? have any element that is (.*)(?<=:)$/) do
|
21
27
|
|attribute, plural, path, negated, assertion, multi|
|
22
|
-
|
23
|
-
|
28
|
+
perform do
|
29
|
+
select_any(dig_from_response(attribute, path, !plural.nil?), (!negated.nil?))
|
30
|
+
step "it is #{assertion}", multi.to_json
|
31
|
+
end
|
24
32
|
end
|
25
33
|
|
26
34
|
#Would be negated with `not all' which would be equivalent to any(not ) but that's not readily supported
|
27
35
|
Then(/^the value of the response #{RESPONSE_ATTRIBUTES}(?: child(ren)? `([^`]*)`)? has elements which are all (.*)(?<!:)$/) do
|
28
36
|
|attribute, plural, path, assertion|
|
29
|
-
|
30
|
-
|
37
|
+
perform do
|
38
|
+
select_all(dig_from_response(attribute, path, !plural.nil?), false)
|
39
|
+
step "it is #{assertion}"
|
40
|
+
end
|
31
41
|
end
|
32
42
|
|
33
43
|
Then(/^the value of the response #{RESPONSE_ATTRIBUTES}(?: child(ren)? `([^`]*)`)? has elements which are all (.*)(?<=:)$/) do
|
34
44
|
|attribute, plural, path, assertion, multi|
|
35
|
-
|
36
|
-
|
45
|
+
perform do
|
46
|
+
select_all(dig_from_response(attribute, path, !plural.nil?), false)
|
47
|
+
step "it is #{assertion}", multi.to_json
|
48
|
+
end
|
37
49
|
end
|
data/lib/brine/test_steps.rb
CHANGED
@@ -5,6 +5,9 @@ require 'rspec'
|
|
5
5
|
#
|
6
6
|
HTTP_METHOD='GET|POST|PATCH|PUT|DELETE|HEAD|OPTIONS'
|
7
7
|
|
8
|
+
ENV['BRINE_DURATION_SECONDS_short'] = '3'
|
9
|
+
ENV['BRINE_DURATION_SECONDS_long'] = '6'
|
10
|
+
|
8
11
|
class StubResponse
|
9
12
|
attr_accessor :body, :status, :headers
|
10
13
|
|
@@ -15,6 +18,19 @@ class StubResponse
|
|
15
18
|
end
|
16
19
|
end
|
17
20
|
|
21
|
+
class DelayedStubResponse < StubResponse
|
22
|
+
|
23
|
+
def initialize(delay)
|
24
|
+
super()
|
25
|
+
@activation = Time.now + delay
|
26
|
+
end
|
27
|
+
|
28
|
+
def body
|
29
|
+
Time.now < @activation ? nil : @body
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
|
18
34
|
class StubRequest
|
19
35
|
attr_accessor :method, :path, :headers, :body
|
20
36
|
|
@@ -135,6 +151,10 @@ When /^the response status is assigned `([^`]*)`$/ do |status|
|
|
135
151
|
@response.status = status.to_i # this coercion isn't needed but is a guarantee
|
136
152
|
end
|
137
153
|
|
154
|
+
When /^the response is delayed `([^`]*)` seconds$/ do |seconds|
|
155
|
+
@response = DelayedStubResponse.new(seconds)
|
156
|
+
end
|
157
|
+
|
138
158
|
Then(/^the response body as JSON is:$/) do |text|
|
139
159
|
expect(response.body.to_json).to eq text
|
140
160
|
end
|
data/lib/brine/transforms.rb
CHANGED
@@ -1,10 +1,14 @@
|
|
1
|
-
|
1
|
+
##
|
2
|
+
# @file transformers.rb
|
3
|
+
# Argument Transforms for Brine.
|
2
4
|
#
|
3
5
|
# The Transforms that convert provided inputs to support richer
|
4
6
|
# functionaliy than the simple strings which Cucumber provides.
|
5
7
|
|
6
|
-
|
8
|
+
##
|
9
|
+
# @defgroup Scalar transforms
|
7
10
|
# Convert inputs into basic Ruby data types which represent a single value
|
11
|
+
##
|
8
12
|
|
9
13
|
# Integers
|
10
14
|
Transform /\A(-?\d+)\z/ do |number|
|
@@ -31,8 +35,10 @@ Transform /^#{DATE}T#{TIME}#{MILLIS}#{TZ}$/ do |date|
|
|
31
35
|
Time.parse(date)
|
32
36
|
end
|
33
37
|
|
34
|
-
|
38
|
+
##
|
39
|
+
# @defgroup Structure transforms
|
35
40
|
# Converts inputs to general data structures
|
41
|
+
##
|
36
42
|
|
37
43
|
# Lists
|
38
44
|
Transform /\A\[.*\]\z/m do |input|
|
@@ -46,8 +52,10 @@ Transform /\A{.*}\z$/m do |input|
|
|
46
52
|
JSON.parse(input)
|
47
53
|
end
|
48
54
|
|
49
|
-
|
55
|
+
##
|
56
|
+
# @defgroup Atypical transforms
|
50
57
|
# Transforms for which data type is not the primary focus
|
58
|
+
##
|
51
59
|
|
52
60
|
# Whitespace removal transforms
|
53
61
|
# Handle stripping leading and trailing whitespace.
|