cucumber-rest-bdd 0.5.3 → 0.6.0.pre.183

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 2547016607fa79d879dbb328aaf2e022d1ac0fd6
4
- data.tar.gz: 7776a33cd86d41035071c0eeae544a801263dc17
2
+ SHA256:
3
+ metadata.gz: 2ace80d0cd81cb18bfbb26e4dc2dfe20ee084cd95408382020d8030f9b9a574b
4
+ data.tar.gz: 86de8eb49558ed6688b5326571eb8440966a565adca95024e72c432e327f1b95
5
5
  SHA512:
6
- metadata.gz: 97acb7ee3d866afd73c1aa28bb54dd8a39f5e5b3eab5c0ba7b6d21418d841dc8024102f6d760435d24cc0da84844d9025a6c3c2ade32e8e504ddb6774b8bde27
7
- data.tar.gz: a8e3153c7afa81a77ce5f326bc8a9f3c7b5ea27a0ea83a42df24603599c00c044efc99b609187eb83df1b1f46520e78c8fc3fa09964294231ac9cd06e32c36f0
6
+ metadata.gz: c36991b0161ea4f86cade6e3aa163b5996feca2990114ca69ca54756c1763087fcf7fc50f0893fc4f1e05ca4062412d048a9d7fe8fb9610812c6cd91582a5677
7
+ data.tar.gz: 286eeddd1565590b32fd528e8e40b0c4ad183bdb78eccee72fa3af9c6e53fc67e5b2db848c2f7406a2869dc8b22a7fc62ecac76195e9b4683bda4367f7a2dcd9
@@ -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 "$."
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_list_field(level[:key])
47
+ when 'list' then get_list_field(level[:key])
48
+ end
49
+ raise %/Key not found: #{level[:key]} as #{levelKey} in #{data}/ if data.is_a?(Array) || !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) [^"]+?(?: 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 = []
@@ -26,14 +31,14 @@ class Level
26
31
 
27
32
  def hash
28
33
  hash = {}
29
- @urls.each{ |l| hash[get_parameter("#{get_parameter(l[:resource]).singularize}_id")] = l[:id] }
34
+ @urls.each{ |l| hash[get_field("#{get_field(l[:resource]).singularize}_id")] = l[:id] }
30
35
  hash
31
36
  end
32
37
 
33
38
  def last_hash
34
39
  last = @urls.last
35
40
  if !last.nil?
36
- key = get_parameter("#{get_parameter(last[:resource]).singularize}_id")
41
+ key = get_field("#{get_field(last[:resource]).singularize}_id")
37
42
  return {
38
43
  key => last[:id]
39
44
  }
@@ -0,0 +1,144 @@
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: -> (match) {
10
+ matches = /(?:(#{FEWER_MORE_THAN_SYNONYM})\s+)?(#{INT_AS_WORDS_SYNONYM}|\d+)/.match(match)
11
+ return ListCountComparison.new(matches[1], matches[2])
12
+ },
13
+ use_for_snippets: false
14
+ )
15
+
16
+ ParameterType(
17
+ name: 'list_nesting',
18
+ 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*)+},
19
+ transformer: -> (match) { ListNesting.new(match) },
20
+ use_for_snippets: false
21
+ )
22
+
23
+ class ListNesting
24
+ def initialize(match)
25
+ @match = match
26
+ # gets an array in the nesting format that nest_match_attributes understands to interrogate nested object and array data
27
+ grouping = []
28
+ nesting = match
29
+
30
+ 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})}
31
+ 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})}
32
+ while matches = minimalListRegex.match(nesting)
33
+ nextMatches = minimalListRegex.match(nesting[matches.end(0), nesting.length])
34
+ matches = maximalListRegex.match(nextMatches.nil? ? nesting : nesting[0, matches.end(0) + nextMatches.begin(0)])
35
+ nesting = nesting[matches.end(0), nesting.length]
36
+
37
+ if matches[1].nil? then
38
+ if matches[3].nil? then
39
+ level = {
40
+ type: 'single',
41
+ key: matches[4],
42
+ root: false
43
+ }
44
+ else
45
+ level = {
46
+ type: 'multiple',
47
+ key: matches[4],
48
+ comparison: ListCountComparison.new(matches[2], matches[3]),
49
+ root: false
50
+ }
51
+ end
52
+ else
53
+ level = {
54
+ type: 'list',
55
+ key: matches[4],
56
+ comparison: ListCountComparison.new(matches[2], matches[3]),
57
+ root: false
58
+ }
59
+ end
60
+ grouping.push(level)
61
+ end
62
+ @grouping = grouping.reverse
63
+ end
64
+
65
+ def push(node)
66
+ @grouping.push(node)
67
+ end
68
+
69
+ def match
70
+ return @match
71
+ end
72
+
73
+ def grouping
74
+ @grouping
75
+ end
76
+ end
77
+
78
+ class ListCountComparison
79
+
80
+ def initialize(type, amount)
81
+ @type = type.nil? ? CMP_EQUALS : to_compare(type)
82
+ @amount = amount.nil? ? 1 : to_num(amount)
83
+ end
84
+
85
+ def compare(actual)
86
+ case @type
87
+ when CMP_LESS_THAN then actual < @amount
88
+ when CMP_MORE_THAN then actual > @amount
89
+ when CMP_AT_MOST then actual <= @amount
90
+ when CMP_AT_LEAST then actual >= @amount
91
+ when CMP_EQUALS then actual == @amount
92
+ else actual == @amount
93
+ end
94
+ end
95
+
96
+ def type
97
+ return @type
98
+ end
99
+
100
+ def amount
101
+ return amount
102
+ end
103
+
104
+ # turn a comparison into a string
105
+ def compare_to_string()
106
+ case @type
107
+ when CMP_LESS_THAN then 'fewer than '
108
+ when CMP_MORE_THAN then 'more than '
109
+ when CMP_AT_LEAST then 'at least '
110
+ when CMP_AT_MOST then 'at most '
111
+ when CMP_EQUALS then 'exactly '
112
+ else ''
113
+ end
114
+ end
115
+
116
+ def to_string()
117
+ return compare_to_string() + ' ' + @amount.to_s
118
+ end
119
+ end
120
+
121
+ CMP_LESS_THAN = '<'
122
+ CMP_MORE_THAN = '>'
123
+ CMP_AT_LEAST = '>='
124
+ CMP_AT_MOST = '<='
125
+ CMP_EQUALS = '='
126
+
127
+ # take a number modifier string (fewer than, less than, etc) and return an operator '<', etc
128
+ def to_compare(compare)
129
+ return case compare
130
+ when 'fewer than' then CMP_LESS_THAN
131
+ when 'less than' then CMP_LESS_THAN
132
+ when 'more than' then CMP_MORE_THAN
133
+ when 'at least' then CMP_AT_LEAST
134
+ when 'at most' then CMP_AT_MOST
135
+ else CMP_EQUALS
136
+ end
137
+ end
138
+
139
+ def to_num(num)
140
+ if /^(?:zero|one|two|three|four|five|six|seven|eight|nine|ten)$/.match(num)
141
+ return %w(zero one two three four five six seven eight nine ten).index(num)
142
+ end
143
+ return num.to_i
144
+ end
@@ -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
@@ -7,41 +7,44 @@ require 'cucumber-rest-bdd/level'
7
7
  require 'cucumber-rest-bdd/hash'
