cucumber-rest-bdd 0.6.0 → 0.6.1

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
2
  SHA256:
3
- metadata.gz: d93393a0b495acb4bed9d413ce6025fad8a6df7985d63045ef5f47c5b6090aa4
4
- data.tar.gz: 5739f55f3e47882cccd85430578441789b019e2e5a9119ec95882db47396041f
3
+ metadata.gz: 51002c5c754272c8adb1452953d14ffba1a92141d11cf07b3a817c098b82bf7d
4
+ data.tar.gz: 519573e43b60171abb392429f9872e32c9377b66470dc51043ea7af6fbb1c068
5
5
  SHA512:
6
- metadata.gz: fc4804e246337e0f8c943e4bba0cab89b9dcfd02833a893e1723644521b729024cc39ef1393ef6a06c5aca2ff0b4585f010ec005fbbc02aa20bd47adebb22282
7
- data.tar.gz: 769fcc221501222f5af0b240193ba70c20c882976fe11f366a72c6d81b0525362ec7bcf4ae1bfce459f5413e6fb6bc2ca28b17bd1e0f69ae5ade5e13a8d7fa5f
6
+ metadata.gz: 179a7d97d8cf964fbc123ab72eb71e12111054e1bde7f9c05a443b3a191beb61d4142447980307400eea099f2140db3a10fd91105c650a0c0a0811cd5854c6f7
7
+ data.tar.gz: 48a01c37577e37bf9992bbadef807e10982ddfcc21b204278b11d9d9d5c8592bfd7a85fc27d41e757a2ab2cc50de3ead89ba9e491b7205a720cd531554996fd1
@@ -2,51 +2,77 @@ require 'cucumber-rest-bdd/types'
2
2
 
3
3
  # gets the relevant key for the response based on the first key element
4
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
5
+ error_key = ENV['error_key']
6
+ if error_key && !error_key.empty? && grouping.count > 1 && grouping[-2][:key].singularize == error_key
7
+ '$.'
8
+ else
9
+ root_data_key
10
+ end
11
11
  end
12
12
 
