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

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
- 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