8
8
  require 'easy_diff'
9
9
 
10
- Given(/^I am a client$/) do
10
+ GET_TYPES = %{(?:an?(?! list)|the)}%
11
+ WITH_ID = %{(?: with (?:key|id))? "([^"]*)"}%
12
+
13
+ Given("I am a client") do
11
14
  steps %Q{
12
15
  Given I send "application/json" and accept JSON
13
16
  }
14
17
  end
15
18
 
19
+ Given("I am issuing requests for {resource_name}") do |resource|
20
+ @urlbasepath = resource
21
+ end
22
+
16
23
  # GET
17
24
 
18
- When(/^I request (?:an?(?! list)|the) ([^"]+?)(?: with (?:key|id))? "([^"]*)"(#{LEVELS})?$/) do |resource, id, levels|
19
- resource_name = get_resource(resource)
20
- 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}")
21
27
  steps %Q{When I send a GET request to "#{url}"}
22
28
  end
23
29
 
24
- When(/^I request (?:an?(?! list)|the) (.+?)(?: with (?:key|id))? "([^"]*)"(#{LEVELS})? with:$/) do |resource, id, levels, params|
25
- resource_name = get_resource(resource)
26
- 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}")
27
32
  unless params.raw.empty?
28
- query = params.raw.map{|key, value| %/#{get_parameter(key)}=#{resolve(value)}/}.join("&")
33
+ query = params.raw.map{|key, value| %/#{get_field(key)}=#{resolve(value)}/}.join("&")
29
34
  url = "#{url}?#{query}"
30
35
  end
31
36
  steps %Q{When I send a GET request to "#{url}"}
32
37
  end
33
38
 
34
- When(/^I request a list of ([^:]+?)(#{LEVELS})?$/) do |resource, levels|
35
- resource_name = get_resource(resource)
36
- 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}")
37
41
  steps %Q{When I send a GET request to "#{url}"}
38
42
  end
39
43
 
40
- When(/^I request a list of (.+?)(#{LEVELS})? with:$/) do |resource, levels, params|
41
- resource_name = get_resource(resource)
42
- 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}")
43
46
  unless params.raw.empty?
44
- query = params.raw.map{|key, value| %/#{get_parameter(key)}=#{resolve(value)}/}.join("&")
47
+ query = params.raw.map{|key, value| %/#{get_field(key)}=#{resolve(value)}/}.join("&")
45
48
  url = "#{url}?#{query}"
46
49
  end
47
50
  steps %Q{When I send a GET request to "#{url}"}
@@ -49,19 +52,16 @@ end
49
52
 
50
53
  # DELETE
51
54
 
52
- When(/^I request to (?:delete|remove) the ([^"]+?) "([^"]*)"(#{LEVELS})?$/) do |resource, id, levels|
53
- resource_name = get_resource(resource)
54
- 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}")
55
57
  steps %Q{When I send a DELETE request to "#{url}"}
56
58
  end
57
59
 
58
60
  # POST
59
61
 
60
- When(/^I request to create an? ([^:]+?)(#{LEVELS})?$/) do |resource, levels|
61
- resource_name = get_resource(resource)
62
- level = Level.new(levels)
62
+ When("I request to create a/an/the {resource_name}{levels}") do |resource, levels|
63
63
  if ENV['set_parent_id'] == 'true'
64
- json = MultiJson.dump(level.last_hash)
64
+ json = MultiJson.dump(levels.last_hash)
65
65
  steps %Q{
66
66
  When I set JSON request body to:
67
67
  """
@@ -69,17 +69,15 @@ When(/^I request to create an? ([^:]+?)(#{LEVELS})?$/) do |resource, levels|
69
69
  """
70
70
  }
71
71
  end
72
- url = get_url("#{level.url}#{resource_name}")
72
+ url = get_url("#{levels.url}#{resource}")
73
73
  steps %Q{When I send a POST request to "#{url}"}
74
74
  end
75
75
 
76
- When(/^I request to create an? ((?!<.+?(?: for | in | on ))[^"]+?)(#{LEVELS})? with:$/) do |resource, levels, params|
77
- resource_name = get_resource(resource)
76
+ When("I request to create a/an/the {resource_name}{levels} with:") do |resource, levels, params|
78
77
  request_hash = get_attributes(params.hashes)
79
- level = Level.new(levels)
80
- 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'
81
79
  json = MultiJson.dump(request_hash)
82
- url = get_url("#{level.url}#{resource_name}")
80
+ url = get_url("#{levels.url}#{resource}")
83
81
  steps %Q{
84
82
  When I set JSON request body to:
85
83
  """
@@ -91,11 +89,9 @@ end
91
89
 
92
90
  # PUT
93
91
 
94
- When(/^I request to (?:create|replace|set) (?:an?|the) ((?![^"]+?(?: for | in | on ))[^"]+?)(?: with (?:key|id))? "([^"]+)"(#{LEVELS})?$/) do |resource, id, levels|
95
- resource_name = get_resource(resource)
96
- 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|
97
93
  if ENV['set_parent_id'] == 'true'
98
- json = MultiJson.dump(level.last_hash)
94
+ json = MultiJson.dump(levels.last_hash)
99
95
  steps %Q{
100
96
  When I set JSON request body to:
101
97
  """
@@ -103,19 +99,17 @@ When(/^I request to (?:create|replace|set) (?:an?|the) ((?![^"]+?(?: for | in |
103
99
  """
104
100
  }
105
101
  end
106
- url = get_url("#{level.url}#{resource_name}/#{id}")
102
+ url = get_url("#{levels.url}#{resource}/#{id}")
107
103
  steps %Q{
108
104
  When I send a PUT request to "#{url}"
109
105
  }
110
106
  end
111
107
 
112
- When(/^I request to (?:create|replace|set) (?:an?|the) ((?![^"]+?(?: for | in | on ))[^"]+?)(?: with (?:key|id))? "([^"]+)"(#{LEVELS})? (?:with|to):$/) do |resource, id, levels, params|
113
- 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|
114
109
  request_hash = get_attributes(params.hashes)
115
- level = Level.new(levels)
116
- 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'
117
111
  json = MultiJson.dump(request_hash)
118
- url = get_url("#{level.url}#{resource_name}/#{id}")
112
+ url = get_url("#{levels.url}#{resource}/#{id}")
119
113
  steps %Q{
120
114
  When I set JSON request body to:
121
115
  """
@@ -127,11 +121,10 @@ end
127
121
 
128
122
  # PATCH
129
123
 
130
- When(/^I request to modify the ((?![^"]+?(?: for | in | on ))[^"]+?)(?: with (?:key|id))? "([^"]+)"(#{LEVELS})? with:$/) do |resource, id, levels, params|
131
- 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|
132
125
  request_hash = get_attributes(params.hashes)
133
126
  json = MultiJson.dump(request_hash)
134
- url = get_url("#{Level.new(levels).url}#{resource_name}/#{id}")
127
+ url = get_url("#{levels.url}#{resource}/#{id}")
135
128
  steps %Q{
136
129
  When I set JSON request body to:
137
130
  """
@@ -143,10 +136,10 @@ end
143
136
 
144
137
  # value capture
145
138
 
146
- When(/^I save (?:attribute )?"([^"]+)"$/) do |attribute|
139
+ When("I save (attribute ){string}") do |attribute|
147
140
  steps %Q{When I grab "#{get_json_path(attribute)}" as "#{attribute}"}
148
141
  end
149
142
 
150
- When(/^I save (?:attribute )?"([^"]+)" to "([^"]+)"$/) do |attribute, ref|
143
+ When("I save (attribute ){string} to {string}") do |attribute, ref|
151
144
  steps %Q{When I grab "#{get_json_path(attribute)}" as "#{ref}"}
152
145
  end