cucumber-rest-bdd 0.6.0 → 0.6.1

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