cucumber-wire 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.rspec +1 -0
- data/.travis.yml +18 -0
- data/Gemfile +20 -0
- data/README.md +1 -0
- data/Rakefile +13 -0
- data/cucumber-wire.gemspec +28 -0
- data/features/erb_configuration.feature +54 -0
- data/features/handle_unexpected_response.feature +29 -0
- data/features/invoke_message.feature +212 -0
- data/features/readme.md +26 -0
- data/features/snippets_message.feature +47 -0
- data/features/step_definitions/aruba_steps.rb +1 -0
- data/features/step_definitions/wire_steps.rb +58 -0
- data/features/step_matches_message.feature +79 -0
- data/features/support/fake_wire_server.rb +80 -0
- data/features/table_diffing.feature +124 -0
- data/features/tags.feature +86 -0
- data/features/timeouts.feature +63 -0
- data/lib/cucumber/wire.rb +5 -0
- data/lib/cucumber/wire/add_hooks_filter.rb +25 -0
- data/lib/cucumber/wire/configuration.rb +38 -0
- data/lib/cucumber/wire/connection.rb +63 -0
- data/lib/cucumber/wire/connections.rb +50 -0
- data/lib/cucumber/wire/data_packet.rb +34 -0
- data/lib/cucumber/wire/exception.rb +32 -0
- data/lib/cucumber/wire/plugin.rb +32 -0
- data/lib/cucumber/wire/protocol.rb +43 -0
- data/lib/cucumber/wire/protocol/requests.rb +128 -0
- data/lib/cucumber/wire/request_handler.rb +32 -0
- data/lib/cucumber/wire/snippet.rb +35 -0
- data/lib/cucumber/wire/step_definition.rb +21 -0
- data/lib/cucumber/wire/version +1 -0
- data/spec/cucumber/wire/configuration_spec.rb +63 -0
- data/spec/cucumber/wire/connection_spec.rb +64 -0
- data/spec/cucumber/wire/connections_spec.rb +23 -0
- data/spec/cucumber/wire/data_packet_spec.rb +43 -0
- data/spec/cucumber/wire/exception_spec.rb +50 -0
- metadata +157 -0
@@ -0,0 +1 @@
|
|
1
|
+
require "aruba/cucumber"
|
@@ -0,0 +1,58 @@
|
|
1
|
+
Given /^there is a wire server (running |)on port (\d+) which understands the following protocol:$/ do |running, port, table|
|
2
|
+
protocol = table.hashes.map do |table_hash|
|
3
|
+
table_hash['response'] = table_hash['response'].gsub(/\n/, '\n')
|
4
|
+
table_hash
|
5
|
+
end
|
6
|
+
|
7
|
+
@server = FakeWireServer.new(port.to_i, protocol)
|
8
|
+
start_wire_server if running.strip == "running"
|
9
|
+
end
|
10
|
+
|
11
|
+
Given /^the wire server takes (.*) seconds to respond to the invoke message$/ do |timeout|
|
12
|
+
@server.delay_response(:invoke, timeout.to_f)
|
13
|
+
start_wire_server
|
14
|
+
end
|
15
|
+
|
16
|
+
Given /^I have environment variable (\w+) set to "([^"]*)"$/ do |variable, value|
|
17
|
+
set_env(variable, value)
|
18
|
+
end
|
19
|
+
|
20
|
+
Then(/^the wire server should have received the following messages:$/) do |expected_messages|
|
21
|
+
expect(messages_received).to eq expected_messages.raw.flatten
|
22
|
+
end
|
23
|
+
|
24
|
+
module WireHelper
|
25
|
+
attr_reader :messages_received
|
26
|
+
|
27
|
+
def start_wire_server
|
28
|
+
@messages_received = []
|
29
|
+
reader, writer = IO.pipe
|
30
|
+
@wire_pid = fork {
|
31
|
+
reader.close
|
32
|
+
@server.run(writer)
|
33
|
+
}
|
34
|
+
writer.close
|
35
|
+
Thread.new do
|
36
|
+
while message = reader.gets
|
37
|
+
@messages_received << message.strip
|
38
|
+
end
|
39
|
+
end
|
40
|
+
at_exit { stop_wire_server }
|
41
|
+
end
|
42
|
+
|
43
|
+
def stop_wire_server
|
44
|
+
return unless @wire_pid
|
45
|
+
Process.kill('KILL', @wire_pid)
|
46
|
+
Process.wait(@wire_pid)
|
47
|
+
rescue Errno::ESRCH
|
48
|
+
# No such process - wire server has already been stopped by the After hook
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
Before do
|
53
|
+
extend(WireHelper)
|
54
|
+
end
|
55
|
+
|
56
|
+
After do
|
57
|
+
stop_wire_server
|
58
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
Feature: Step matches message
|
2
|
+
|
3
|
+
When the features have been parsed, Cucumber will send a `step_matches`
|
4
|
+
message to ask the wire server if it can match a step name. This happens for
|
5
|
+
each of the steps in each of the features.
|
6
|
+
|
7
|
+
The wire server replies with an array of StepMatch objects.
|
8
|
+
|
9
|
+
When each StepMatch is returned, it contains the following data:
|
10
|
+
|
11
|
+
* `id` - identifier for the step definition to be used later when if it
|
12
|
+
needs to be invoked. The identifier can be any string value and
|
13
|
+
is simply used for the wire server's own reference.
|
14
|
+
* `args` - any argument values as captured by the wire end's own regular
|
15
|
+
expression (or other argument matching) process.
|
16
|
+
|
17
|
+
Background:
|
18
|
+
Given a file named "features/wired.feature" with:
|
19
|
+
"""
|
20
|
+
Feature: High strung
|
21
|
+
Scenario: Wired
|
22
|
+
Given we're all wired
|
23
|
+
|
24
|
+
"""
|
25
|
+
And a file named "features/step_definitions/some_remote_place.wire" with:
|
26
|
+
"""
|
27
|
+
host: localhost
|
28
|
+
port: 54321
|
29
|
+
|
30
|
+
"""
|
31
|
+
|
32
|
+
Scenario: Dry run finds no step match
|
33
|
+
Given there is a wire server running on port 54321 which understands the following protocol:
|
34
|
+
| request | response |
|
35
|
+
| ["step_matches",{"name_to_match":"we're all wired"}] | ["success",[]] |
|
36
|
+
When I run `cucumber --dry-run --no-snippets -f progress`
|
37
|
+
And it should pass with:
|
38
|
+
"""
|
39
|
+
U
|
40
|
+
|
41
|
+
1 scenario (1 undefined)
|
42
|
+
1 step (1 undefined)
|
43
|
+
|
44
|
+
"""
|
45
|
+
|
46
|
+
Scenario: Dry run finds a step match
|
47
|
+
Given there is a wire server running on port 54321 which understands the following protocol:
|
48
|
+
| request | response |
|
49
|
+
| ["step_matches",{"name_to_match":"we're all wired"}] | ["success",[{"id":"1", "args":[]}]] |
|
50
|
+
When I run `cucumber --dry-run -f progress`
|
51
|
+
And it should pass with:
|
52
|
+
"""
|
53
|
+
-
|
54
|
+
|
55
|
+
1 scenario (1 skipped)
|
56
|
+
1 step (1 skipped)
|
57
|
+
|
58
|
+
"""
|
59
|
+
|
60
|
+
Scenario: Step matches returns details about the remote step definition
|
61
|
+
|
62
|
+
Optionally, the StepMatch can also contain a source reference, and a native
|
63
|
+
regexp string which will be used by some formatters.
|
64
|
+
|
65
|
+
Given there is a wire server running on port 54321 which understands the following protocol:
|
66
|
+
| request | response |
|
67
|
+
| ["step_matches",{"name_to_match":"we're all wired"}] | ["success",[{"id":"1", "args":[], "source":"MyApp.MyClass:123", "regexp":"we.*"}]] |
|
68
|
+
When I run `cucumber -f stepdefs --dry-run`
|
69
|
+
Then it should pass with:
|
70
|
+
"""
|
71
|
+
-
|
72
|
+
|
73
|
+
we.* # MyApp.MyClass:123
|
74
|
+
|
75
|
+
1 scenario (1 skipped)
|
76
|
+
1 step (1 skipped)
|
77
|
+
|
78
|
+
"""
|
79
|
+
And the stderr should not contain anything
|
@@ -0,0 +1,80 @@
|
|
1
|
+
require 'multi_json'
|
2
|
+
require 'socket'
|
3
|
+
|
4
|
+
class FakeWireServer
|
5
|
+
def initialize(port, protocol_table)
|
6
|
+
@port, @protocol_table = port, protocol_table
|
7
|
+
@delays = {}
|
8
|
+
end
|
9
|
+
|
10
|
+
def run(io)
|
11
|
+
@server = TCPServer.open(@port)
|
12
|
+
loop { handle_connections(io) }
|
13
|
+
end
|
14
|
+
|
15
|
+
def delay_response(message, delay)
|
16
|
+
@delays[message] = delay
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def handle_connections(io)
|
22
|
+
Thread.start(@server.accept) { |socket| open_session_on socket, io }
|
23
|
+
end
|
24
|
+
|
25
|
+
def open_session_on(socket, io)
|
26
|
+
begin
|
27
|
+
on_message = lambda { |message| io.puts message }
|
28
|
+
SocketSession.new(socket, @protocol_table, @delays, on_message).start
|
29
|
+
rescue Exception => e
|
30
|
+
raise e
|
31
|
+
ensure
|
32
|
+
socket.close
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
class SocketSession
|
37
|
+
def initialize(socket, protocol, delays, on_message)
|
38
|
+
@socket = socket
|
39
|
+
@protocol = protocol
|
40
|
+
@delays = delays
|
41
|
+
@on_message = on_message
|
42
|
+
end
|
43
|
+
|
44
|
+
def start
|
45
|
+
while message = @socket.gets
|
46
|
+
handle(message)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
private
|
51
|
+
|
52
|
+
def handle(data)
|
53
|
+
if protocol_entry = response_to(data.strip)
|
54
|
+
sleep delay(data)
|
55
|
+
@on_message.call(MultiJson.load(protocol_entry['request'])[0])
|
56
|
+
send_response(protocol_entry['response'])
|
57
|
+
else
|
58
|
+
serialized_exception = { :message => "Not understood: #{data}", :backtrace => [] }
|
59
|
+
send_response(['fail', serialized_exception ].to_json)
|
60
|
+
end
|
61
|
+
rescue => e
|
62
|
+
send_response(['fail', { :message => e.message, :backtrace => e.backtrace, :exception => e.class } ].to_json)
|
63
|
+
end
|
64
|
+
|
65
|
+
def response_to(data)
|
66
|
+
@protocol.detect do |entry|
|
67
|
+
MultiJson.load(entry['request']) == MultiJson.load(data)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def send_response(response)
|
72
|
+
@socket.puts response + "\n"
|
73
|
+
end
|
74
|
+
|
75
|
+
def delay(data)
|
76
|
+
message = MultiJson.load(data.strip)[0]
|
77
|
+
@delays[message.to_sym] || 0
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,124 @@
|
|
1
|
+
Feature: Wire protocol table diffing
|
2
|
+
|
3
|
+
In order to use the amazing functionality in the Cucumber table object
|
4
|
+
As a wire server
|
5
|
+
I want to be able to ask for a table diff during a step definition invocation
|
6
|
+
|
7
|
+
Background:
|
8
|
+
Given a file named "features/wired.feature" with:
|
9
|
+
"""
|
10
|
+
Feature: Hello
|
11
|
+
Scenario: Wired
|
12
|
+
Given we're all wired
|
13
|
+
|
14
|
+
"""
|
15
|
+
And a file named "features/step_definitions/some_remote_place.wire" with:
|
16
|
+
"""
|
17
|
+
host: localhost
|
18
|
+
port: 54321
|
19
|
+
|
20
|
+
"""
|
21
|
+
|
22
|
+
@spawn
|
23
|
+
Scenario: Invoke a step definition tries to diff the table and fails
|
24
|
+
Given there is a wire server running on port 54321 which understands the following protocol:
|
25
|
+
| request | response |
|
26
|
+
| ["step_matches",{"name_to_match":"we're all wired"}] | ["success",[{"id":"1", "args":[]}]] |
|
27
|
+
| ["begin_scenario"] | ["success"] |
|
28
|
+
| ["invoke",{"id":"1","args":[]}] | ["diff",[[["a","b"],["c","d"]],[["x","y"],["z","z"]]]] |
|
29
|
+
| ["diff_failed"] | ["fail",{"message":"Not same", "exception":"DifferentException", "backtrace":["a.cs:12","b.cs:34"]}] |
|
30
|
+
| ["end_scenario"] | ["success"] |
|
31
|
+
When I run `cucumber -f progress --backtrace -q`
|
32
|
+
Then the stderr should not contain anything
|
33
|
+
And it should fail with exactly:
|
34
|
+
"""
|
35
|
+
F
|
36
|
+
|
37
|
+
(::) failed steps (::)
|
38
|
+
|
39
|
+
Not same (DifferentException from localhost:54321)
|
40
|
+
a.cs:12
|
41
|
+
b.cs:34
|
42
|
+
features/wired.feature:3:in `Given we're all wired'
|
43
|
+
|
44
|
+
Failing Scenarios:
|
45
|
+
cucumber features/wired.feature:2
|
46
|
+
|
47
|
+
1 scenario (1 failed)
|
48
|
+
1 step (1 failed)
|
49
|
+
|
50
|
+
"""
|
51
|
+
|
52
|
+
Scenario: Invoke a step definition tries to diff the table and passes
|
53
|
+
Given there is a wire server running on port 54321 which understands the following protocol:
|
54
|
+
| request | response |
|
55
|
+
| ["step_matches",{"name_to_match":"we're all wired"}] | ["success",[{"id":"1", "args":[]}]] |
|
56
|
+
| ["begin_scenario"] | ["success"] |
|
57
|
+
| ["invoke",{"id":"1","args":[]}] | ["diff",[[["a"],["b"]],[["a"],["b"]]]] |
|
58
|
+
| ["diff_ok"] | ["success"] |
|
59
|
+
| ["end_scenario"] | ["success"] |
|
60
|
+
When I run `cucumber -f progress -q`
|
61
|
+
Then it should pass with exactly:
|
62
|
+
"""
|
63
|
+
.
|
64
|
+
|
65
|
+
1 scenario (1 passed)
|
66
|
+
1 step (1 passed)
|
67
|
+
|
68
|
+
"""
|
69
|
+
|
70
|
+
@spawn
|
71
|
+
Scenario: Invoke a step definition which successfully diffs a table but then fails
|
72
|
+
Given there is a wire server running on port 54321 which understands the following protocol:
|
73
|
+
| request | response |
|
74
|
+
| ["step_matches",{"name_to_match":"we're all wired"}] | ["success",[{"id":"1", "args":[]}]] |
|
75
|
+
| ["begin_scenario"] | ["success"] |
|
76
|
+
| ["invoke",{"id":"1","args":[]}] | ["diff",[[["a"],["b"]],[["a"],["b"]]]] |
|
77
|
+
| ["diff_ok"] | ["fail",{"message":"I wanted things to be different for us"}] |
|
78
|
+
| ["end_scenario"] | ["success"] |
|
79
|
+
When I run `cucumber -f progress -q`
|
80
|
+
Then it should fail with exactly:
|
81
|
+
"""
|
82
|
+
F
|
83
|
+
|
84
|
+
(::) failed steps (::)
|
85
|
+
|
86
|
+
I wanted things to be different for us (Cucumber::Wire::Exception)
|
87
|
+
features/wired.feature:3:in `Given we're all wired'
|
88
|
+
|
89
|
+
Failing Scenarios:
|
90
|
+
cucumber features/wired.feature:2
|
91
|
+
|
92
|
+
1 scenario (1 failed)
|
93
|
+
1 step (1 failed)
|
94
|
+
|
95
|
+
"""
|
96
|
+
|
97
|
+
@spawn
|
98
|
+
Scenario: Invoke a step definition which asks for an immediate diff that fails
|
99
|
+
Given there is a wire server running on port 54321 which understands the following protocol:
|
100
|
+
| request | response |
|
101
|
+
| ["step_matches",{"name_to_match":"we're all wired"}] | ["success",[{"id":"1", "args":[]}]] |
|
102
|
+
| ["begin_scenario"] | ["success"] |
|
103
|
+
| ["invoke",{"id":"1","args":[]}] | ["diff!",[[["a"]],[["b"]]]] |
|
104
|
+
| ["end_scenario"] | ["success"] |
|
105
|
+
When I run `cucumber -f progress -q`
|
106
|
+
And it should fail with exactly:
|
107
|
+
"""
|
108
|
+
F
|
109
|
+
|
110
|
+
(::) failed steps (::)
|
111
|
+
|
112
|
+
Tables were not identical:
|
113
|
+
|
114
|
+
| (-) a | (+) b |
|
115
|
+
(Cucumber::MultilineArgument::DataTable::Different)
|
116
|
+
features/wired.feature:3:in `Given we're all wired'
|
117
|
+
|
118
|
+
Failing Scenarios:
|
119
|
+
cucumber features/wired.feature:2
|
120
|
+
|
121
|
+
1 scenario (1 failed)
|
122
|
+
1 step (1 failed)
|
123
|
+
|
124
|
+
"""
|
@@ -0,0 +1,86 @@
|
|
1
|
+
Feature: Wire protocol tags
|
2
|
+
|
3
|
+
In order to use Before and After hooks in a wire server, we send tags with the
|
4
|
+
scenario in the begin_scenario and end_scenario messages
|
5
|
+
|
6
|
+
Background:
|
7
|
+
Given a file named "features/step_definitions/some_remote_place.wire" with:
|
8
|
+
"""
|
9
|
+
host: localhost
|
10
|
+
port: 54321
|
11
|
+
|
12
|
+
"""
|
13
|
+
|
14
|
+
Scenario: Run a scenario
|
15
|
+
Given a file named "features/wired.feature" with:
|
16
|
+
"""
|
17
|
+
@foo @bar
|
18
|
+
Feature: Wired
|
19
|
+
|
20
|
+
@baz
|
21
|
+
Scenario: Everybody's Wired
|
22
|
+
Given we're all wired
|
23
|
+
|
24
|
+
"""
|
25
|
+
And there is a wire server running on port 54321 which understands the following protocol:
|
26
|
+
| request | response |
|
27
|
+
| ["step_matches",{"name_to_match":"we're all wired"}] | ["success",[{"id":"1", "args":[]}]] |
|
28
|
+
| ["begin_scenario", {"tags":["bar","baz","foo"]}] | ["success"] |
|
29
|
+
| ["invoke",{"id":"1","args":[]}] | ["success"] |
|
30
|
+
| ["end_scenario", {"tags":["bar","baz","foo"]}] | ["success"] |
|
31
|
+
When I run `cucumber -f pretty -q`
|
32
|
+
Then the stderr should not contain anything
|
33
|
+
And it should pass with:
|
34
|
+
"""
|
35
|
+
@foo @bar
|
36
|
+
Feature: Wired
|
37
|
+
|
38
|
+
@baz
|
39
|
+
Scenario: Everybody's Wired
|
40
|
+
Given we're all wired
|
41
|
+
|
42
|
+
1 scenario (1 passed)
|
43
|
+
1 step (1 passed)
|
44
|
+
|
45
|
+
"""
|
46
|
+
|
47
|
+
Scenario: Run a scenario outline example
|
48
|
+
Given a file named "features/wired.feature" with:
|
49
|
+
"""
|
50
|
+
@foo @bar
|
51
|
+
Feature: Wired
|
52
|
+
|
53
|
+
@baz
|
54
|
+
Scenario Outline: Everybody's Wired
|
55
|
+
Given we're all <something>
|
56
|
+
|
57
|
+
Examples:
|
58
|
+
| something |
|
59
|
+
| wired |
|
60
|
+
|
61
|
+
"""
|
62
|
+
And there is a wire server running on port 54321 which understands the following protocol:
|
63
|
+
| request | response |
|
64
|
+
| ["step_matches",{"name_to_match":"we're all wired"}] | ["success",[{"id":"1", "args":[]}]] |
|
65
|
+
| ["begin_scenario", {"tags":["bar","baz","foo"]}] | ["success"] |
|
66
|
+
| ["invoke",{"id":"1","args":[]}] | ["success"] |
|
67
|
+
| ["end_scenario", {"tags":["bar","baz","foo"]}] | ["success"] |
|
68
|
+
When I run `cucumber -f pretty -q`
|
69
|
+
Then the stderr should not contain anything
|
70
|
+
And it should pass with exactly:
|
71
|
+
"""
|
72
|
+
@foo @bar
|
73
|
+
Feature: Wired
|
74
|
+
|
75
|
+
@baz
|
76
|
+
Scenario Outline: Everybody's Wired
|
77
|
+
Given we're all <something>
|
78
|
+
|
79
|
+
Examples:
|
80
|
+
| something |
|
81
|
+
| wired |
|
82
|
+
|
83
|
+
1 scenario (1 passed)
|
84
|
+
1 step (1 passed)
|
85
|
+
|
86
|
+
"""
|
@@ -0,0 +1,63 @@
|
|
1
|
+
Feature: Wire protocol timeouts
|
2
|
+
|
3
|
+
We don't want Cucumber to hang forever on a wire server that's not even there,
|
4
|
+
but equally we need to give the user the flexibility to allow step definitions
|
5
|
+
to take a while to execute, if that's what they need.
|
6
|
+
|
7
|
+
Background:
|
8
|
+
Given a file named "features/wired.feature" with:
|
9
|
+
"""
|
10
|
+
Feature: Telegraphy
|
11
|
+
Scenario: Wired
|
12
|
+
Given we're all wired
|
13
|
+
|
14
|
+
"""
|
15
|
+
|
16
|
+
Scenario: Try to talk to a server that's not there
|
17
|
+
Given a file named "features/step_definitions/some_remote_place.wire" with:
|
18
|
+
"""
|
19
|
+
host: localhost
|
20
|
+
port: 54321
|
21
|
+
|
22
|
+
"""
|
23
|
+
When I run `cucumber -f progress`
|
24
|
+
Then the stderr should contain:
|
25
|
+
"""
|
26
|
+
Unable to contact the wire server at localhost:54321
|
27
|
+
"""
|
28
|
+
|
29
|
+
@spawn
|
30
|
+
Scenario: Invoke a step definition that takes longer than its timeout
|
31
|
+
Given a file named "features/step_definitions/some_remote_place.wire" with:
|
32
|
+
"""
|
33
|
+
host: localhost
|
34
|
+
port: 54321
|
35
|
+
timeout:
|
36
|
+
invoke: 0.1
|
37
|
+
|
38
|
+
"""
|
39
|
+
And there is a wire server on port 54321 which understands the following protocol:
|
40
|
+
| request | response |
|
41
|
+
| ["step_matches",{"name_to_match":"we're all wired"}] | ["success",[{"id":"1", "args":[{"val":"wired", "pos":10}]}]] |
|
42
|
+
| ["begin_scenario"] | ["success"] |
|
43
|
+
| ["invoke",{"id":"1","args":["wired"]}] | ["success"] |
|
44
|
+
| ["end_scenario"] | ["success"] |
|
45
|
+
And the wire server takes 0.2 seconds to respond to the invoke message
|
46
|
+
When I run `cucumber -f pretty -q`
|
47
|
+
Then the stderr should not contain anything
|
48
|
+
And it should fail with exactly:
|
49
|
+
"""
|
50
|
+
Feature: Telegraphy
|
51
|
+
|
52
|
+
Scenario: Wired
|
53
|
+
Given we're all wired
|
54
|
+
Timed out calling wire server with message 'invoke' (Timeout::Error)
|
55
|
+
features/wired.feature:3:in `Given we're all wired'
|
56
|
+
|
57
|
+
Failing Scenarios:
|
58
|
+
cucumber features/wired.feature:2
|
59
|
+
|
60
|
+
1 scenario (1 failed)
|
61
|
+
1 step (1 failed)
|
62
|
+
|
63
|
+
"""
|