cucumber-blinkbox 0.3.3
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 +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
|