cucumber-blinkbox 0.3.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +15 -0
- data/VERSION +1 -0
- data/lib/cucumber/blinkbox.rb +6 -0
- data/lib/cucumber/blinkbox/data_dependencies.rb +31 -0
- data/lib/cucumber/blinkbox/environment.rb +35 -0
- data/lib/cucumber/blinkbox/formatter/html.rb +124 -0
- data/lib/cucumber/blinkbox/requests.rb +92 -0
- data/lib/cucumber/blinkbox/response_validation.rb +135 -0
- data/lib/cucumber/blinkbox/responses.rb +17 -0
- data/lib/cucumber/blinkbox/subjects.rb +28 -0
- data/spec/cucumber/blinkbox/requests_spec.rb +56 -0
- data/spec/spec_helper.rb +6 -0
- metadata +169 -0
checksums.yaml
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
---
|
2
|
+
!binary "U0hBMQ==":
|
3
|
+
metadata.gz: !binary |-
|
4
|
+
Y2MyODE2NDZjYTE4Y2VhNjNhN2VmNmM4MDhkY2EzMmU0MTIyYzNiNQ==
|
5
|
+
data.tar.gz: !binary |-
|
6
|
+
NmNkZGExNjNkNTcyYWJlZmUxZTVlMjM2YTdiZTVmNzZlYTIwNjQzMQ==
|
7
|
+
SHA512:
|
8
|
+
metadata.gz: !binary |-
|
9
|
+
ZDI5MTIwZGJlOTQ2NzkyZDU5YzczYzFhNDczYWJiNjU1M2I0MjM4NzNkYjc4
|
10
|
+
ZjkyNGU1MTIwZjM1OTVmNTM0YjA3NjMzOTIzZjIzNzNiNTY5Y2NiOGMyMDEy
|
11
|
+
Mzg2MmE1MTgwOWVmNjZjZmMzMTYyNGQxZmY4NWE2MDQyNGJlOWU=
|
12
|
+
data.tar.gz: !binary |-
|
13
|
+
YjU4ZGI4YzhkYjZmMDlhNmMxM2YwNTdhYmQzNDRjZjJhZTk2MTMxNDVhYjI4
|
14
|
+
OTY5MWI3ZjM5YjQ1MTFhYzViYzNlYmQ4M2E3NWVhYWNmMTIzZTE2Y2Y3MGYy
|
15
|
+
ZGY5ZThjMzBlYWJjOGI2MjI2M2NkY2IyMDQ3ODgzYTdmMDRkMzU=
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.3.3
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module KnowsAboutDataDependencies
|
2
|
+
::TEST_CONFIG ||= {}
|
3
|
+
|
4
|
+
def self.extended(base)
|
5
|
+
base.instance_eval do
|
6
|
+
path = TEST_CONFIG["data.yml"] || "config/data.yml"
|
7
|
+
raise "The data dependencies file does not exist at #{path}" unless File.exist?(path)
|
8
|
+
@data_dependencies = YAML.load_file(path)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def data_for_a(object, which: nil, but_isnt: nil, instances: nil)
|
13
|
+
raise ArgumentError, "Please specify a condition using `which:`" if which.nil?
|
14
|
+
data = @data_dependencies[object.to_s][which] rescue nil
|
15
|
+
|
16
|
+
if data.respond_to? :sample
|
17
|
+
data.delete_if { |item| item == but_isnt } if but_isnt
|
18
|
+
if instances
|
19
|
+
pending "Test error: There are not enough examples defined for a #{object} which #{which}" unless data.size >= instances
|
20
|
+
data = data.sample(instances)
|
21
|
+
else
|
22
|
+
data = data.sample
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
pending "Test error: There is no data dependency defined for a #{object} which #{which}" unless data
|
27
|
+
data
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
World(KnowsAboutDataDependencies)
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module KnowsAboutTheEnvironment
|
2
|
+
::TEST_CONFIG ||= {}
|
3
|
+
|
4
|
+
class EnvStruct
|
5
|
+
def initialize(env)
|
6
|
+
@env = env
|
7
|
+
end
|
8
|
+
def [](key)
|
9
|
+
value = @env[key.to_s]
|
10
|
+
value.is_a?(Hash) ? EnvStruct.new(value) : value
|
11
|
+
end
|
12
|
+
def method_missing(name, *args)
|
13
|
+
key = name.to_s.tr("_", " ").downcase
|
14
|
+
self[key]
|
15
|
+
end
|
16
|
+
def inspect
|
17
|
+
@env.inspect
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.extended(base)
|
22
|
+
base.instance_eval do
|
23
|
+
path = TEST_CONFIG["environments.yml"] || "config/environments.yml"
|
24
|
+
raise "The environments file does not exist at #{path}" unless File.exist?(path)
|
25
|
+
env = YAML.load_file(path)[TEST_CONFIG["server"].downcase]
|
26
|
+
raise "Environment '#{TEST_CONFIG["server"]}' is not defined in environments.yml" if env.nil?
|
27
|
+
@test_env = EnvStruct.new(env)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def test_env
|
32
|
+
@test_env
|
33
|
+
end
|
34
|
+
end
|
35
|
+
World(KnowsAboutTheEnvironment)
|
@@ -0,0 +1,124 @@
|
|
1
|
+
require 'cucumber/formatter/html'
|
2
|
+
|
3
|
+
module Cucumber
|
4
|
+
module Blinkbox
|
5
|
+
module Formatter
|
6
|
+
class Html < Cucumber::Formatter::Html
|
7
|
+
|
8
|
+
def timestamp
|
9
|
+
Time.now.strftime("%b %e %H:%M:%S")
|
10
|
+
end
|
11
|
+
|
12
|
+
def append_timestamp_to(name)
|
13
|
+
"#{name} [#{timestamp}]"
|
14
|
+
end
|
15
|
+
|
16
|
+
def prefix_with_timestamp(name)
|
17
|
+
"#{timestamp} #{name}"
|
18
|
+
end
|
19
|
+
|
20
|
+
#override inline JS in the default html formatter to always show cucumber tags regardless expand/collapse state of the scenarios
|
21
|
+
#also make all scenarios collapsed on page load
|
22
|
+
def inline_js_content
|
23
|
+
<<-EOF
|
24
|
+
|
25
|
+
SCENARIOS = "h3[id^='scenario_'],h3[id^=background_]";
|
26
|
+
|
27
|
+
$(document).ready(function() {
|
28
|
+
$(SCENARIOS).css('cursor', 'pointer');
|
29
|
+
$(SCENARIOS).click(function() {
|
30
|
+
$(this).siblings().toggle(250, function(){
|
31
|
+
$(this).siblings(".tag").show();
|
32
|
+
$(this).siblings(".examples").show();
|
33
|
+
$(this).siblings(".examples").children().show();
|
34
|
+
});
|
35
|
+
});
|
36
|
+
|
37
|
+
$("#collapser").css('cursor', 'pointer');
|
38
|
+
$("#collapser").click(function() {
|
39
|
+
$(SCENARIOS).siblings().hide();
|
40
|
+
$(SCENARIOS).siblings(".tag").show();
|
41
|
+
});
|
42
|
+
|
43
|
+
$("#expander").css('cursor', 'pointer');
|
44
|
+
$("#expander").click(function() {
|
45
|
+
$(SCENARIOS).siblings().show();
|
46
|
+
});
|
47
|
+
|
48
|
+
$(SCENARIOS).siblings().hide();
|
49
|
+
$(SCENARIOS).siblings(".tag").show();
|
50
|
+
})
|
51
|
+
|
52
|
+
function moveProgressBar(percentDone) {
|
53
|
+
$("cucumber-header").css('width', percentDone +"%");
|
54
|
+
}
|
55
|
+
function makeRed(element_id) {
|
56
|
+
$('#'+element_id).css('background', '#C40D0D');
|
57
|
+
$('#'+element_id).css('color', '#FFFFFF');
|
58
|
+
}
|
59
|
+
function makeYellow(element_id) {
|
60
|
+
$('#'+element_id).css('background', '#FAF834');
|
61
|
+
$('#'+element_id).css('color', '#000000');
|
62
|
+
}
|
63
|
+
|
64
|
+
EOF
|
65
|
+
end
|
66
|
+
|
67
|
+
#log scenario name so that console log is more readable
|
68
|
+
#also track the position in the all logs array from which out for current scenario starts
|
69
|
+
def scenario_name(keyword, name, file_colon_line, source_indent)
|
70
|
+
name = append_timestamp_to(name)
|
71
|
+
super(keyword, name, file_colon_line, source_indent)
|
72
|
+
end
|
73
|
+
|
74
|
+
def step_name(keyword, step_match, status, source_indent, background, file_colon_line)
|
75
|
+
name = super
|
76
|
+
append_timestamp_to(name)
|
77
|
+
end
|
78
|
+
|
79
|
+
def build_step(keyword, step_match, status)
|
80
|
+
super
|
81
|
+
@builder.div(:class => 'step_file') do |div|
|
82
|
+
@builder.span do
|
83
|
+
@builder << append_timestamp_to("")
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
#override to set color of pending exceptions backtraces to Yellow instead of Red (and thus not make the whole scenario Red)
|
89
|
+
def after_table_row(table_row)
|
90
|
+
return if @hide_this_step
|
91
|
+
print_table_row_messages
|
92
|
+
@builder << '</tr>'
|
93
|
+
if table_row.exception
|
94
|
+
if table_row.exception.instance_of?(Cucumber::Pending)
|
95
|
+
@builder.tr do
|
96
|
+
@builder.td(:colspan => @col_index.to_s, :class => 'pending') do
|
97
|
+
@builder.pre do |pre|
|
98
|
+
pre << h(format_exception(table_row.exception))
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
set_scenario_color_pending
|
103
|
+
else
|
104
|
+
@builder.tr do
|
105
|
+
@builder.td(:colspan => @col_index.to_s, :class => 'failed') do
|
106
|
+
@builder.pre do |pre|
|
107
|
+
pre << h(format_exception(table_row.exception))
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
set_scenario_color_failed
|
112
|
+
end
|
113
|
+
end
|
114
|
+
if @outline_row
|
115
|
+
@outline_row += 1
|
116
|
+
end
|
117
|
+
@step_number += 1
|
118
|
+
move_progress
|
119
|
+
end
|
120
|
+
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
@@ -0,0 +1,92 @@
|
|
1
|
+
module KnowsAboutApiRequests
|
2
|
+
CONTENT_TYPE = "application/vnd.blinkboxbooks.data.v1+json"
|
3
|
+
|
4
|
+
def http_client
|
5
|
+
@http ||= HTTPClient.new(TEST_CONFIG["proxy"])
|
6
|
+
# Ensure we're using the system SSL certs, as per other libraries (like HTTParty)
|
7
|
+
@http.ssl_config.set_trust_ca(OpenSSL::X509::DEFAULT_CERT_FILE)
|
8
|
+
@http.debug_dev = STDOUT if TEST_CONFIG["debug"]
|
9
|
+
#@http.reset_all
|
10
|
+
@http
|
11
|
+
end
|
12
|
+
|
13
|
+
def query
|
14
|
+
@query ||= {}
|
15
|
+
end
|
16
|
+
|
17
|
+
def request_headers(header = {})
|
18
|
+
@request_headers ||= {
|
19
|
+
"Accept" => CONTENT_TYPE
|
20
|
+
}
|
21
|
+
if @access_token
|
22
|
+
@request_headers["Authorization"] = "Bearer #{@access_token}"
|
23
|
+
else
|
24
|
+
@request_headers.delete("Authorization")
|
25
|
+
end
|
26
|
+
@request_headers.merge!(header)
|
27
|
+
@request_headers
|
28
|
+
end
|
29
|
+
|
30
|
+
def set_query_param(name, value)
|
31
|
+
if value.respond_to?(:each)
|
32
|
+
value.each { |v| set_query_param(name, v) }
|
33
|
+
else
|
34
|
+
name = name.camel_case
|
35
|
+
value = is_enum_param(name) ? value.snake_case(:upper) : value
|
36
|
+
current_value = query[name]
|
37
|
+
|
38
|
+
if current_value.nil?
|
39
|
+
query[name] = value
|
40
|
+
elsif current_value.is_a?(Array)
|
41
|
+
query[name] << value
|
42
|
+
else
|
43
|
+
query[name] = [current_value, value]
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def qualified_uri(server, path)
|
49
|
+
uri = test_env.servers[server.to_sym]
|
50
|
+
raise "Test Error: #{server} doesn't appear to be defined in the environments.yml" if uri.nil?
|
51
|
+
File.join(uri.to_s, path)
|
52
|
+
end
|
53
|
+
|
54
|
+
# request methods
|
55
|
+
|
56
|
+
def http_get(server, path, header = {})
|
57
|
+
uri = qualified_uri(server, path)
|
58
|
+
params = query if query.count > 0
|
59
|
+
@response = http_client.get(uri, query: params, header: request_headers(header), follow_redirect: true)
|
60
|
+
end
|
61
|
+
|
62
|
+
def http_post(server, path, body = {}, header = {})
|
63
|
+
uri = qualified_uri(server, path)
|
64
|
+
@response = http_client.post(uri, body: format_body(body), header: request_headers({"Content-Type" => CONTENT_TYPE}.merge(header)))
|
65
|
+
end
|
66
|
+
|
67
|
+
def http_put(server, path, body = {}, header = {})
|
68
|
+
uri = qualified_uri(server, path)
|
69
|
+
@response = http_client.put(uri, body: format_body(body), header: request_headers({"Content-Type" => CONTENT_TYPE}.merge(header)))
|
70
|
+
end
|
71
|
+
|
72
|
+
def http_delete(server, path, header = {})
|
73
|
+
uri = qualified_uri(server, path)
|
74
|
+
params = query if query.count > 0
|
75
|
+
@response = http_client.delete(uri, query: params, header: request_headers(header))
|
76
|
+
end
|
77
|
+
|
78
|
+
private
|
79
|
+
|
80
|
+
def format_body(body)
|
81
|
+
body.is_a?(Hash) ? JSON.dump(body) : body
|
82
|
+
end
|
83
|
+
|
84
|
+
# So that we don't have to put enum parameters in the Gherkin in SCREAMING_SNAKE_CASE this heuristic identifies
|
85
|
+
# enum parameters so that we can transform them, meaning an enum value like PURCHASE_DATE can be written in the
|
86
|
+
# Gherkin as "purchase date" but still be sent correctly. This heuristic will need updating over time.
|
87
|
+
def is_enum_param(name)
|
88
|
+
["bookmarkType", "order", "role"].include?(name)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
World(KnowsAboutApiRequests)
|
@@ -0,0 +1,135 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
module KnowsAboutResponseValidation
|
3
|
+
INFINITY = 1.0 / 0.0
|
4
|
+
|
5
|
+
# Parses a string into a data if that's the specified date type. This is needed for interpreting JSON markup as
|
6
|
+
# it doesn't have dates as a first-class concept so they should appear as specifically formatted strings.
|
7
|
+
def parse_and_validate_date_if_necessary(value, type)
|
8
|
+
# note: cannot use 'case type' as that expands to === which checks for instances of rather than type equality
|
9
|
+
if type == Date
|
10
|
+
expect(value).to match(/^\d{4}-\d{2}-\d{2}$/)
|
11
|
+
Date.strptime(value, "%Y-%m-%d")
|
12
|
+
elsif type == DateTime
|
13
|
+
expect(value).to match(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z$/)
|
14
|
+
DateTime.strptime(value, "%Y-%m-%dT%H:%M:%SZ")
|
15
|
+
else
|
16
|
+
value
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def validate_attribute(data, name, options = {})
|
21
|
+
type = options[:type]
|
22
|
+
type = type.constantize if type.respond_to?(:constantize) # allows it to be passed in as a string or a constant
|
23
|
+
expected_value = options[:is] || options[:value]
|
24
|
+
expected_value = expected_value.to_type(type) if expected_value.is_a?(String) && !type.nil?
|
25
|
+
expected_content = options[:contains]
|
26
|
+
|
27
|
+
value = parse_and_validate_date_if_necessary(data.deep_key(name), type)
|
28
|
+
|
29
|
+
begin
|
30
|
+
expect(value).to_not be_nil
|
31
|
+
expect(value).to be_a_kind_of(type) unless type.nil?
|
32
|
+
expect(value).to eq(expected_value) unless expected_value.nil?
|
33
|
+
expect(value).to match(/#{Regexp.escape(expected_content)}/) unless expected_content.nil?
|
34
|
+
yield value if block_given?
|
35
|
+
rescue => e
|
36
|
+
message = "'#{name}' is invalid:\n#{e.message}"
|
37
|
+
send((options[:warn_only] ? "puts" : "raise").to_sym, message)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def validate_entity(data, type, options = {})
|
42
|
+
warn_only = options[:warn_only] || []
|
43
|
+
validate_attribute(data, "type", type: String, warn_only: warn_only.include?(:type)) { |value| expect(value).to eq("urn:blinkboxbooks:schema:#{type}") }
|
44
|
+
validate_attribute(data, "guid", type: String, warn_only: warn_only.include?(:guid)) { |value| expect(value).to start_with "urn:blinkboxbooks:id:#{type}:#{data["id"]}" }
|
45
|
+
end
|
46
|
+
|
47
|
+
def validate_link(data)
|
48
|
+
validate_attribute(data, "rel", type: String) { |value| expect(value).to start_with "urn:blinkboxbooks:schema:" }
|
49
|
+
validate_attribute(data, "href", type: String) { |value| expect(value).to_not be_empty } if data["href"]
|
50
|
+
validate_attribute(data, "title", type: String) { |value| expect(value).to_not be_empty } if data["title"]
|
51
|
+
validate_attribute(data, "text", type: String) { |value| expect(value).to_not be_empty } if data["text"]
|
52
|
+
end
|
53
|
+
|
54
|
+
def validate_links(data, *expected)
|
55
|
+
validate_attribute(data, "links", type: Array)
|
56
|
+
data["links"].each { |link_data| validate_link(link_data) }
|
57
|
+
expected.each do |link_info|
|
58
|
+
rel = link_info[:rel] || link_info[:relationship]
|
59
|
+
rel = "urn:blinkboxbooks:schema:" << rel.tr(" ", "").downcase unless rel.start_with?("urn:")
|
60
|
+
count = data["links"].select { |link| link["rel"] == rel }.count
|
61
|
+
min = link_info[:min].to_i
|
62
|
+
max = %w{∞ *}.include?(link_info[:max]) ? INFINITY : link_info[:min].to_i
|
63
|
+
raise "Wrong number of #{rel} links: Expected #{min..max}, Actual: #{count}" unless count >= min && count <= max
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
# Validates a list of items is in the standardised blinkbox books format.
|
68
|
+
#
|
69
|
+
# @param options [Hash] Options for the validation.
|
70
|
+
# @option options [String] :item_type The (singular, snake cased) name of the items in the list, and the validation method (`validate_item_type`) that will be used to test them.
|
71
|
+
# @option options [String] :list_type The (signular, snake cased) name of the list type to be validated. Corresponds to the schema urn of `urn:blinkboxbooks:schema:listtypelist` and will look for the method `validate_list_of_list_type`.
|
72
|
+
# @option options [Integer] :min_count The minimum number of items there must be.
|
73
|
+
# @option options [Integer] :max_count The maximum number of items there can be.
|
74
|
+
# @option options [Integer] :count The exact number of items there must be. (Is overridden by min_ and max_count)
|
75
|
+
# @option options [Integer] :offset The offset expected for the data (used for ensuring the `offset` value is correct)
|
76
|
+
# @option options [Array<Symbol>] :warn_only Ask the validator to be lenient while testing the specified attributes (NB. Use snake_case)
|
77
|
+
def validate_list(data, options = {})
|
78
|
+
list_type = options[:list_type]
|
79
|
+
item_type = options[:item_type]
|
80
|
+
min_count = options[:min_count] || options[:count] || 0
|
81
|
+
max_count = options[:max_count] || options[:count] || 1000000
|
82
|
+
offset = options[:offset] || 0
|
83
|
+
warn_only = options[:warn_only] || []
|
84
|
+
|
85
|
+
# TODO: Should this be :list:#{list_type} rather than the list type before list?
|
86
|
+
expected_type = "urn:blinkboxbooks:schema:#{(list_type || "").tr("_", "")}list"
|
87
|
+
validate_attribute(data, "type", type: String, warn_only: warn_only.include?(:type)) { |value| expect(value).to eq(expected_type) }
|
88
|
+
validate_attribute(data, "count", type: Integer, warn_only: warn_only.include?(:count)) { |value| expect(min_count..max_count).to cover(value) }
|
89
|
+
validate_attribute(data, "offset", type: Integer, warn_only: warn_only.include?(:offset)) { |value| expect(value).to eq(offset) }
|
90
|
+
validate_attribute(data, "numberOfResults", type: Integer, warn_only: warn_only.include?(:number_of_results)) { |value| expect(value).to be >= data["count"] }
|
91
|
+
validate_attribute(data, "items", type: Array, warn_only: warn_only.include?(:empty_items))
|
92
|
+
|
93
|
+
unless list_type.nil?
|
94
|
+
further_validation = "validate_list_of_#{list_type}".to_sym
|
95
|
+
send(further_validation, data) if respond_to?(further_validation)
|
96
|
+
end
|
97
|
+
|
98
|
+
if data["items"] || min_count > 0
|
99
|
+
validate_attribute(data, "items", type: Array) do |value|
|
100
|
+
if min_count == max_count
|
101
|
+
expect(value.count).to eq(min_count)
|
102
|
+
else
|
103
|
+
expect(value.count).to be_between(min_count, max_count)
|
104
|
+
end
|
105
|
+
end
|
106
|
+
unless item_type.nil?
|
107
|
+
item_validation_method = "validate_#{item_type}".to_sym
|
108
|
+
data["items"].each { |item| self.send(item_validation_method, item) }
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
def validate_list_order(data, attribute_name, descending: false)
|
114
|
+
attribute_values = data["items"].map.with_index do |item, index|
|
115
|
+
value = item[attribute_name]
|
116
|
+
expect(value).to_not be_nil, "'#{attribute_name}' is nil in item at index #{index}"
|
117
|
+
if attribute_name =~ /date$/i
|
118
|
+
DateTime.parse(value)
|
119
|
+
else
|
120
|
+
value
|
121
|
+
end
|
122
|
+
end
|
123
|
+
expected_values = attribute_values.sort do |a, b|
|
124
|
+
if a.respond_to?(:upcase)
|
125
|
+
a.upcase <=> b.upcase # ordering of values should be case-insensitive
|
126
|
+
else
|
127
|
+
a <=> b
|
128
|
+
end
|
129
|
+
end
|
130
|
+
expected_values.reverse! if descending
|
131
|
+
expect(attribute_values).to eq(expected_values)
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
World(KnowsAboutResponseValidation)
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require "http_capture"
|
2
|
+
|
3
|
+
module KnowsAboutApiResponses
|
4
|
+
# Parses response data that is expected to be in JSON format.
|
5
|
+
#
|
6
|
+
# @return [Hash] The response data.
|
7
|
+
def parse_response_data
|
8
|
+
expect(HttpCapture::RESPONSES.last["Content-Type"]).to match(%r{^application/vnd.blinkboxbooks.data.v1\+json;?})
|
9
|
+
begin
|
10
|
+
@response_data = JSON.load(HttpCapture::RESPONSES.last.body)
|
11
|
+
rescue => e
|
12
|
+
raise "The response is not valid JSON: #{e.message}"
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
World(KnowsAboutApiResponses)
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# This module allows steps to set the subject of a scenario simply.
|
2
|
+
#
|
3
|
+
# subject(:book, {"isbn" => "9780111222333"})
|
4
|
+
# subject(:contributor, {"guid" => "abc123"})
|
5
|
+
#
|
6
|
+
# p subject(:book)
|
7
|
+
# # => {"isbn" => "9780111222333"}
|
8
|
+
#
|
9
|
+
# It's really just a readable wrapper for a hash with some test specific exceptions and things
|
10
|
+
module KnowsAboutGrammaticalSubjects
|
11
|
+
SUBJECTS = {}
|
12
|
+
USING_SETTER = :you_will_never_accidentally_pass_this
|
13
|
+
|
14
|
+
def subject(type, details = USING_SETTER)
|
15
|
+
if details == USING_SETTER
|
16
|
+
raise "Test error: A #{type} subject hasn't been defined in this scenario yet." unless SUBJECTS[type]
|
17
|
+
return SUBJECTS[type]
|
18
|
+
else
|
19
|
+
SUBJECTS[type] = details
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
World(KnowsAboutGrammaticalSubjects)
|
25
|
+
|
26
|
+
Before do
|
27
|
+
KnowsAboutGrammaticalSubjects::SUBJECTS.clear
|
28
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
require_relative "../../spec_helper"
|
2
|
+
require 'rspec'
|
3
|
+
require 'cucumber/blinkbox/requests'
|
4
|
+
|
5
|
+
# needed for String#camel_case
|
6
|
+
# (ought this be required by cucumber/blinkbox/requests ?)
|
7
|
+
require 'cucumber/helpers'
|
8
|
+
|
9
|
+
# needed for String#camelize
|
10
|
+
# (ought this be required by cucumber/helpers ?)
|
11
|
+
require 'active_support/core_ext/string'
|
12
|
+
|
13
|
+
describe "requests" do
|
14
|
+
|
15
|
+
describe "setting query parameters" do
|
16
|
+
|
17
|
+
before :each do
|
18
|
+
@request = Object.new
|
19
|
+
@request.extend(KnowsAboutApiRequests)
|
20
|
+
end
|
21
|
+
|
22
|
+
it "accepts a single value" do
|
23
|
+
@request.set_query_param("foo", 1)
|
24
|
+
@request.query["foo"].should eq(1)
|
25
|
+
end
|
26
|
+
|
27
|
+
it "accepts an array of values" do
|
28
|
+
@request.set_query_param("foo", [1, 2])
|
29
|
+
@request.query["foo"].should eq([1, 2])
|
30
|
+
end
|
31
|
+
|
32
|
+
it "accepts multiple single values" do
|
33
|
+
@request.set_query_param("foo", 1)
|
34
|
+
@request.set_query_param("foo", 2)
|
35
|
+
@request.query["foo"].should eq([1, 2])
|
36
|
+
end
|
37
|
+
|
38
|
+
it "appends arrays to existing arrays" do
|
39
|
+
@request.set_query_param("foo", [1, 2])
|
40
|
+
@request.set_query_param("foo", [3, 4])
|
41
|
+
@request.query["foo"].should eq([1, 2, 3, 4])
|
42
|
+
end
|
43
|
+
|
44
|
+
it "appends single values to existing arrays" do
|
45
|
+
@request.set_query_param("foo", [1, 2])
|
46
|
+
@request.set_query_param("foo", 3)
|
47
|
+
@request.query["foo"].should eq([1, 2, 3])
|
48
|
+
end
|
49
|
+
|
50
|
+
it "appends arrays to existing values" do
|
51
|
+
@request.set_query_param("foo", 1)
|
52
|
+
@request.set_query_param("foo", [2, 3])
|
53
|
+
@request.query["foo"].should eq([1, 2, 3])
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,169 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: cucumber-blinkbox
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.3.3
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- blinkbox books
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-01-29 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: activesupport
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ! '>='
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '4.0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ! '>='
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '4.0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: cucumber
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ~>
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '1.3'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ~>
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '1.3'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: httpclient
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ! '>='
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ! '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: http_capture
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ! '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ! '>='
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: bundler
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ~>
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '1.3'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ~>
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '1.3'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: rspec
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ~>
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '2.99'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ~>
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '2.99'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: rake
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ! '>='
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ! '>='
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: cucumber-helpers
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - ! '>='
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - ! '>='
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '0'
|
125
|
+
description: blinkbox books specific cucumber test helpers
|
126
|
+
email:
|
127
|
+
- jphastings@blinkbox.com
|
128
|
+
executables: []
|
129
|
+
extensions: []
|
130
|
+
extra_rdoc_files: []
|
131
|
+
files:
|
132
|
+
- VERSION
|
133
|
+
- lib/cucumber/blinkbox.rb
|
134
|
+
- lib/cucumber/blinkbox/data_dependencies.rb
|
135
|
+
- lib/cucumber/blinkbox/environment.rb
|
136
|
+
- lib/cucumber/blinkbox/formatter/html.rb
|
137
|
+
- lib/cucumber/blinkbox/requests.rb
|
138
|
+
- lib/cucumber/blinkbox/response_validation.rb
|
139
|
+
- lib/cucumber/blinkbox/responses.rb
|
140
|
+
- lib/cucumber/blinkbox/subjects.rb
|
141
|
+
- spec/cucumber/blinkbox/requests_spec.rb
|
142
|
+
- spec/spec_helper.rb
|
143
|
+
homepage: http://blinkboxbooks.github.io/
|
144
|
+
licenses:
|
145
|
+
- MIT
|
146
|
+
metadata: {}
|
147
|
+
post_install_message:
|
148
|
+
rdoc_options: []
|
149
|
+
require_paths:
|
150
|
+
- lib
|
151
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
152
|
+
requirements:
|
153
|
+
- - ! '>='
|
154
|
+
- !ruby/object:Gem::Version
|
155
|
+
version: '0'
|
156
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
157
|
+
requirements:
|
158
|
+
- - ! '>='
|
159
|
+
- !ruby/object:Gem::Version
|
160
|
+
version: '0'
|
161
|
+
requirements: []
|
162
|
+
rubyforge_project:
|
163
|
+
rubygems_version: 2.4.5
|
164
|
+
signing_key:
|
165
|
+
specification_version: 4
|
166
|
+
summary: blinkbox books specific cucumber test helpers
|
167
|
+
test_files:
|
168
|
+
- spec/cucumber/blinkbox/requests_spec.rb
|
169
|
+
- spec/spec_helper.rb
|