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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 6c1c08f37c955dfab322c7f134f644e30503a01c
4
- data.tar.gz: dbd38aab4b7e14cf854eb9e95c3226bd811e0abd
3
+ metadata.gz: 90847911531d65c3733c4d551f71f2baa423a928
4
+ data.tar.gz: 6c63a087ab4bc38b81ae0f116d5eecc23aca0691
5
5
  SHA512:
6
- metadata.gz: 51fbffaf21822b8c00fe6ccc82c9b92338d143974ca842f58bf2bdcaecac181d1a0e9ed486fa2f7cee1a6e2002c8b42524135b2ac9ca14b4bb336f52e047b12b
7
- data.tar.gz: 45ff06672c504df19e3acc5332fdefa49a45cda5fc81a94dea8fa2bdf1ea0b8f848459e551ec2e93c3ee1dc07c97bd1b87d4e25fd38845fb8123eb417712cfc5
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
- LEVELS = %{((?: (?:for|in|on) #{RESOURCE_NAME}(?: with (?:key|id))? "[^"]*")*)?}%
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(/^I retrieve the API Management subscription key secret "(.*?)" from Azure Storage Vault "(.*?)" using tenant "(.*?)" with credentials "(.*?)" and "(.*?)"$/) do |secret_name, vault_name, tenant_id, client_id, client_secret|
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(/^I add the API Management key header$/) do
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(/^I retrieve the secret "(.*?)" from Azure Storage Vault "(.*?)" using tenant "(.*?)" with credentials "(.*?)" and "(.*?)"$/) do |secret_name, vault_name, tenant_id, client_id, client_secret|
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(/^I authenticate with Azure tenant "(.*?)" using client credentials "(.*?)" and "(.*?)"$/) do |tenant_id, client_id, client_secret|
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(/^I authenticate with "(.*?)" using client credentials "(.*?)" and "(.*?)"$/) do |url, client_id, client_secret|
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(/^I retrieve the secret "([^"]*)" from Azure Storage Vault "([^"]*)" using access token "([^"]*)"$/) do |secret_name, vault_name, access_token|
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(/^the response (?:should have|has a|has the) header "([^"]*)" with (?:a |the )?value "([^"]*)"$/) do |header, value|
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(/^the JSON response should have "([^"]*)" of type array with (\d+) entr(?:y|ies)$/) do |json_path, number|
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 #{number} items in array for path '#{json_path}', found: #{list.count}\n#{@response.to_json_s}/ if list.count != number.to_i
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(/^the JSON response should have "([^"]*)" of type array with ({fewer_more_than}) (\d+) entr(?:y|ies)$/) do |json_path, count_mod, number|
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(/^the JSON response should have "([^"]*)" of type (?:nill|null|nil)$/) do |json_path|
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(/^I am a client$/) do
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(/^I am issuing requests for #{RESOURCE_NAME_CAPTURE}$/) do |resource|
20
- @urlbasepath = get_resource(resource)
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(/^I request #{GET_TYPES} #{RESOURCE_NAME_CAPTURE}#{WITH_ID}#{LEVELS}$/) do |resource, id, levels|
26
- resource_name = get_resource(resource)
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(/^I request #{GET_TYPES} #{RESOURCE_NAME_CAPTURE}#{WITH_ID}#{LEVELS} with:$/) do |resource, id, levels, params|
32
- resource_name = get_resource(resource)
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(/^I request a list of #{RESOURCE_NAME_CAPTURE}#{LEVELS}$/) do |resource, levels|
42
- resource_name = get_resource(resource)
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(/^I request a list of #{RESOURCE_NAME_CAPTURE}#{LEVELS} with:$/) do |resource, levels, params|
48
- resource_name = get_resource(resource)
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(/^I request to (?:delete|remove) #{ARTICLE} #{RESOURCE_NAME_CAPTURE}#{WITH_ID}#{LEVELS}$/) do |resource, id, levels|
60
- resource_name = get_resource(resource)
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(/^I request to create #{ARTICLE} #{RESOURCE_NAME_CAPTURE}#{LEVELS}$/) do |resource, levels|
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(level.last_hash)
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("#{level.url}#{resource_name}")
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(/^I request to create #{ARTICLE} #{RESOURCE_NAME_CAPTURE}#{LEVELS} with:$/) do |resource, levels, params|
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
- level = Level.new(levels)
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("#{level.url}#{resource_name}")
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(/^I request to (?:create|replace|set) #{ARTICLE} #{RESOURCE_NAME_CAPTURE}#{WITH_ID}#{LEVELS}$/) do |resource, id, levels|
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(level.last_hash)
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("#{level.url}#{resource_name}/#{id}")
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(/^I request to (?:create|replace|set) #{ARTICLE} #{RESOURCE_NAME_CAPTURE}#{WITH_ID}#{LEVELS} (?:with|to):$/) do |resource, id, levels, params|
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
- level = Level.new(levels)
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("#{level.url}#{resource_name}/#{id}")
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(/^I request to modify #{ARTICLE} #{RESOURCE_NAME_CAPTURE}#{WITH_ID}#{LEVELS} with:$/) do |resource, id, levels, params|
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("#{Level.new(levels).url}#{resource_name}/#{id}")
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(/^I save (?:attribute )?"([^"]+)"$/) do |attribute|
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(/^I save (?:attribute )?"([^"]+)" to "([^"]+)"$/) do |attribute, ref|
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
- LIST_HAS_SYNONYM = %r{(?:a|an|(?:(#{FEWER_MORE_THAN_SYNONYM})\s+)?(#{INT_AS_WORDS_SYNONYM}|\d+))\s+(#{FIELD_NAME_SYNONYM})}
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(/^the response #{HAVE_SYNONYM} (#{FIELD_NAME_SYNONYM}) of type (datetime|guid)$/) do |names, type|
12
+ Then("the response #{HAVE_ALTERNATION} {field_name} of type {word}") do |field, type|
14
13
  regex = case type
15
- when 'datetime' then /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{1,3})?(?:[+|-]\d{2}:\d{2})?$/i
16
- when 'guid' then /^[{(]?[0-9A-F]{8}[-]?([0-9A-F]{4}[-]?){3}[0-9A-F]{12}[)}]?$/i
17
- else 'UNKNOWN'
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
- Then(/^the response #{HAVE_SYNONYM} (#{FIELD_NAME_SYNONYM}) of type (\w+) that matches "(.+)"$/) do |names, type, regex|
23
- validate_value(names, type, Regexp.new(regex))
19
+ type = 'string' if regex.nil?
20
+ field.validate_value(field.get_value(type), Regexp.new(regex))
24
21
  end
25
22
 
26
- def validate_value(names, type, regex)
27
- json_path = get_json_path(names)
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 at least #{count} items in array for path '#{get_root_data_key()}', found: #{list.count}\n#{@response.to_json_s}/ if !list_comparison.compare(list.count)
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(/^the response ((?:#{HAVE_SYNONYM}\s+#{LIST_HAS_SYNONYM_WITHOUT_CAPTURE}\s+)*)#{HAVE_SYNONYM} (?:the )?(?:following )?(?:data|error )?attributes:$/) do |nesting, attributes|
32
+ Then("the response( {list_nesting}) #{HAVE_ALTERNATION} (the )(following )attributes:") do |nesting, attributes|
39
33
  expected = get_attributes(attributes.hashes)
40
- groups = nesting
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(/^the response ((?:#{HAVE_SYNONYM}\s+#{LIST_HAS_SYNONYM_WITHOUT_CAPTURE}\s+)*)#{HAVE_SYNONYM} (?:the )?(?:following )?value "([^"]*)"$/) do |nesting, value|
42
+ Then("the response( {list_nesting}) #{HAVE_ALTERNATION} (the )(following )value {string}") do |nesting, value|
51
43
  expected = value
52
- groups = nesting
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? || !nest_match_value(data, grouping, expected)
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(/^the response ((?:#{HAVE_SYNONYM}\s+#{LIST_HAS_SYNONYM_WITHOUT_CAPTURE}\s+)+)$/) do |nesting|
63
- groups = nesting
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(/^#{LIST_HAS_SYNONYM} ((?:#{HAVE_SYNONYM}\s+#{LIST_HAS_SYNONYM_WITHOUT_CAPTURE}\s+)*)#{HAVE_SYNONYM} (?:the )?(?:following )?(?:data )?attributes:$/) do |count_mod, count, count_item, nesting, attributes|
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
- groups = nesting
76
- grouping = get_grouping(groups)
77
- grouping.push({
63
+ nesting.push({
78
64
  root: true,
79
65
  type: 'multiple',
80
- count: to_num(count),
81
- count_mod: to_compare(count_mod)
66
+ comparison: list_comparison
82
67
  })
83
- data = @response.get get_key(grouping)
84
- raise %/Expected #{compare_to_string(count_mod)}#{count} items in array with attributes for: #{nesting}\n#{expected.inspect}\n#{@response.to_json_s}/ if !nest_match_attributes(data, grouping, 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(/^#{LIST_HAS_SYNONYM} ((?:#{HAVE_SYNONYM}\s+#{LIST_HAS_SYNONYM_WITHOUT_CAPTURE}\s+)+)$/) do |count_mod, count, count_item, nesting|
88
- groups = nesting
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
- count: to_num(count),
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 %/Could not find a match for #{nesting}#{compare_to_string(num_mod)}#{num} #{item}\n#{@response.to_json_s}/ if !nest_match_attributes(data, grouping, {})
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
- Then(/^the request (?:is|was) successful$/) do
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(/^the request (?:is|was) redirected$/) do
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(/^(?:it|the request) fail(?:s|ed)$/) do
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(/^the (?!request)(?:.+?) (?:is|was) created$/) do
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(/^the request (?:is|was) successfully accepted$/) do
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(/^the request (?:is|was) successful and (?:no|an empty) response body is returned$/) do
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(/^(?:it|the request) fail(?:s|ed) because it (?:is|was) invalid$/) do
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(/^(?:it|the request) fail(?:s|ed) because (?:.+) (?:is|was|am|are) unauthori[sz]ed$/) do
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(/^(?:it|the request) fail(?:s|ed) because (?:.+) (?:is|was) forbidden$/) do
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(/^(?:it|the request) fail(?:s|ed) because the (?:.+) (?:is|was) not found$/) do
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(/^(?:it|the request) fail(?:s|ed) because it (?:is|was) not allowed$/) do
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(/^(?:it|the request) fail(?:s|ed) because there (?:is|was|has) a conflict(?: with .+)?$/) do
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(/^(?:it|the request) fail(?:s|ed) because the (?:.+) (?:is|was|has) gone$/) do
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(/^(?:it|the request) fail(?:s|ed) because the (?:.+) (?:is|was) not implemented$/) do
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
- HAVE_SYNONYM = %{(?:has|have|having|contain|contains|containing|with)}
4
- RESOURCE_NAME = '[\w\s]+'
5
- RESOURCE_NAME_CAPTURE = /([\w\s]+)/
6
- ARTICLE = %{(?:an?|the)}
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: 'field_name',
13
- regexp: /[\w\s]+|`[^`]*`/,
14
- transformer: -> (s) { get_fields(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
- name: 'int_as_words',
20
- regexp: /#{INT_AS_WORDS_SYNONYM}/,
21
- transformer: -> (s) { to_num(s) }
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 ListCountComparison
109
- def initialize(type, amount)
110
- @type = type
111
- @amount = amount
42
+ class ResponseField
43
+ def initialize(names)
44
+ @fields = get_fields(names)
112
45
  end
113
46
 
114
- def compare(actual)
115
- case @type
116
- when CMP_LESS_THAN then actual < @amount
117
- when CMP_MORE_THAN then actual > @amount
118
- when CMP_AT_MOST then actual <= @amount
119
- when CMP_AT_LEAST then actual >= @amount
120
- when CMP_EQUALS then actual == @amount
121
- else actual == @amount
122
- end
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
- new_array[n] = b[n] == nil ? a[n] : b[n]
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.15
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-02-27 00:00:00.000000000 Z
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