alinta-cucumber-rest-bdd 0.5.15 → 0.5.16
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/cucumber-rest-bdd/data.rb +52 -0
- data/lib/cucumber-rest-bdd/level.rb +6 -1
- data/lib/cucumber-rest-bdd/list.rb +140 -0
- data/lib/cucumber-rest-bdd/steps/auth.rb +6 -6
- data/lib/cucumber-rest-bdd/steps/functional.rb +6 -11
- data/lib/cucumber-rest-bdd/steps/resource.rb +29 -43
- data/lib/cucumber-rest-bdd/steps/response.rb +34 -191
- data/lib/cucumber-rest-bdd/steps/status.rb +21 -18
- data/lib/cucumber-rest-bdd/types.rb +32 -94
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 90847911531d65c3733c4d551f71f2baa423a928
|
4
|
+
data.tar.gz: 6c63a087ab4bc38b81ae0f116d5eecc23aca0691
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 49a81414220f17c4a70d3ba7748c4eff58e41679d831e01c68527041a5af8fb20b9848c4cb52cc836cf5e5a7c3bbeb586999b3cfdf125a0d00a41e1837f3fb4a
|
7
|
+
data.tar.gz: a6043d6bb06019ad6ccfe738cb1884f9714aa3ae359aab1d38552a874fe9346fa1a097b8c5757e3abc3fd1f026a85d7f97dbbba29af7ad3a7670dd9f703fe803
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'cucumber-rest-bdd/types'
|
2
|
+
|
3
|
+
# gets the relevant key for the response based on the first key element
|
4
|
+
def get_key(grouping)
|
5
|
+
errorKey = ENV['error_key']
|
6
|
+
if errorKey && !errorKey.empty? && grouping.count > 1 && grouping[-2][:key].singularize == errorKey then
|
7
|
+
return "$.#{errorKey}."
|
8
|
+
else
|
9
|
+
return get_root_data_key()
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
# top level has 2 children with an item containing at most three fish with attributes:
|
14
|
+
#
|
15
|
+
# nesting = [{key=fish,count=3,count_mod='<=',type=multiple},{key=item,type=single},{key=children,type=multiple,count=2,count_mod='='},{root=true,type=single}]
|
16
|
+
#
|
17
|
+
# returns true if the expected data is contained within the data based on the nesting information
|
18
|
+
def nest_match_attributes(data, nesting, expected, matchValue)
|
19
|
+
return false if !data
|
20
|
+
return data.deep_include?(expected) if !matchValue && nesting.size == 0
|
21
|
+
return data.include?(expected) if matchValue && nesting.size == 0
|
22
|
+
|
23
|
+
local_nesting = nesting.dup
|
24
|
+
level = local_nesting.pop
|
25
|
+
child_data = get_child_data(level, data)
|
26
|
+
|
27
|
+
case level[:type]
|
28
|
+
when 'single' then
|
29
|
+
return nest_match_attributes(child_data, local_nesting, expected, matchValue)
|
30
|
+
when 'multiple' then
|
31
|
+
matched = child_data.select { |item| nest_match_attributes(item, local_nesting, expected, matchValue) }
|
32
|
+
return level[:comparison].compare(matched.count)
|
33
|
+
when 'list' then
|
34
|
+
return child_data.is_a?(Array) && (!level.has_key?(:comparison) || level[:comparison].compare(child_data.count))
|
35
|
+
else
|
36
|
+
raise %/Unknown nested data type: #{level[:type]}/
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def get_child_data(level, data)
|
41
|
+
if (level[:root]) then
|
42
|
+
return data.dup
|
43
|
+
else
|
44
|
+
levelKey = case level[:type]
|
45
|
+
when 'single' then get_field(level[:key])
|
46
|
+
when 'multiple' then get_field(level[:key])
|
47
|
+
when 'list' then get_resource(level[:key])
|
48
|
+
end
|
49
|
+
raise %/Key not found: #{level[:key]} as #{levelKey} in #{data}/ if !data[levelKey]
|
50
|
+
return data[levelKey]
|
51
|
+
end
|
52
|
+
end
|
@@ -1,7 +1,12 @@
|
|
1
1
|
require 'cucumber-rest-bdd/types'
|
2
2
|
require 'active_support/inflector'
|
3
3
|
|
4
|
-
|
4
|
+
ParameterType(
|
5
|
+
name: 'levels',
|
6
|
+
regexp: /((?: (?:for|in|on) #{RESOURCE_NAME_SYNONYM}(?: with (?:key|id))? "[^"]*")*)/,
|
7
|
+
transformer: -> (levels) { Level.new(levels) },
|
8
|
+
use_for_snippets: false
|
9
|
+
)
|
5
10
|
|
6
11
|
class Level
|
7
12
|
@urls = []
|
@@ -0,0 +1,140 @@
|
|
1
|
+
require 'cucumber-rest-bdd/types'
|
2
|
+
|
3
|
+
FEWER_MORE_THAN_SYNONYM = %q{(?:fewer|less|more) than|at (?:least|most)}
|
4
|
+
INT_AS_WORDS_SYNONYM = %q{zero|one|two|three|four|five|six|seven|eight|nine|ten}
|
5
|
+
|
6
|
+
ParameterType(
|
7
|
+
name: 'list_has_count',
|
8
|
+
regexp: /a|an|(?:(#{FEWER_MORE_THAN_SYNONYM})\s+)?(#{INT_AS_WORDS_SYNONYM}|\d+)/,
|
9
|
+
transformer: -> (type, amount) { ListCountComparison.new(type, amount) },
|
10
|
+
use_for_snippets: false
|
11
|
+
)
|
12
|
+
|
13
|
+
ParameterType(
|
14
|
+
name: 'list_nesting',
|
15
|
+
regexp: %r{((?:(?:#{HAVE_ALTERNATION.split('/').join('|')})\s+(?:a list of\s+)?(?:a|an|(?:(?:#{FEWER_MORE_THAN_SYNONYM})\s+)?(?:#{INT_AS_WORDS_SYNONYM}|\d+))\s+(?:#{FIELD_NAME_SYNONYM})\s*)*)},
|
16
|
+
transformer: -> (match) { ListNesting.new(match) },
|
17
|
+
use_for_snippets: false
|
18
|
+
)
|
19
|
+
|
20
|
+
class ListNesting
|
21
|
+
def initialize(match)
|
22
|
+
@match = match
|
23
|
+
# gets an array in the nesting format that nest_match_attributes understands to interrogate nested object and array data
|
24
|
+
grouping = []
|
25
|
+
nesting = match
|
26
|
+
|
27
|
+
minimalListRegex = %r{(?:#{HAVE_ALTERNATION.split('/').join('|')})\s+(?:(a list of)\s+)?(?:a|an|(?:(#{FEWER_MORE_THAN_SYNONYM})\s+)?(#{INT_AS_WORDS_SYNONYM}|\d+))\s+(#{FIELD_NAME_SYNONYM})}
|
28
|
+
maximalListRegex = %r{(?:#{HAVE_ALTERNATION.split('/').join('|')})\s+(?:(a list of)\s+)?(?:a|an|(?:(#{FEWER_MORE_THAN_SYNONYM})\s+)?(#{INT_AS_WORDS_SYNONYM}|\d+))\s+(#{MAXIMAL_FIELD_NAME_SYNONYM})}
|
29
|
+
while matches = minimalListRegex.match(nesting)
|
30
|
+
nextMatches = minimalListRegex.match(nesting[matches.end(0), nesting.length])
|
31
|
+
matches = maximalListRegex.match(nextMatches.nil? ? nesting : nesting[0, matches.end(0) + nextMatches.begin(0)])
|
32
|
+
nesting = nesting[matches.end(0), nesting.length]
|
33
|
+
|
34
|
+
if matches[1].nil? then
|
35
|
+
if matches[3].nil? then
|
36
|
+
level = {
|
37
|
+
type: 'single',
|
38
|
+
key: matches[4],
|
39
|
+
root: false
|
40
|
+
}
|
41
|
+
else
|
42
|
+
level = {
|
43
|
+
type: 'multiple',
|
44
|
+
key: matches[4],
|
45
|
+
comparison: ListCountComparison.new(matches[2], matches[3]),
|
46
|
+
root: false
|
47
|
+
}
|
48
|
+
end
|
49
|
+
else
|
50
|
+
level = {
|
51
|
+
type: 'list',
|
52
|
+
key: matches[4],
|
53
|
+
comparison: ListCountComparison.new(matches[2], matches[3]),
|
54
|
+
root: false
|
55
|
+
}
|
56
|
+
end
|
57
|
+
grouping.push(level)
|
58
|
+
end
|
59
|
+
@grouping = grouping.reverse
|
60
|
+
end
|
61
|
+
|
62
|
+
def push(node)
|
63
|
+
@grouping.push(node)
|
64
|
+
end
|
65
|
+
|
66
|
+
def match
|
67
|
+
return @match
|
68
|
+
end
|
69
|
+
|
70
|
+
def grouping
|
71
|
+
@grouping
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
class ListCountComparison
|
76
|
+
def initialize(type, amount)
|
77
|
+
@type = type.nil? ? CMP_EQUALS : to_compare(type)
|
78
|
+
@amount = amount.nil? ? 1 : to_num(amount)
|
79
|
+
end
|
80
|
+
|
81
|
+
def compare(actual)
|
82
|
+
case @type
|
83
|
+
when CMP_LESS_THAN then actual < @amount
|
84
|
+
when CMP_MORE_THAN then actual > @amount
|
85
|
+
when CMP_AT_MOST then actual <= @amount
|
86
|
+
when CMP_AT_LEAST then actual >= @amount
|
87
|
+
when CMP_EQUALS then actual == @amount
|
88
|
+
else actual == @amount
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
def type
|
93
|
+
return @type
|
94
|
+
end
|
95
|
+
|
96
|
+
def amount
|
97
|
+
return amount
|
98
|
+
end
|
99
|
+
|
100
|
+
# turn a comparison into a string
|
101
|
+
def compare_to_string()
|
102
|
+
case @type
|
103
|
+
when CMP_LESS_THAN then 'fewer than '
|
104
|
+
when CMP_MORE_THAN then 'more than '
|
105
|
+
when CMP_AT_LEAST then 'at least '
|
106
|
+
when CMP_AT_MOST then 'at most '
|
107
|
+
when CMP_EQUALS then 'exactly '
|
108
|
+
else ''
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
def to_string()
|
113
|
+
return compare_to_string() + ' ' + @amount.to_s
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
CMP_LESS_THAN = '<'
|
118
|
+
CMP_MORE_THAN = '>'
|
119
|
+
CMP_AT_LEAST = '>='
|
120
|
+
CMP_AT_MOST = '<='
|
121
|
+
CMP_EQUALS = '='
|
122
|
+
|
123
|
+
# take a number modifier string (fewer than, less than, etc) and return an operator '<', etc
|
124
|
+
def to_compare(compare)
|
125
|
+
return case compare
|
126
|
+
when 'fewer than' then CMP_LESS_THAN
|
127
|
+
when 'less than' then CMP_LESS_THAN
|
128
|
+
when 'more than' then CMP_MORE_THAN
|
129
|
+
when 'at least' then CMP_AT_LEAST
|
130
|
+
when 'at most' then CMP_AT_MOST
|
131
|
+
else CMP_EQUALS
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
def to_num(num)
|
136
|
+
if /^(?:zero|one|two|three|four|five|six|seven|eight|nine|ten)$/.match(num)
|
137
|
+
return %w(zero one two three four five six seven eight nine ten).index(num)
|
138
|
+
end
|
139
|
+
return num.to_i
|
140
|
+
end
|
@@ -1,4 +1,4 @@
|
|
1
|
-
Given(
|
1
|
+
Given("I retrieve the API Management subscription key secret {string} from Azure Storage Vault {string} using tenant {string} with credentials {string} and {string}") do |secret_name, vault_name, tenant_id, client_id, client_secret|
|
2
2
|
if @apim_subscription_key.to_s.empty?
|
3
3
|
steps %Q{
|
4
4
|
Given I retrieve the secret "#{secret_name}" from Azure Storage Vault "#{vault_name}" using tenant "#{tenant_id}" with credentials "#{client_id}" and "#{client_secret}"
|
@@ -7,14 +7,14 @@ Given(/^I retrieve the API Management subscription key secret "(.*?)" from Azure
|
|
7
7
|
end
|
8
8
|
end
|
9
9
|
|
10
|
-
Given(
|
10
|
+
Given("I add the API Management key header") do
|
11
11
|
steps %Q{
|
12
12
|
And I add Headers:
|
13
13
|
| Ocp-Apim-Subscription-Key | #{@apim_subscription_key} |
|
14
14
|
}
|
15
15
|
end
|
16
16
|
|
17
|
-
Given(
|
17
|
+
Given("I retrieve the secret {string} from Azure Storage Vault {string} using tenant {string} with credentials {string} and {string}") do |secret_name, vault_name, tenant_id, client_id, client_secret|
|
18
18
|
steps %Q{
|
19
19
|
Given I authenticate with Azure tenant "#{tenant_id}" using client credentials "#{client_id}" and "#{client_secret}"
|
20
20
|
}
|
@@ -24,13 +24,13 @@ Given(/^I retrieve the secret "(.*?)" from Azure Storage Vault "(.*?)" using ten
|
|
24
24
|
}
|
25
25
|
end
|
26
26
|
|
27
|
-
Given(
|
27
|
+
Given("I authenticate with Azure tenant {string} using client credentials {string} and {string}") do |tenant_id, client_id, client_secret|
|
28
28
|
steps %Q{
|
29
29
|
Given I authenticate with "https://login.windows.net/#{tenant_id}/oauth2/token" using client credentials "#{client_id}" and "#{client_secret}"
|
30
30
|
}
|
31
31
|
end
|
32
32
|
|
33
|
-
Given(
|
33
|
+
Given("I authenticate with {string} using client credentials {string} and {string}") do |url, client_id, client_secret|
|
34
34
|
steps %Q{
|
35
35
|
Given I send "www-x-form-urlencoded" and accept JSON
|
36
36
|
When I set form request body to:
|
@@ -43,7 +43,7 @@ Given(/^I authenticate with "(.*?)" using client credentials "(.*?)" and "(.*?)"
|
|
43
43
|
}
|
44
44
|
end
|
45
45
|
|
46
|
-
Given(
|
46
|
+
Given("I retrieve the secret {string} from Azure Storage Vault {string} using access token {string}") do |secret_name, vault_name, access_token|
|
47
47
|
api_version = '2015-06-01'
|
48
48
|
url = "https://#{vault_name}.vault.azure.net/secrets/#{secret_name}?api-version=#{api_version}"
|
49
49
|
steps %Q{
|
@@ -1,8 +1,9 @@
|
|
1
1
|
require 'cucumber-api/response'
|
2
2
|
require 'cucumber-api/steps'
|
3
3
|
require 'cucumber-rest-bdd/types'
|
4
|
+
require 'cucumber-rest-bdd/list'
|
4
5
|
|
5
|
-
Then(
|
6
|
+
Then("the response (should )have/has (a/the )header {string} #{HAVE_ALTERNATION} (a/the )value {string}") do |header, value|
|
6
7
|
p_value = resolve(value)
|
7
8
|
p_header = header.parameterize
|
8
9
|
raise %/Required header: #{header} not found\n#{@response.raw_headers.inspect}/ if !@response.raw_headers.key?(p_header)
|
@@ -10,23 +11,17 @@ Then(/^the response (?:should have|has a|has the) header "([^"]*)" with (?:a |th
|
|
10
11
|
raise %/Expect #{p_value} in #{header} (#{p_header})\n#{@response.raw_headers.inspect}/ if !exists
|
11
12
|
end
|
12
13
|
|
13
|
-
Then(
|
14
|
+
Then("the JSON response should have {string} of type array #{HAVE_ALTERNATION} {list_has_count} entry/entries") do |json_path, list_comparison|
|
14
15
|
list = @response.get_as_type json_path, 'array'
|
15
|
-
raise %/Expected #{
|
16
|
+
raise %/Expected #{list_comparison.to_string()} entries in array for path '#{json_path}', found: #{list.count}\n#{@response.to_json_s}/ if !list_comparison.compare(list.count)
|
16
17
|
end
|
17
18
|
|
18
|
-
Then(
|
19
|
-
list = @response.get_as_type json_path, 'array'
|
20
|
-
raise %/Expected #{count_mod} #{number} items in array for path '#{json_path}', found: #{list.count}\n#{@response.to_json_s}/ \
|
21
|
-
if !num_compare(count_mod, list.count, number.to_i)
|
22
|
-
end
|
23
|
-
|
24
|
-
Then(/^the JSON response should have "([^"]*)" of type (.+) that matches "(.+)"$/) do |json_path, type, regex|
|
19
|
+
Then("the JSON response should have {string} of type {word} that matches {string}") do |json_path, type, regex|
|
25
20
|
value = @response.get_as_type json_path, type
|
26
21
|
raise %/Expected #{json_path} value '#{value}' to match regex: #{regex}\n#{@response.to_json_s}/ if (Regexp.new(regex) =~ value).nil?
|
27
22
|
end
|
28
23
|
|
29
|
-
Then(
|
24
|
+
Then("the JSON response should have {string} of type nill/null/nil") do |json_path|
|
30
25
|
value = @response.get_as_type_or_null json_path, 'string'
|
31
26
|
raise %/Expected #{json_path} to be nil, was: #{value.class}\n#{@response.to_json_s}/ if !value.nil?
|
32
27
|
end
|
@@ -10,27 +10,25 @@ require 'easy_diff'
|
|
10
10
|
GET_TYPES = %{(?:an?(?! list)|the)}%
|
11
11
|
WITH_ID = %{(?: with (?:key|id))? "([^"]*)"}%
|
12
12
|
|
13
|
-
Given(
|
13
|
+
Given("I am a client") do
|
14
14
|
steps %Q{
|
15
15
|
Given I send "application/json" and accept JSON
|
16
16
|
}
|
17
17
|
end
|
18
18
|
|
19
|
-
Given(
|
20
|
-
@urlbasepath =
|
19
|
+
Given("I am issuing requests for {resource_name}") do |resource|
|
20
|
+
@urlbasepath = resource
|
21
21
|
end
|
22
22
|
|
23
23
|
# GET
|
24
24
|
|
25
|
-
When(
|
26
|
-
|
27
|
-
url = get_url("#{Level.new(levels).url}#{resource_name}/#{id}")
|
25
|
+
When("I request the {resource_name} (with key/id ){string}{levels}") do |resource, id, levels|
|
26
|
+
url = get_url("#{levels.url}#{resource}/#{id}")
|
28
27
|
steps %Q{When I send a GET request to "#{url}"}
|
29
28
|
end
|
30
29
|
|
31
|
-
When(
|
32
|
-
|
33
|
-
url = get_url("#{Level.new(levels).url}#{resource_name}/#{id}")
|
30
|
+
When("I request the {resource_name} (with key/id ){string}{levels} with:") do |resource, id, levels, params|
|
31
|
+
url = get_url("#{levels.url}#{resource}/#{id}")
|
34
32
|
unless params.raw.empty?
|
35
33
|
query = params.raw.map{|key, value| %/#{get_field(key)}=#{resolve(value)}/}.join("&")
|
36
34
|
url = "#{url}?#{query}"
|
@@ -38,15 +36,13 @@ When(/^I request #{GET_TYPES} #{RESOURCE_NAME_CAPTURE}#{WITH_ID}#{LEVELS} with:$
|
|
38
36
|
steps %Q{When I send a GET request to "#{url}"}
|
39
37
|
end
|
40
38
|
|
41
|
-
When(
|
42
|
-
|
43
|
-
url = get_url("#{Level.new(levels).url}#{resource_name}")
|
39
|
+
When("I request a list of {resource_name}{levels}") do |resource, levels|
|
40
|
+
url = get_url("#{levels.url}#{resource}")
|
44
41
|
steps %Q{When I send a GET request to "#{url}"}
|
45
42
|
end
|
46
43
|
|
47
|
-
When(
|
48
|
-
|
49
|
-
url = get_url("#{Level.new(levels).url}#{resource_name}")
|
44
|
+
When("I request a list of {resource_name}{levels} with:") do |resource, levels, params|
|
45
|
+
url = get_url("#{levels.url}#{resource}")
|
50
46
|
unless params.raw.empty?
|
51
47
|
query = params.raw.map{|key, value| %/#{get_field(key)}=#{resolve(value)}/}.join("&")
|
52
48
|
url = "#{url}?#{query}"
|
@@ -56,19 +52,16 @@ end
|
|
56
52
|
|
57
53
|
# DELETE
|
58
54
|
|
59
|
-
When(
|
60
|
-
|
61
|
-
url = get_url("#{Level.new(levels).url}#{resource_name}/#{id}")
|
55
|
+
When("I request to delete/remove a/an/the {resource_name} (with key/id ){string}{levels}") do |resource, id, levels|
|
56
|
+
url = get_url("#{levels.url}#{resource}/#{id}")
|
62
57
|
steps %Q{When I send a DELETE request to "#{url}"}
|
63
58
|
end
|
64
59
|
|
65
60
|
# POST
|
66
61
|
|
67
|
-
When(
|
68
|
-
resource_name = get_resource(resource)
|
69
|
-
level = Level.new(levels)
|
62
|
+
When("I request to create a/an/the {resource_name}{levels}") do |resource, levels|
|
70
63
|
if ENV['set_parent_id'] == 'true'
|
71
|
-
json = MultiJson.dump(
|
64
|
+
json = MultiJson.dump(levels.last_hash)
|
72
65
|
steps %Q{
|
73
66
|
When I set JSON request body to:
|
74
67
|
"""
|
@@ -76,17 +69,15 @@ When(/^I request to create #{ARTICLE} #{RESOURCE_NAME_CAPTURE}#{LEVELS}$/) do |r
|
|
76
69
|
"""
|
77
70
|
}
|
78
71
|
end
|
79
|
-
url = get_url("#{
|
72
|
+
url = get_url("#{levels.url}#{resource}")
|
80
73
|
steps %Q{When I send a POST request to "#{url}"}
|
81
74
|
end
|
82
75
|
|
83
|
-
When(
|
84
|
-
resource_name = get_resource(resource)
|
76
|
+
When("I request to create a/an/the {resource_name}{levels} with:") do |resource, levels, params|
|
85
77
|
request_hash = get_attributes(params.hashes)
|
86
|
-
|
87
|
-
request_hash = request_hash.merge(level.last_hash) if ENV['set_parent_id'] == 'true'
|
78
|
+
request_hash = request_hash.merge(levels.last_hash) if ENV['set_parent_id'] == 'true'
|
88
79
|
json = MultiJson.dump(request_hash)
|
89
|
-
url = get_url("#{
|
80
|
+
url = get_url("#{levels.url}#{resource}")
|
90
81
|
steps %Q{
|
91
82
|
When I set JSON request body to:
|
92
83
|
"""
|
@@ -98,11 +89,9 @@ end
|
|
98
89
|
|
99
90
|
# PUT
|
100
91
|
|
101
|
-
When(
|
102
|
-
resource_name = get_resource(resource)
|
103
|
-
level = Level.new(levels)
|
92
|
+
When("I request to replace/set a/an/the {resource_name} (with key/id ){string}{levels}") do |resource, id, levels|
|
104
93
|
if ENV['set_parent_id'] == 'true'
|
105
|
-
json = MultiJson.dump(
|
94
|
+
json = MultiJson.dump(levels.last_hash)
|
106
95
|
steps %Q{
|
107
96
|
When I set JSON request body to:
|
108
97
|
"""
|
@@ -110,19 +99,17 @@ When(/^I request to (?:create|replace|set) #{ARTICLE} #{RESOURCE_NAME_CAPTURE}#{
|
|
110
99
|
"""
|
111
100
|
}
|
112
101
|
end
|
113
|
-
url = get_url("#{
|
102
|
+
url = get_url("#{levels.url}#{resource}/#{id}")
|
114
103
|
steps %Q{
|
115
104
|
When I send a PUT request to "#{url}"
|
116
105
|
}
|
117
106
|
end
|
118
107
|
|
119
|
-
When(
|
120
|
-
resource_name = get_resource(resource)
|
108
|
+
When("I request to replace/set a/an/the {resource_name} (with key/id ){string}{levels} with:") do |resource, id, levels, params|
|
121
109
|
request_hash = get_attributes(params.hashes)
|
122
|
-
|
123
|
-
request_hash = request_hash.merge(level.last_hash) if ENV['set_parent_id'] == 'true'
|
110
|
+
request_hash = request_hash.merge(levels.last_hash) if ENV['set_parent_id'] == 'true'
|
124
111
|
json = MultiJson.dump(request_hash)
|
125
|
-
url = get_url("#{
|
112
|
+
url = get_url("#{levels.url}#{resource}/#{id}")
|
126
113
|
steps %Q{
|
127
114
|
When I set JSON request body to:
|
128
115
|
"""
|
@@ -134,11 +121,10 @@ end
|
|
134
121
|
|
135
122
|
# PATCH
|
136
123
|
|
137
|
-
When(
|
138
|
-
resource_name = get_resource(resource)
|
124
|
+
When("I request to modify/update a/an/the {resource_name} (with key/id ){string}{levels} with:") do |resource, id, levels, params|
|
139
125
|
request_hash = get_attributes(params.hashes)
|
140
126
|
json = MultiJson.dump(request_hash)
|
141
|
-
url = get_url("#{
|
127
|
+
url = get_url("#{levels.url}#{resource}/#{id}")
|
142
128
|
steps %Q{
|
143
129
|
When I set JSON request body to:
|
144
130
|
"""
|
@@ -150,10 +136,10 @@ end
|
|
150
136
|
|
151
137
|
# value capture
|
152
138
|
|
153
|
-
When(
|
139
|
+
When("I save (attribute ){string}") do |attribute|
|
154
140
|
steps %Q{When I grab "#{get_json_path(attribute)}" as "#{attribute}"}
|
155
141
|
end
|
156
142
|
|
157
|
-
When(
|
143
|
+
When("I save (attribute ){string} to {string}") do |attribute, ref|
|
158
144
|
steps %Q{When I grab "#{get_json_path(attribute)}" as "#{ref}"}
|
159
145
|
end
|
@@ -1,237 +1,80 @@
|
|
1
1
|
require 'cucumber-rest-bdd/steps/resource'
|
2
2
|
require 'cucumber-rest-bdd/types'
|
3
|
+
require 'cucumber-rest-bdd/list'
|
4
|
+
require 'cucumber-rest-bdd/data'
|
3
5
|
|
4
|
-
|
5
|
-
LIST_HAS_SYNONYM_WITHOUT_CAPTURE = %r{(?:a|an|(?:(?:#{FEWER_MORE_THAN_SYNONYM})\s+)?(?:#{INT_AS_WORDS_SYNONYM}|\d+))\s+(?:#{FIELD_NAME_SYNONYM})}
|
6
|
-
|
7
|
-
Then(/^print the response$/) do
|
6
|
+
Then("print the response") do
|
8
7
|
puts %/The response:\n#{@response.to_json_s}/
|
9
8
|
end
|
10
9
|
|
11
10
|
# response interrogation
|
12
11
|
|
13
|
-
Then(
|
12
|
+
Then("the response #{HAVE_ALTERNATION} {field_name} of type {word}") do |field, type|
|
14
13
|
regex = case type
|
15
|
-
|
16
|
-
|
17
|
-
|
14
|
+
when 'datetime' then /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{1,3})?(?:[+|-]\d{2}:\d{2})?$/i
|
15
|
+
when 'guid' then /^[{(]?[0-9A-F]{8}[-]?([0-9A-F]{4}[-]?){3}[0-9A-F]{12}[)}]?$/i
|
16
|
+
else nil
|
18
17
|
end
|
19
|
-
validate_value(names, 'string', regex)
|
20
|
-
end
|
21
18
|
|
22
|
-
|
23
|
-
validate_value(
|
19
|
+
type = 'string' if regex.nil?
|
20
|
+
field.validate_value(field.get_value(type), Regexp.new(regex))
|
24
21
|
end
|
25
22
|
|
26
|
-
|
27
|
-
|
28
|
-
type = parse_type(type)
|
29
|
-
value = @response.get_as_type json_path, type
|
30
|
-
raise %/Expected #{json_path} value '#{value}' to match regex: #{regex}\n#{@response.to_json_s}/ if (regex =~ value).nil?
|
23
|
+
Then("the response #{HAVE_ALTERNATION} {field_name} of type {word} that matches {string}") do |field, type, regex|
|
24
|
+
field.validate_value(field.get_value(type), Regexp.new(regex))
|
31
25
|
end
|
32
26
|
|
33
27
|
Then("the response is a list of/containing {list_has_count} {field_name}") do |list_comparison, item|
|
34
28
|
list = @response.get_as_type get_root_data_key(), 'array'
|
35
|
-
raise %/Expected
|
29
|
+
raise %/Expected #{list_comparison.to_string()} items in array for path '#{get_root_data_key()}', found: #{list.count}\n#{@response.to_json_s}/ if !list_comparison.compare(list.count)
|
36
30
|
end
|
37
31
|
|
38
|
-
Then(
|
32
|
+
Then("the response( {list_nesting}) #{HAVE_ALTERNATION} (the )(following )attributes:") do |nesting, attributes|
|
39
33
|
expected = get_attributes(attributes.hashes)
|
40
|
-
|
41
|
-
grouping = get_grouping(groups)
|
42
|
-
grouping.push({
|
34
|
+
nesting.push({
|
43
35
|
root: true,
|
44
36
|
type: 'single'
|
45
37
|
})
|
46
|
-
data = @response.get get_key(grouping)
|
47
|
-
raise %/Could not find a match for: #{nesting}\n#{expected.inspect}\n#{@response.to_json_s}/ if data.empty? || !nest_match_attributes(data, grouping, expected)
|
38
|
+
data = @response.get get_key(nesting.grouping)
|
39
|
+
raise %/Could not find a match for: #{nesting.match}\n#{expected.inspect}\n#{@response.to_json_s}/ if data.empty? || !nest_match_attributes(data, nesting.grouping, expected, false)
|
48
40
|
end
|
49
41
|
|
50
|
-
Then(
|
42
|
+
Then("the response( {list_nesting}) #{HAVE_ALTERNATION} (the )(following )value {string}") do |nesting, value|
|
51
43
|
expected = value
|
52
|
-
|
53
|
-
grouping = get_grouping(groups)
|
54
|
-
grouping.push({
|
44
|
+
nesting.push({
|
55
45
|
root: true,
|
56
46
|
type: 'single'
|
57
47
|
})
|
58
|
-
data = @response.get get_key(grouping)
|
59
|
-
raise %/Could not find a match for: #{nesting}\n#{expected}\n#{@response.to_json_s}/ if data.empty? || !
|
48
|
+
data = @response.get get_key(nesting.grouping)
|
49
|
+
raise %/Could not find a match for: #{nesting.match}\n#{expected}\n#{@response.to_json_s}/ if data.empty? || !nest_match_attributes(data, nesting.grouping, expected, true)
|
60
50
|
end
|
61
51
|
|
62
|
-
Then(
|
63
|
-
|
64
|
-
grouping = get_grouping(groups)
|
65
|
-
grouping.push({
|
52
|
+
Then("the response {list_nesting}") do |nesting|
|
53
|
+
nesting.push({
|
66
54
|
root: true,
|
67
55
|
type: 'single'
|
68
56
|
})
|
69
|
-
data = @response.get get_key(grouping)
|
70
|
-
raise %/Could not find a match for: #{nesting}\n#{@response.to_json_s}/ if data.empty? || !nest_match_attributes(data, grouping, {})
|
57
|
+
data = @response.get get_key(nesting.grouping)
|
58
|
+
raise %/Could not find a match for: #{nesting.match}\n#{@response.to_json_s}/ if data.empty? || !nest_match_attributes(data, nesting.grouping, {}, false)
|
71
59
|
end
|
72
60
|
|
73
|
-
Then(
|
61
|
+
Then("{list_has_count} {field_name}( {list_nesting}) #{HAVE_ALTERNATION} (the )(following )(data )attributes:") do |list_comparison, count_item, nesting, attributes|
|
74
62
|
expected = get_attributes(attributes.hashes)
|
75
|
-
|
76
|
-
grouping = get_grouping(groups)
|
77
|
-
grouping.push({
|
63
|
+
nesting.push({
|
78
64
|
root: true,
|
79
65
|
type: 'multiple',
|
80
|
-
|
81
|
-
count_mod: to_compare(count_mod)
|
66
|
+
comparison: list_comparison
|
82
67
|
})
|
83
|
-
data = @response.get get_key(grouping)
|
84
|
-
raise %/Expected #{
|
68
|
+
data = @response.get get_key(nesting.grouping)
|
69
|
+
raise %/Expected #{list_comparison.to_string()} items in array with attributes for: #{nesting.match}\n#{expected.inspect}\n#{@response.to_json_s}/ if !nest_match_attributes(data, nesting.grouping, expected, false)
|
85
70
|
end
|
86
71
|
|
87
|
-
Then(
|
88
|
-
|
89
|
-
grouping = get_grouping(groups)
|
90
|
-
grouping.push({
|
72
|
+
Then("{list_has_count} {field_name} {list_nesting}") do |list_comparison, item, nesting|
|
73
|
+
nesting.push({
|
91
74
|
root: true,
|
92
75
|
type: 'multiple',
|
93
|
-
|
94
|
-
count_mod: to_compare(count_mod)
|
95
|
-
})
|
96
|
-
data = @response.get get_key(grouping)
|
97
|
-
raise %/Expected #{compare_to_string(count_mod)}#{count} items in array with: #{nesting}\n#{@response.to_json_s}/ if !nest_match_attributes(data, grouping, {})
|
98
|
-
end
|
99
|
-
|
100
|
-
Then(/^the response ((?:#{HAVE_SYNONYM}\s+#{LIST_HAS_SYNONYM_WITHOUT_CAPTURE}\s+)*)#{HAVE_SYNONYM} a list of #{LIST_HAS_SYNONYM}$/) do |nesting, num_mod, num, item|
|
101
|
-
groups = nesting
|
102
|
-
list = {
|
103
|
-
type: 'list',
|
104
|
-
key: get_resource(item)
|
105
|
-
}
|
106
|
-
if (num) then
|
107
|
-
list[:count] = num.to_i
|
108
|
-
list[:count_mod] = num_mod
|
109
|
-
end
|
110
|
-
grouping = [list]
|
111
|
-
grouping.concat(get_grouping(groups))
|
112
|
-
grouping.push({
|
113
|
-
root: true,
|
114
|
-
type: 'single'
|
76
|
+
comparison: list_comparison
|
115
77
|
})
|
116
|
-
data = @response.get get_key(grouping)
|
117
|
-
raise %/
|
118
|
-
end
|
119
|
-
|
120
|
-
Then(/^#{LIST_HAS_SYNONYM} ((?:#{HAVE_SYNONYM}\s+#{LIST_HAS_SYNONYM_WITHOUT_CAPTURE}\s+)*)#{HAVE_SYNONYM} a list of #{LIST_HAS_SYNONYM}$/) do |count_mod, count, count_item, nesting, num_mod, num, item|
|
121
|
-
groups = nesting
|
122
|
-
list = {
|
123
|
-
type: 'list',
|
124
|
-
key: get_resource(item)
|
125
|
-
}
|
126
|
-
if (num) then
|
127
|
-
list[:count] = num.to_i
|
128
|
-
list[:count_mod] = num_mod
|
129
|
-
end
|
130
|
-
grouping = [list]
|
131
|
-
grouping.concat(get_grouping(groups))
|
132
|
-
grouping.push({
|
133
|
-
root: true,
|
134
|
-
type: 'multiple',
|
135
|
-
count: to_num(count),
|
136
|
-
count_mod: to_compare(count_mod)
|
137
|
-
})
|
138
|
-
data = @response.get get_key(grouping)
|
139
|
-
raise %/Expected #{compare_to_string(count_mod)}#{count} items with #{nesting}#{compare_to_string(num_mod)}#{num}#{item}\n#{@response.to_json_s}/ if !nest_match_attributes(data, grouping, {})
|
140
|
-
end
|
141
|
-
|
142
|
-
# gets the relevant key for the response based on the first key element
|
143
|
-
def get_key(grouping)
|
144
|
-
if ENV['error_key'] && !ENV['error_key'].empty? && grouping.count > 1 && grouping[-2][:key].singularize == ENV['error_key'] then
|
145
|
-
get_root_error_key()
|
146
|
-
else
|
147
|
-
get_root_data_key()
|
148
|
-
end
|
149
|
-
end
|
150
|
-
|
151
|
-
# gets an array in the nesting format that nest_match_attributes understands to interrogate nested object and array data
|
152
|
-
def get_grouping(nesting)
|
153
|
-
grouping = []
|
154
|
-
while matches = /#{LIST_HAS_SYNONYM}/.match(nesting)
|
155
|
-
nesting = nesting[matches.end(0), nesting.length]
|
156
|
-
if matches[2].nil? then
|
157
|
-
level = {
|
158
|
-
type: 'single',
|
159
|
-
key: matches[3],
|
160
|
-
root: false
|
161
|
-
}
|
162
|
-
else
|
163
|
-
level = {
|
164
|
-
type: 'multiple',
|
165
|
-
key: matches[3],
|
166
|
-
count: to_num(matches[2]),
|
167
|
-
root: false,
|
168
|
-
count_mod: to_compare(matches[1])
|
169
|
-
}
|
170
|
-
end
|
171
|
-
grouping.push(level)
|
172
|
-
end
|
173
|
-
return grouping.reverse
|
174
|
-
end
|
175
|
-
|
176
|
-
# top level has 2 children with an item containing at most three fish with attributes:
|
177
|
-
#
|
178
|
-
# nesting = [{key=fish,count=3,count_mod='<=',type=multiple},{key=item,type=single},{key=children,type=multiple,count=2,count_mod='='},{root=true,type=single}]
|
179
|
-
#
|
180
|
-
# returns true if the expected data is contained within the data based on the nesting information
|
181
|
-
def nest_match_attributes(data, nesting, expected)
|
182
|
-
print 'data:' + data.to_s
|
183
|
-
print 'nesting:' + nesting.to_s
|
184
|
-
print 'expected:' + expected.to_s
|
185
|
-
|
186
|
-
return false if !data
|
187
|
-
return data.deep_include?(expected) if nesting.size == 0
|
188
|
-
|
189
|
-
local_nesting = nesting.dup
|
190
|
-
level = local_nesting.pop
|
191
|
-
case level[:type]
|
192
|
-
when 'single' then
|
193
|
-
child_data = level[:root] ? data.dup : data[get_field(level[:key])]
|
194
|
-
return nest_match_attributes(child_data, local_nesting, expected)
|
195
|
-
when 'multiple' then
|
196
|
-
child_data = level[:root] ? data.dup : data[get_field(level[:key])]
|
197
|
-
matched = child_data.select { |item| nest_match_attributes(item, local_nesting, expected) }
|
198
|
-
print 'matched:' + matched.count.to_s
|
199
|
-
return num_compare(level[:count_mod], matched.count, level[:count])
|
200
|
-
when 'list' then
|
201
|
-
child_data = level[:root] ? data.dup : data[get_resource(level[:key])]
|
202
|
-
return false if !child_data.is_a?(Array)
|
203
|
-
if level.has_key?(:count) then
|
204
|
-
return num_compare(level[:count_mod], child_data.count, level[:count])
|
205
|
-
end
|
206
|
-
return true
|
207
|
-
else
|
208
|
-
raise %/Unknown nested data type: #{level[:type]}/
|
209
|
-
end
|
210
|
-
end
|
211
|
-
|
212
|
-
def nest_match_value(data, nesting, expected)
|
213
|
-
return false if !data
|
214
|
-
return data.include?(expected) if nesting.size == 0
|
215
|
-
|
216
|
-
local_nesting = nesting.dup
|
217
|
-
level = local_nesting.pop
|
218
|
-
case level[:type]
|
219
|
-
when 'single' then
|
220
|
-
child_data = level[:root] ? data.dup : data[get_field(level[:key])]
|
221
|
-
return nest_match_value(child_data, local_nesting, expected)
|
222
|
-
when 'multiple' then
|
223
|
-
child_data = level[:root] ? data.dup : data[get_field(level[:key])]
|
224
|
-
raise %/Key not found: #{level[:key]} as #{get_field(level[:key])} in #{data}/ if !child_data
|
225
|
-
matched = child_data.select { |item| nest_match_value(item, local_nesting, expected) }
|
226
|
-
return num_compare(level[:count_mod], matched.count, level[:count])
|
227
|
-
when 'list' then
|
228
|
-
child_data = level[:root] ? data.dup : data[get_resource(level[:key])]
|
229
|
-
return false if !child_data.is_a?(Array)
|
230
|
-
if level.has_key?(:count) then
|
231
|
-
return num_compare(level[:count_mod], child_data.count, level[:count])
|
232
|
-
end
|
233
|
-
return true
|
234
|
-
else
|
235
|
-
raise %/Unknown nested data type: #{level[:type]}/
|
236
|
-
end
|
78
|
+
data = @response.get get_key(nesting.grouping)
|
79
|
+
raise %/Expected #{list_comparison.to_string()} items in array with: #{nesting.match}\n#{@response.to_json_s}/ if !nest_match_attributes(data, nesting.grouping, {}, false)
|
237
80
|
end
|
@@ -1,63 +1,66 @@
|
|
1
1
|
require 'cucumber-api/response'
|
2
2
|
require 'cucumber-api/steps'
|
3
3
|
|
4
|
-
|
4
|
+
ParameterType(
|
5
|
+
name: 'item_type',
|
6
|
+
regexp: /([\w\s]+)/,
|
7
|
+
transformer: -> (s) { s },
|
8
|
+
use_for_snippets: false
|
9
|
+
)
|
10
|
+
|
11
|
+
Then("the request is/was successful") do
|
5
12
|
raise %/Expected Successful response code 2xx but was #{@response.code}/ if @response.code < 200 || @response.code >= 300
|
6
13
|
end
|
7
14
|
|
8
|
-
Then(
|
15
|
+
Then("the request is/was redirected") do
|
9
16
|
raise %/Expected redirected response code 3xx but was #{@response.code}/ if @response.code < 300 || @response.code >= 400
|
10
17
|
end
|
11
18
|
|
12
|
-
Then(
|
19
|
+
Then("the request fail(s/ed)") do
|
13
20
|
raise %/Expected failed response code 4xx\/5xx but was #{@response.code}/ if @response.code < 400 || @response.code >= 600
|
14
21
|
end
|
15
22
|
|
16
|
-
Then(
|
17
|
-
steps %Q{Then the response status should be "201"}
|
18
|
-
end
|
19
|
-
|
20
|
-
Then(/^the request (?:is|was) successful and (?:a resource|.+) (?:is|was) created$/) do
|
23
|
+
Then("the request is/was successful and a/the resource/{item_type} is/was created") do | item_type |
|
21
24
|
steps %Q{Then the response status should be "201"}
|
22
25
|
end
|
23
26
|
|
24
|
-
Then(
|
27
|
+
Then("the request is/was successfully accepted") do
|
25
28
|
steps %Q{Then the response status should be "202"}
|
26
29
|
end
|
27
30
|
|
28
|
-
Then(
|
31
|
+
Then("the request is/was successful and (an )empty/blank/no response body is/was returned") do
|
29
32
|
steps %Q{Then the response status should be "204"}
|
30
33
|
raise %/Expected the request body to be empty/ if !@response.body.empty?
|
31
34
|
end
|
32
35
|
|
33
|
-
Then(
|
36
|
+
Then("the request fail(s/ed) because (the )it/resource/{item_type} is/was invalid") do | item_type |
|
34
37
|
steps %Q{Then the response status should be "400"}
|
35
38
|
end
|
36
39
|
|
37
|
-
Then(
|
40
|
+
Then("the request fail(s/ed) because (the )it/resource/{item_type} is/was/am/are unauthorised/unauthorized") do | item_type |
|
38
41
|
steps %Q{Then the response status should be "401"}
|
39
42
|
end
|
40
43
|
|
41
|
-
Then(
|
44
|
+
Then("the request fail(s/ed) because (the )it/resource/{item_type} is/was forbidden") do | item_type |
|
42
45
|
steps %Q{Then the response status should be "403"}
|
43
46
|
end
|
44
47
|
|
45
|
-
Then(
|
48
|
+
Then("the request fail(s/ed) because (the )it/resource/{item_type} is/was not found") do | item_type |
|
46
49
|
steps %Q{Then the response status should be "404"}
|
47
50
|
end
|
48
51
|
|
49
|
-
Then(
|
52
|
+
Then("the request fail(s/ed) because (the )it/resource/{item_type} is/was not allowed") do
|
50
53
|
steps %Q{Then the response status should be "405"}
|
51
54
|
end
|
52
55
|
|
53
|
-
Then(
|
56
|
+
Then("the request fail(s/ed) because there is/was/has a conflict( with {item_type})") do | item_type |
|
54
57
|
steps %Q{Then the response status should be "409"}
|
55
58
|
end
|
56
59
|
|
57
|
-
Then(
|
60
|
+
Then("the request fail(s/ed) because (the )it/resource/{item_type} is/was/has gone") do | item_type |
|
58
61
|
steps %Q{Then the response status should be "410"}
|
59
62
|
end
|
60
63
|
|
61
|
-
Then(
|
64
|
+
Then("the request fail(s/ed) because (the )it/resource/{item_type} is/was not implemented") do | item_type |
|
62
65
|
steps %Q{Then the response status should be "501"}
|
63
66
|
end
|
@@ -1,90 +1,24 @@
|
|
1
1
|
require 'active_support/inflector'
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
FIELD_NAME_SYNONYM = %q{[\w\s]+|`[^`]*`}
|
8
|
-
FEWER_MORE_THAN_SYNONYM = %q{(?:fewer|less|more) than|at (?:least|most)}
|
9
|
-
INT_AS_WORDS_SYNONYM = %q{zero|one|two|three|four|five|six|seven|eight|nine|ten}
|
3
|
+
HAVE_ALTERNATION = "has/have/having/contain/contains/containing/with"
|
4
|
+
RESOURCE_NAME_SYNONYM = '\w+\b(?:\s+\w+\b)*?'
|
5
|
+
FIELD_NAME_SYNONYM = "#{RESOURCE_NAME_SYNONYM}|`[^`]*`"
|
6
|
+
MAXIMAL_FIELD_NAME_SYNONYM = '\w+\b(?:\s+\w+\b)*|`[^`]*`'
|
10
7
|
|
11
8
|
ParameterType(
|
12
|
-
name: '
|
13
|
-
regexp:
|
14
|
-
transformer: -> (s) {
|
9
|
+
name: 'resource_name',
|
10
|
+
regexp: /#{RESOURCE_NAME_SYNONYM}/,
|
11
|
+
transformer: -> (s) { get_resource(s) },
|
15
12
|
use_for_snippets: false
|
16
13
|
)
|
17
14
|
|
18
15
|
ParameterType(
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
)
|
23
|
-
|
24
|
-
ParameterType(
|
25
|
-
name: 'fewer_more_than',
|
26
|
-
regexp: /#{FEWER_MORE_THAN_SYNONYM}/,
|
27
|
-
transformer: -> (s) { to_compare(s) }
|
28
|
-
)
|
29
|
-
|
30
|
-
ParameterType(
|
31
|
-
name: 'list_has_count',
|
32
|
-
regexp: /a|an|(?:(#{FEWER_MORE_THAN_SYNONYM})\s+)?(#{INT_AS_WORDS_SYNONYM})/,
|
33
|
-
transformer: -> (count_mod, count) {
|
34
|
-
ListCountComparison.new(count_mod.nil? ? to_compare(count_mod) : CMP_EQUALS, count.nil? ? to_num(count) : 1)
|
35
|
-
},
|
16
|
+
name: 'field_name',
|
17
|
+
regexp: /#{FIELD_NAME_SYNONYM}/,
|
18
|
+
transformer: -> (s) { ResponseField.new(s) },
|
36
19
|
use_for_snippets: false
|
37
20
|
)
|
38
21
|
|
39
|
-
CMP_LESS_THAN = '<'
|
40
|
-
CMP_MORE_THAN = '>'
|
41
|
-
CMP_AT_LEAST = '>='
|
42
|
-
CMP_AT_MOST = '<='
|
43
|
-
CMP_EQUALS = '='
|
44
|
-
|
45
|
-
# take a number modifier string (fewer than, less than, etc) and return an operator '<', etc
|
46
|
-
def to_compare(compare)
|
47
|
-
return case compare
|
48
|
-
when 'fewer than' then CMP_LESS_THAN
|
49
|
-
when 'less than' then CMP_LESS_THAN
|
50
|
-
when 'more than' then CMP_MORE_THAN
|
51
|
-
when 'at least' then CMP_AT_LEAST
|
52
|
-
when 'at most' then CMP_AT_MOST
|
53
|
-
else CMP_EQUALS
|
54
|
-
end
|
55
|
-
end
|
56
|
-
|
57
|
-
# turn a comparison into a string
|
58
|
-
def compare_to_string(compare)
|
59
|
-
case compare
|
60
|
-
when CMP_LESS_THAN then 'fewer than '
|
61
|
-
when CMP_MORE_THAN then 'more than '
|
62
|
-
when CMP_AT_LEAST then 'at least '
|
63
|
-
when CMP_AT_MOST then 'at most '
|
64
|
-
when CMP_EQUALS then ''
|
65
|
-
else ''
|
66
|
-
end
|
67
|
-
end
|
68
|
-
|
69
|
-
# compare two numbers using the fewer_more_than optional modifier
|
70
|
-
def num_compare(type, left, right)
|
71
|
-
case type
|
72
|
-
when CMP_LESS_THAN then left < right
|
73
|
-
when CMP_MORE_THAN then left > right
|
74
|
-
when CMP_AT_MOST then left <= right
|
75
|
-
when CMP_AT_LEAST then left >= right
|
76
|
-
when CMP_EQUALS then left == right
|
77
|
-
else left == right
|
78
|
-
end
|
79
|
-
end
|
80
|
-
|
81
|
-
def to_num(num)
|
82
|
-
if /^(?:zero|one|two|three|four|five|six|seven|eight|nine|ten)$/.match(num)
|
83
|
-
return %w(zero one two three four five six seven eight nine ten).index(num)
|
84
|
-
end
|
85
|
-
return num.to_i
|
86
|
-
end
|
87
|
-
|
88
22
|
module Boolean; end
|
89
23
|
class TrueClass; include Boolean; end
|
90
24
|
class FalseClass; include Boolean; end
|
@@ -105,21 +39,21 @@ class String
|
|
105
39
|
end
|
106
40
|
end
|
107
41
|
|
108
|
-
class
|
109
|
-
def initialize(
|
110
|
-
@
|
111
|
-
@amount = amount
|
42
|
+
class ResponseField
|
43
|
+
def initialize(names)
|
44
|
+
@fields = get_fields(names)
|
112
45
|
end
|
113
46
|
|
114
|
-
def
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
47
|
+
def to_json_path()
|
48
|
+
return "#{get_root_data_key()}#{@fields.join('.')}"
|
49
|
+
end
|
50
|
+
|
51
|
+
def get_value(type)
|
52
|
+
return @response.get_as_type to_json_path(), parse_type(type)
|
53
|
+
end
|
54
|
+
|
55
|
+
def validate_value(value, regex)
|
56
|
+
raise %/Expected #{json_path} value '#{value}' to match regex: #{regex}\n#{@response.to_json_s}/ if (regex =~ value).nil?
|
123
57
|
end
|
124
58
|
end
|
125
59
|
|
@@ -151,10 +85,6 @@ def get_root_data_key()
|
|
151
85
|
return ENV.has_key?('data_key') && !ENV['data_key'].empty? ? "$.#{ENV['data_key']}." : "$."
|
152
86
|
end
|
153
87
|
|
154
|
-
def get_root_error_key()
|
155
|
-
return "$."
|
156
|
-
end
|
157
|
-
|
158
88
|
def get_json_path(names)
|
159
89
|
return "#{get_root_data_key()}#{get_fields(names).join('.')}"
|
160
90
|
end
|
@@ -200,7 +130,15 @@ def merge_arrays(a, b)
|
|
200
130
|
new_length = [a.length, b.length].max
|
201
131
|
new_array = Array.new(new_length)
|
202
132
|
new_length.times do |n|
|
203
|
-
|
133
|
+
if b[n].nil? then
|
134
|
+
new_array[n] = a[n]
|
135
|
+
else
|
136
|
+
if a[n].nil? then
|
137
|
+
new_array[n] = b[n]
|
138
|
+
else
|
139
|
+
new_array[n] = a[n].merge(b[n])
|
140
|
+
end
|
141
|
+
end
|
204
142
|
end
|
205
143
|
return new_array
|
206
144
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: alinta-cucumber-rest-bdd
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.5.
|
4
|
+
version: 0.5.16
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Harry Bragg
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2018-
|
12
|
+
date: 2018-03-10 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: alinta-cucumber-api
|
@@ -62,8 +62,10 @@ extensions: []
|
|
62
62
|
extra_rdoc_files: []
|
63
63
|
files:
|
64
64
|
- lib/cucumber-rest-bdd.rb
|
65
|
+
- lib/cucumber-rest-bdd/data.rb
|
65
66
|
- lib/cucumber-rest-bdd/hash.rb
|
66
67
|
- lib/cucumber-rest-bdd/level.rb
|
68
|
+
- lib/cucumber-rest-bdd/list.rb
|
67
69
|
- lib/cucumber-rest-bdd/steps.rb
|
68
70
|
- lib/cucumber-rest-bdd/steps/auth.rb
|
69
71
|
- lib/cucumber-rest-bdd/steps/functional.rb
|