cucumber-wire 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. checksums.yaml +7 -0
  2. data/.rspec +1 -0
  3. data/.travis.yml +18 -0
  4. data/Gemfile +20 -0
  5. data/README.md +1 -0
  6. data/Rakefile +13 -0
  7. data/cucumber-wire.gemspec +28 -0
  8. data/features/erb_configuration.feature +54 -0
  9. data/features/handle_unexpected_response.feature +29 -0
  10. data/features/invoke_message.feature +212 -0
  11. data/features/readme.md +26 -0
  12. data/features/snippets_message.feature +47 -0
  13. data/features/step_definitions/aruba_steps.rb +1 -0
  14. data/features/step_definitions/wire_steps.rb +58 -0
  15. data/features/step_matches_message.feature +79 -0
  16. data/features/support/fake_wire_server.rb +80 -0
  17. data/features/table_diffing.feature +124 -0
  18. data/features/tags.feature +86 -0
  19. data/features/timeouts.feature +63 -0
  20. data/lib/cucumber/wire.rb +5 -0
  21. data/lib/cucumber/wire/add_hooks_filter.rb +25 -0
  22. data/lib/cucumber/wire/configuration.rb +38 -0
  23. data/lib/cucumber/wire/connection.rb +63 -0
  24. data/lib/cucumber/wire/connections.rb +50 -0
  25. data/lib/cucumber/wire/data_packet.rb +34 -0
  26. data/lib/cucumber/wire/exception.rb +32 -0
  27. data/lib/cucumber/wire/plugin.rb +32 -0
  28. data/lib/cucumber/wire/protocol.rb +43 -0
  29. data/lib/cucumber/wire/protocol/requests.rb +128 -0
  30. data/lib/cucumber/wire/request_handler.rb +32 -0
  31. data/lib/cucumber/wire/snippet.rb +35 -0
  32. data/lib/cucumber/wire/step_definition.rb +21 -0
  33. data/lib/cucumber/wire/version +1 -0
  34. data/spec/cucumber/wire/configuration_spec.rb +63 -0
  35. data/spec/cucumber/wire/connection_spec.rb +64 -0
  36. data/spec/cucumber/wire/connections_spec.rb +23 -0
  37. data/spec/cucumber/wire/data_packet_spec.rb +43 -0
  38. data/spec/cucumber/wire/exception_spec.rb +50 -0
  39. 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
+ """