13
- # top level has 2 children with an item containing at most three fish with attributes:
13
+ # top level has 2 children
14
+ # with an item containing
15
+ # at most three fish with attributes:
14
16
  #
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}]
17
+ # nesting = [
18
+ # {key=fish,count=3,count_mod='<=',type=multiple},
19
+ # {key=item,type=single},
20
+ # {key=children,type=multiple,count=2,count_mod='='},
21
+ # {root=true,type=single}
22
+ # ]
16
23
  #
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
24
+ # returns true if the expected data is contained within the data based on the
25
+ # nesting information
26
+ def nest_match_attributes(data, nesting, expected, match_value)
27
+ # puts data.inspect, nesting.inspect, expected.inspect, match_value.inspect
28
+ return false unless data
29
+ return data.deep_include?(expected) if !match_value && nesting.empty?
30
+ return data.include?(expected) if match_value && nesting.empty?
31
+
32
+ local_nesting = nesting.dup
33
+ level = local_nesting.pop
34
+ child_data = get_child_data(level, data)
35
+
36
+ nest_get_match(level, child_data, local_nesting, expected, match_value)
37
+ end
38
+
39
+ # nest_get_match returns true if the child data matches the expected data
40
+ def nest_get_match(level, child_data, local_nesting, expected, match_value)
41
+ case level[:type]
42
+ when 'single' then
43
+ nest_match_attributes(child_data, local_nesting, expected, match_value)
44
+ when 'multiple' then
45
+ child_check(level, child_data, local_nesting, expected, match_value)
46
+ when 'list' then
47
+ child_is_list(level, child_data)
48
+ else
49
+ raise %(Unknown nested data type: #{level[:type]})
50
+ end
51
+ end
52
+
53
+ # check that all the children in child_data match the expected values
54
+ def child_check(level, child_data, local_nesting, expected, match_value)
55
+ matched = child_data.select do |item|
56
+ nest_match_attributes(item, local_nesting, expected, match_value)
57
+ end
58
+ level[:comparison].compare(matched.count)
59
+ end
60
+
61
+ # is the child a list, and does it match the comparison?
62
+ def child_is_list(level, child_data)
63
+ child_data.is_a?(Array) \
64
+ && (!level.key?(:comparison) \
65
+ || level[:comparison].compare(child_data.count))
38
66
  end
39
67
 
68
+ # parse the field and get the data for a given child
40
69
  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
70
+ return data.dup if level[:root]
71
+ level_key = case level[:type]
72
+ when 'single' then parse_field(level[:key])
73
+ when 'multiple', 'list' then parse_list_field(level[:key])
74
+ end
75
+ raise %(Key not found: #{level[:key]} as #{level_key} in #{data}) \
76
+ if data.is_a?(Array) || !data[level_key]
77
+ data[level_key]
52
78
  end
@@ -1,9 +1,10 @@
1
1
  require 'easy_diff'
2
2
 
3
+ # Adds deep_include? to the Hash class
3
4
  class Hash
4
- def deep_include?(other)
5
- diff = other.easy_diff(self)
6
- diff[0].delete_if { |k, v| v.empty? if v.is_a?(::Hash) }
7
- diff[0].empty?
8
- end
5
+ def deep_include?(other)
6
+ diff = other.easy_diff(self)
7
+ diff[0].delete_if { |_k, v| v.empty? if v.is_a?(::Hash) }
8
+ diff[0].empty?
9
+ end
9
10
  end
@@ -2,51 +2,52 @@ 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
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
9
  )
10
10
 
11
+ # Helper class when creating nested resources
11
12
  class Level
12
- @urls = []
13
+ @urls = []
13
14
 
14
- def initialize(levels)
15
- arr = []
16
- while matches = /^ (?:for|in|on) ([^"]+?)(?: with (?:key|id))? "([^"]*)"/.match(levels)
17
- levels = levels[matches[0].length, levels.length]
18
- item = {
19
- resource: get_resource(matches[1]),
20
- id: matches[2]
21
- }
22
- item[:id] = item[:id].to_i if item[:id].match(/^\d+$/)
23
- arr.append(item)
24
- end
25
- @urls = arr.reverse
15
+ def initialize(levels)
16
+ arr = []
17
+ while (matches = /^ (?:for|in|on) ([^"]+?)(?: with (?:key|id))? "([^"]*)"/
18
+ .match(levels))
19
+ levels = levels[matches[0].length, levels.length]
20
+ item = { resource: get_resource(matches[1]), id: matches[2] }
21
+ item[:id] = item[:id].to_i if item[:id] =~ /^\d+$/
22
+ arr.append(item)
26
23
  end
24
+ @urls = arr.reverse
25
+ end
27
26
 
28
- def url
29
- @urls.map{ |l| "#{l[:resource]}/#{l[:id]}/"}.join()
30
- end
27
+ def url
28
+ @urls.map { |l| "#{l[:resource]}/#{l[:id]}/" }.join
29
+ end
31
30
 
32
- def hash
33
- hash = {}
34
- @urls.each{ |l| hash[get_field("#{get_field(l[:resource]).singularize}_id")] = l[:id] }
35
- hash
31
+ def hash
32
+ hash = {}
33
+ @urls.each do |l|
34
+ hash[parse_field("#{parse_field(l[:resource]).singularize}_id")] = l[:id]
36
35
  end
36
+ hash
37
+ end
37
38
 
38
- def last_hash
39
- last = @urls.last
40
- if !last.nil?
41
- key = get_field("#{get_field(last[:resource]).singularize}_id")
42
- return {
43
- key => last[:id]
44
- }
45
- end
46
- return {}
39
+ def last_hash
40
+ last = @urls.last
41
+ unless last.nil?
42
+ key = parse_field("#{parse_field(last[:resource]).singularize}_id")
43
+ return {
44
+ key => last[:id]
45
+ }
47
46
  end
47
+ {}
48
+ end
48
49
 
49
- def to_s
50
- self.url
51
- end
50
+ def to_s
51
+ url
52
+ end
52
53
  end
@@ -1,144 +1,156 @@
1
1
  require 'cucumber-rest-bdd/types'
2
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}
3
+ FEWER_MORE_THAN_SYNONYM = '(?:fewer|less|more)\sthan|at\s(?:least|most)'.freeze
4
+ INT_AS_WORDS_SYNONYM = 'zero|one|two|three|four|five|six|seven|eight|nine|ten'
5
+ .freeze
5
6
 
6
7
  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
8
+ name: 'list_has_count',
9
+ regexp: /a|an|(?:(?:#{FEWER_MORE_THAN_SYNONYM})\s+)?(?:#{INT_AS_WORDS_SYNONYM}|\d+)/,
10
+ transformer: lambda { |match|
11
+ matches = /(?:(#{FEWER_MORE_THAN_SYNONYM})\s+)?
12
+ (#{INT_AS_WORDS_SYNONYM}|\d+)/x.match(match)
13
+ return ListCountComparison.new(matches[1], matches[2])
14
+ },
15
+ use_for_snippets: false
14
16
  )
15
-
16
17
  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
18
+ name: 'list_nesting',
19
+ # rubocop:disable Metrics/LineLength
20
+ regexp: /(?:(?:#{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*)+/,
21
+ # rubocop:enable Metrics/LineLength
22
+ transformer: ->(match) { ListNesting.new(match) },
23
+ use_for_snippets: false
21
24
  )
22
25
 
26
+ # Handle many children within objects or lists
23
27
  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
- }
28
+ def initialize(match)
29
+ @match = match
30
+ # gets an array in the nesting format that nest_match_attributes understands
31
+ # to interrogate nested object and array data
32
+ grouping = []
33
+ nesting = match
34
+
35
+ minimal_list = /(?:#{HAVE_ALTERNATION.split('/').join('|')})?\s*
36
+ (?:(a\slist\sof)\s+)?(?:a|an|(?:(#{FEWER_MORE_THAN_SYNONYM})
37
+ \s+)?(#{INT_AS_WORDS_SYNONYM}|\d+))\s+
38
+ (#{FIELD_NAME_SYNONYM})/x
39
+ maximal_list = /(?:#{HAVE_ALTERNATION.split('/').join('|')})?\s*
40
+ (?:(a\slist\sof)\s+)?(?:a|an|(?:(#{FEWER_MORE_THAN_SYNONYM})
41
+ \s+)?(#{INT_AS_WORDS_SYNONYM}|\d+))\s+
42
+ (#{MAXIMAL_FIELD_NAME_SYNONYM})/x
43
+ while (matches = minimal_list.match(nesting))
44
+ next_matches = minimal_list.match(nesting[matches.end(0), nesting.length])
45
+ to_match = if next_matches.nil?
46
+ nesting
47
+ else
48
+ nesting[0, matches.end(0) + next_matches.begin(0)]
49
+ end
50
+ matches = maximal_list.match(to_match)
51
+ nesting = nesting[matches.end(0), nesting.length]
52
+
53
+ level = if matches[1].nil?
54
+ if matches[3].nil?
55
+ {
56
+ type: 'single',
57
+ key: matches[4],
58
+ root: false
59
+ }
44
60
  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',
61
+ {
62
+ type: 'multiple',
55
63
  key: matches[4],
56
64
  comparison: ListCountComparison.new(matches[2], matches[3]),
57
65
  root: false
66
+ }
67
+ end
68
+ else
69
+ {
70
+ type: 'list',
71
+ key: matches[4],
72
+ comparison: ListCountComparison.new(matches[2], matches[3]),
73
+ root: false
58
74
  }
59
- end
60
- grouping.push(level)
61
- end
62
- @grouping = grouping.reverse
75
+ end
76
+ grouping.push(level)
63
77
  end
78
+ @grouping = grouping.reverse
79
+ end
64
80
 
65
- def push(node)
66
- @grouping.push(node)
67
- end
81
+ def push(node)
82
+ @grouping.push(node)
83
+ end
68
84
 
69
- def match
70
- return @match
71
- end
85
+ attr_reader :match
72
86
 
73
- def grouping
74
- @grouping
75
- end
87
+ attr_reader :grouping
76
88
  end
77
89
 
90
+ # Store a value and a comparison operator to determine if a provided number
91
+ # validates against it
78
92
  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)
93
+ def initialize(type, amount)
94
+ @type = type.nil? ? CMP_EQUALS : to_compare(type)
95
+ @amount = amount.nil? ? 1 : to_num(amount)
96
+ end
97
+
98
+ def compare(actual)
99
+ case @type
100
+ when CMP_LESS_THAN then actual < @amount
101
+ when CMP_MORE_THAN then actual > @amount
102
+ when CMP_AT_MOST then actual <= @amount
103
+ when CMP_AT_LEAST then actual >= @amount
104
+ when CMP_EQUALS then actual == @amount
105
+ else actual == @amount
83
106
  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
107
+ end
108
+
109
+ attr_reader :type
110
+
111
+ def amount
112
+ amount
113
+ end
114
+
115
+ # turn a comparison into a string
116
+ def compare_to_string
117
+ case @type
118
+ when CMP_LESS_THAN then 'fewer than '
119
+ when CMP_MORE_THAN then 'more than '
120
+ when CMP_AT_LEAST then 'at least '
121
+ when CMP_AT_MOST then 'at most '
122
+ when CMP_EQUALS then 'exactly '
123
+ else ''
94
124
  end
125
+ end
95
126
 
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
127
+ def to_string
128
+ compare_to_string + ' ' + @amount.to_s
129
+ end
119
130
  end
120
131
 
121
- CMP_LESS_THAN = '<'
122
- CMP_MORE_THAN = '>'
123
- CMP_AT_LEAST = '>='
124
- CMP_AT_MOST = '<='
125
- CMP_EQUALS = '='
132
+ CMP_LESS_THAN = '<'.freeze
133
+ CMP_MORE_THAN = '>'.freeze
134
+ CMP_AT_LEAST = '>='.freeze
135
+ CMP_AT_MOST = '<='.freeze
136
+ CMP_EQUALS = '='.freeze
126
137
 
127
- # take a number modifier string (fewer than, less than, etc) and return an operator '<', etc
138
+ # take a number modifier string (fewer than, less than, etc) and return an
139
+ # operator '<', etc
128
140
  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
141
+ case compare
142
+ when 'fewer than' then CMP_LESS_THAN
143
+ when 'less than' then CMP_LESS_THAN
144
+ when 'more than' then CMP_MORE_THAN
145
+ when 'at least' then CMP_AT_LEAST
146
+ when 'at most' then CMP_AT_MOST
147
+ else CMP_EQUALS
148
+ end
137
149
  end
138
150
 
139
151
  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
152
+ if num =~ /^(?:zero|one|two|three|four|five|six|seven|eight|nine|ten)$/
153
+ return %w[zero one two three four five six seven eight nine ten].index(num)
154
+ end
155
+ num.to_i
144
156
  end