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 +4 -4
- data/lib/cucumber-rest-bdd/data.rb +66 -40
- data/lib/cucumber-rest-bdd/hash.rb +6 -5
- data/lib/cucumber-rest-bdd/level.rb +37 -36
- data/lib/cucumber-rest-bdd/list.rb +124 -112
- data/lib/cucumber-rest-bdd/steps/functional.rb +39 -14
- data/lib/cucumber-rest-bdd/steps/resource.rb +102 -80
- data/lib/cucumber-rest-bdd/steps/response.rb +131 -88
- data/lib/cucumber-rest-bdd/steps/status.rb +57 -51
- data/lib/cucumber-rest-bdd/types.rb +150 -141
- data/lib/cucumber-rest-bdd/url.rb +6 -5
- metadata +17 -15
@@ -2,101 +2,107 @@ require 'cucumber-api/response'
|
|
2
2
|
require 'cucumber-api/steps'
|
3
3
|
|
4
4
|
ParameterType(
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
5
|
+
name: 'item_type',
|
6
|
+
regexp: /([\w\s]+)/,
|
7
|
+
transformer: ->(s) { s },
|
8
|
+
use_for_snippets: false
|
9
9
|
)
|
10
10
|
|
11
|
-
Then(
|
12
|
-
|
11
|
+
Then('the request is/was successful') do
|
12
|
+
if @response.code < 200 || @response.code >= 300
|
13
|
+
raise %(Expected Successful response code 2xx but was #{@response.code})
|
14
|
+
end
|
13
15
|
end
|
14
16
|
|
15
|
-
Then(
|
16
|
-
|
17
|
+
Then('the request is/was redirected') do
|
18
|
+
if @response.code < 300 || @response.code >= 400
|
19
|
+
raise %(Expected redirected response code 3xx but was #{@response.code})
|
20
|
+
end
|
17
21
|
end
|
18
22
|
|
19
|
-
Then(
|
20
|
-
|
23
|
+
Then('the request fail(s/ed)') do
|
24
|
+
if @response.code < 400 || @response.code >= 600
|
25
|
+
raise %(Expected failed response code 4xx\/5xx but was #{@response.code})
|
26
|
+
end
|
21
27
|
end
|
22
28
|
|
23
|
-
Then(
|
24
|
-
|
29
|
+
Then('the request is/was successful and a/the resource is/was created') do
|
30
|
+
steps %(Then the response status should be "201")
|
25
31
|
end
|
26
32
|
|
27
|
-
Then(
|
28
|
-
|
33
|
+
Then('(the request is/was successful and )a/the {item_type} is/was created') do |_item_type|
|
34
|
+
steps %(Then the response status should be "201")
|
29
35
|
end
|
30
36
|
|
31
|
-
Then(
|
32
|
-
steps %
|
37
|
+
Then('the request is/was successfully accepted') do
|
38
|
+
steps %(Then the response status should be "202")
|
33
39
|
end
|
34
40
|
|
35
|
-
Then(
|
36
|
-
steps %
|
37
|
-
raise
|
41
|
+
Then('the request is/was successful and (an )empty/blank/no response body is/was returned') do
|
42
|
+
steps %(Then the response status should be "204")
|
43
|
+
raise %(Expected the request body to be empty) unless @response.body.empty?
|
38
44
|
end
|
39
45
|
|
40
|
-
Then(
|
41
|
-
|
46
|
+
Then('the request fail(s/ed) because (the )it/resource is/was invalid') do
|
47
|
+
steps %(Then the response status should be "400")
|
42
48
|
end
|
43
49
|
|
44
|
-
Then(
|
45
|
-
|
50
|
+
Then('the request fail(s/ed) because (the ){item_type} is/was invalid') do |_item_type|
|
51
|
+
steps %(Then the response status should be "400")
|
46
52
|
end
|
47
53
|
|
48
|
-
Then(
|
49
|
-
|
54
|
+
Then('the request fail(s/ed) because (the )it/resource is/was/am/are unauthorised/unauthorized') do
|
55
|
+
steps %(Then the response status should be "401")
|
50
56
|
end
|
51
57
|
|
52
|
-
Then(
|
53
|
-
|
58
|
+
Then('the request fail(s/ed) because (the ){item_type} is/was/am/are unauthorised/unauthorized') do |_item_type|
|
59
|
+
steps %(Then the response status should be "401")
|
54
60
|
end
|
55
61
|
|
56
|
-
Then(
|
57
|
-
|
62
|
+
Then('the request fail(s/ed) because (the )it/resource is/was forbidden') do
|
63
|
+
steps %(Then the response status should be "403")
|
58
64
|
end
|
59
65
|
|
60
|
-
Then(
|
61
|
-
|
66
|
+
Then('the request fail(s/ed) because (the ){item_type} is/was forbidden') do |_item_type|
|
67
|
+
steps %(Then the response status should be "403")
|
62
68
|
end
|
63
69
|
|
64
|
-
Then(
|
65
|
-
|
70
|
+
Then('the request fail(s/ed) because (the )it/resource is/was not found') do
|
71
|
+
steps %(Then the response status should be "404")
|
66
72
|
end
|
67
73
|
|
68
|
-
Then(
|
69
|
-
|
74
|
+
Then('the request fail(s/ed) because (the ){item_type} is/was not found') do |_item_type|
|
75
|
+
steps %(Then the response status should be "404")
|
70
76
|
end
|
71
77
|
|
72
|
-
Then(
|
73
|
-
|
78
|
+
Then('the request fail(s/ed) because (the )it/resource is/was not allowed') do
|
79
|
+
steps %(Then the response status should be "405")
|
74
80
|
end
|
75
81
|
|
76
|
-
Then(
|
77
|
-
|
82
|
+
Then('the request fail(s/ed) because (the ){item_type} is/was not allowed') do |_item_type|
|
83
|
+
steps %(Then the response status should be "405")
|
78
84
|
end
|
79
85
|
|
80
|
-
Then(
|
81
|
-
|
86
|
+
Then('the request fail(s/ed) because there is/was/has a conflict') do
|
87
|
+
steps %(Then the response status should be "409")
|
82
88
|
end
|
83
89
|
|
84
|
-
Then(
|
85
|
-
|
90
|
+
Then('the request fail(s/ed) because there is/was/has a conflict with {item_type}') do |_item_type|
|
91
|
+
steps %(Then the response status should be "409")
|
86
92
|
end
|
87
93
|
|
88
|
-
Then(
|
89
|
-
|
94
|
+
Then('the request fail(s/ed) because (the )it/resource is/was/has gone') do
|
95
|
+
steps %(Then the response status should be "410")
|
90
96
|
end
|
91
97
|
|
92
|
-
Then(
|
93
|
-
|
98
|
+
Then('the request fail(s/ed) because (the ){item_type} is/was/has gone') do |_item_type|
|
99
|
+
steps %(Then the response status should be "410")
|
94
100
|
end
|
95
101
|
|
96
|
-
Then(
|
97
|
-
|
102
|
+
Then('the request fail(s/ed) because (the )it/resource is/was not implemented') do
|
103
|
+
steps %(Then the response status should be "501")
|
98
104
|
end
|
99
105
|
|
100
|
-
Then(
|
101
|
-
|
106
|
+
Then('the request fail(s/ed) because (the ){item_type} is/was not implemented') do |_item_type|
|
107
|
+
steps %(Then the response status should be "501")
|
102
108
|
end
|
@@ -1,188 +1,197 @@
|
|
1
1
|
require 'active_support/inflector'
|
2
2
|
|
3
|
-
HAVE_ALTERNATION =
|
4
|
-
RESOURCE_NAME_SYNONYM = '\w+\b(?:\s+\w+\b)*?|`[^`]*`'
|
5
|
-
FIELD_NAME_SYNONYM = '\w+\b(?:(?:\s+:)?\s+\w+\b)*?|`[^`]*`'
|
6
|
-
MAXIMAL_FIELD_NAME_SYNONYM = '\w+\b(?:(?:\s+:)?\s+\w+\b)*|`[^`]*`'
|
3
|
+
HAVE_ALTERNATION = 'has/have/having/contain/contains/containing/with'.freeze
|
4
|
+
RESOURCE_NAME_SYNONYM = '\w+\b(?:\s+\w+\b)*?|`[^`]*`'.freeze
|
5
|
+
FIELD_NAME_SYNONYM = '\w+\b(?:(?:\s+:)?\s+\w+\b)*?|`[^`]*`'.freeze
|
6
|
+
MAXIMAL_FIELD_NAME_SYNONYM = '\w+\b(?:(?:\s+:)?\s+\w+\b)*|`[^`]*`'.freeze
|
7
7
|
|
8
8
|
ParameterType(
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
9
|
+
name: 'resource_name',
|
10
|
+
regexp: /#{RESOURCE_NAME_SYNONYM}/,
|
11
|
+
transformer: ->(s) { get_resource(s) },
|
12
|
+
use_for_snippets: false
|
13
13
|
)
|
14
14
|
|
15
15
|
ParameterType(
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
16
|
+
name: 'field_name',
|
17
|
+
regexp: /#{FIELD_NAME_SYNONYM}/,
|
18
|
+
transformer: ->(s) { ResponseField.new(s) },
|
19
|
+
use_for_snippets: false
|
20
20
|
)
|
21
21
|
|
22
|
+
# Add Boolean module to handle types
|
22
23
|
module Boolean; end
|
24
|
+
# True
|
23
25
|
class TrueClass; include Boolean; end
|
26
|
+
# False
|
24
27
|
class FalseClass; include Boolean; end
|
25
28
|
|
29
|
+
# Add Enum module to handle types
|
26
30
|
module Enum; end
|
31
|
+
# Enum is a type of string
|
27
32
|
class String; include Enum; end
|
28
33
|
|
34
|
+
# Handle parsing a field from a response
|
29
35
|
class ResponseField
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
36
|
+
def initialize(names)
|
37
|
+
@fields = split_fields(names)
|
38
|
+
end
|
39
|
+
|
40
|
+
def to_json_path
|
41
|
+
"#{root_data_key}#{@fields.join('.')}"
|
42
|
+
end
|
43
|
+
|
44
|
+
def get_value(response, type)
|
45
|
+
response.get_as_type to_json_path, parse_type(type)
|
46
|
+
end
|
47
|
+
|
48
|
+
def validate_value(response, value, regex)
|
49
|
+
raise "Expected #{json_path} value '#{value}' to match regex: #{regex}\n#{response.to_json_s}" \
|
50
|
+
if (regex =~ value).nil?
|
51
|
+
end
|
45
52
|
end
|
46
53
|
|
47
54
|
def parse_type(type)
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
55
|
+
replacements = {
|
56
|
+
/^numeric$/i => 'numeric',
|
57
|
+
/^int$/i => 'numeric',
|
58
|
+
/^long$/i => 'numeric',
|
59
|
+
/^number$/i => 'numeric',
|
60
|
+
/^decimal$/i => 'numeric',
|
61
|
+
/^double$/i => 'numeric',
|
62
|
+
/^bool$/i => 'boolean',
|
63
|
+
/^null$/i => 'nil_class',
|
64
|
+
/^nil$/i => 'nil_class',
|
65
|
+
/^string$/i => 'string',
|
66
|
+
/^text$/i => 'string'
|
67
|
+
}
|
68
|
+
type.tr(' ', '_')
|
69
|
+
replacements.each { |k, v| type.gsub!(k, v) }
|
70
|
+
type
|
64
71
|
end
|
65
72
|
|
66
73
|
def string_to_type(value, type)
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
74
|
+
replacements = {
|
75
|
+
/^numeric$/i => 'integer',
|
76
|
+
/^int$/i => 'integer',
|
77
|
+
/^long$/i => 'integer',
|
78
|
+
/^number$/i => 'integer',
|
79
|
+
/^decimal$/i => 'float',
|
80
|
+
/^double$/i => 'float',
|
81
|
+
/^bool$/i => 'boolean',
|
82
|
+
/^null$/i => 'nil_class',
|
83
|
+
/^nil$/i => 'nil_class',
|
84
|
+
/^string$/i => 'string',
|
85
|
+
/^text$/i => 'string'
|
86
|
+
}
|
87
|
+
type.tr(' ', '_')
|
88
|
+
replacements.each { |k, v| type.gsub!(k, v) }
|
89
|
+
type = type.camelize.constantize
|
90
|
+
# cannot use 'case type' which checks for instances of a type rather than type equality
|
91
|
+
if type == Boolean then !(value =~ /true|yes/i).nil?
|
92
|
+
elsif type == Enum then value.upcase.tr(' ', '_')
|
93
|
+
elsif type == Float then value.to_f
|
94
|
+
elsif type == Integer then value.to_i
|
95
|
+
elsif type == NilClass then nil
|
96
|
+
else value
|
97
|
+
end
|
91
98
|
end
|
92
99
|
|
93
100
|
def get_resource(name)
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
+
if name[0] == '`' && name[-1] == '`'
|
102
|
+
name = name[1..-2]
|
103
|
+
else
|
104
|
+
name = name.parameterize
|
105
|
+
name = ENV.key?('resource_single') && ENV['resource_single'] == 'true' ? name.singularize : name.pluralize
|
106
|
+
end
|
107
|
+
name
|
101
108
|
end
|
102
109
|
|
103
|
-
def
|
104
|
-
|
110
|
+
def root_data_key
|
111
|
+
ENV.key?('data_key') && !ENV['data_key'].empty? ? "$.#{ENV['data_key']}." : '$.'
|
105
112
|
end
|
106
113
|
|
107
|
-
def
|
108
|
-
|
114
|
+
def json_path(names)
|
115
|
+
"#{root_data_key}#{split_fields(names).join('.')}"
|
109
116
|
end
|
110
117
|
|
111
|
-
def
|
112
|
-
|
118
|
+
def split_fields(names)
|
119
|
+
names.split(':').map { |n| parse_field(n.strip) }
|
113
120
|
end
|
114
121
|
|
115
|
-
def
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
122
|
+
def parse_field(name)
|
123
|
+
if name[0] == '`' && name[-1] == '`'
|
124
|
+
name = name[1..-2]
|
125
|
+
elsif name[0] != '[' || name[-1] != ']'
|
126
|
+
separator = ENV.key?('field_separator') ? ENV['field_separator'] : '_'
|
127
|
+
name = name.parameterize(separator: separator)
|
128
|
+
name = name.camelize(:lower) if ENV.key?('field_camel') && ENV['field_camel'] == 'true'
|
129
|
+
end
|
130
|
+
name
|
124
131
|
end
|
125
132
|
|
126
|
-
def
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
133
|
+
def parse_list_field(name)
|
134
|
+
if name[0] == '`' && name[-1] == '`'
|
135
|
+
name = name[1..-2]
|
136
|
+
elsif name[0] != '[' || name[-1] != ']'
|
137
|
+
separator = ENV.key?('field_separator') ? ENV['field_separator'] : '_'
|
138
|
+
name = name.parameterize(separator: separator)
|
139
|
+
name = name.pluralize
|
140
|
+
name = name.camelize(:lower) if ENV.key?('field_camel') && ENV['field_camel'] == 'true'
|
141
|
+
end
|
142
|
+
name
|
136
143
|
end
|
137
144
|
|
138
|
-
def
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
145
|
+
def parse_attributes(hashes)
|
146
|
+
hashes.each_with_object({}) do |row, hash|
|
147
|
+
name = row['attribute']
|
148
|
+
value = row['value']
|
149
|
+
type = row['type']
|
150
|
+
value = resolve_functions(value)
|
151
|
+
value = resolve(value)
|
152
|
+
value.gsub!(/\\n/, "\n")
|
153
|
+
names = split_fields(name)
|
154
|
+
new_hash = names.reverse.inject(string_to_type(value, type)) { |a, n| add_to_hash(a, n) }
|
155
|
+
hash.deep_merge!(new_hash) { |_, old, new| new.is_a?(Array) ? merge_arrays(old, new) : new }
|
156
|
+
end
|
148
157
|
end
|
149
158
|
|
150
159
|
def resolve_functions(value)
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
end
|
160
|
+
value.gsub!(/\[([a-zA-Z0-9_]+)\]/) do |s|
|
161
|
+
s.gsub!(/[\[\]]/, '')
|
162
|
+
case s.downcase
|
163
|
+
when 'datetime'
|
164
|
+
Time.now.strftime('%Y%m%d%H%M%S')
|
165
|
+
else
|
166
|
+
raise 'Unrecognised function ' + s + '?'
|
159
167
|
end
|
160
|
-
|
168
|
+
end
|
169
|
+
value
|
161
170
|
end
|
162
171
|
|
163
|
-
def add_to_hash(
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
172
|
+
def add_to_hash(hash, node)
|
173
|
+
result = nil
|
174
|
+
if node[0] == '[' && node[-1] == ']'
|
175
|
+
array = Array.new(node[1..-2].to_i + 1)
|
176
|
+
array[node[1..-2].to_i] = hash
|
177
|
+
result = array
|
178
|
+
end
|
179
|
+
!result.nil? ? result : { node => hash }
|
171
180
|
end
|
172
181
|
|
173
|
-
def merge_arrays(
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
182
|
+
def merge_arrays(first, second)
|
183
|
+
new_length = [first.length, second.length].max
|
184
|
+
new_array = Array.new(new_length)
|
185
|
+
new_length.times do |n|
|
186
|
+
new_array[n] = if second[n].nil?
|
187
|
+
first[n]
|
188
|
+
else
|
189
|
+
new_array[n] = if first[n].nil?
|
190
|
+
second[n]
|
191
|
+
else
|
192
|
+
first[n].merge(second[n])
|
193
|
+
end
|
194
|
+
end
|
195
|
+
end
|
196
|
+
new_array
|
188
197
|
end
|