cucumber 2.0.0.rc.4 → 2.0.0.rc.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/History.md +74 -52
- data/examples/i18n/ht/Rakefile +6 -0
- data/examples/i18n/ht/features/adisyon.feature +17 -0
- data/examples/i18n/ht/features/divizyon.feature +10 -0
- data/examples/i18n/ht/features/step_definitions/kalkilatris_steps.rb +25 -0
- data/examples/i18n/ht/lib/kalkilatris.rb +14 -0
- data/features/docs/cli/dry_run.feature +43 -0
- data/features/docs/cli/strict_mode.feature +24 -1
- data/features/docs/defining_steps/nested_steps.feature +49 -0
- data/features/docs/exception_in_after_step_hook.feature +1 -1
- data/features/docs/exception_in_around_hook.feature +80 -0
- data/features/docs/formatters/json_formatter.feature +65 -1
- data/features/docs/formatters/junit_formatter.feature +40 -0
- data/features/docs/{wire_protocol_erb.feature → wire_protocol/erb_configuration.feature} +2 -2
- data/features/docs/wire_protocol/handle_unexpected_response.feature +30 -0
- data/features/docs/wire_protocol/invoke_message.feature +216 -0
- data/features/docs/wire_protocol/readme.md +26 -0
- data/features/docs/wire_protocol/snippets_message.feature +51 -0
- data/features/docs/wire_protocol/step_matches_message.feature +81 -0
- data/features/docs/{wire_protocol_table_diffing.feature → wire_protocol/table_diffing.feature} +1 -0
- data/features/docs/{wire_protocol_tags.feature → wire_protocol/tags.feature} +1 -0
- data/features/docs/{wire_protocol_timeouts.feature → wire_protocol/timeouts.feature} +1 -0
- data/features/docs/work_in_progress.feature +1 -1
- data/features/docs/writing_support_code/around_hooks.feature +31 -0
- data/features/docs/writing_support_code/before_hook.feature +7 -3
- data/features/docs/writing_support_code/tagged_hooks.feature +44 -6
- data/features/lib/step_definitions/wire_steps.rb +18 -1
- data/features/lib/support/fake_wire_server.rb +10 -7
- data/lib/cucumber.rb +1 -3
- data/lib/cucumber/cli/options.rb +7 -0
- data/lib/cucumber/encoding.rb +5 -0
- data/lib/cucumber/errors.rb +8 -0
- data/lib/cucumber/filters/prepare_world.rb +13 -5
- data/lib/cucumber/formatter/console.rb +1 -1
- data/lib/cucumber/formatter/gherkin_formatter_adapter.rb +9 -6
- data/lib/cucumber/formatter/junit.rb +95 -0
- data/lib/cucumber/formatter/legacy_api/adapter.rb +39 -3
- data/lib/cucumber/platform.rb +1 -1
- data/lib/cucumber/project_initializer.rb +43 -0
- data/lib/cucumber/rb_support/rb_step_definition.rb +11 -2
- data/lib/cucumber/rb_support/rb_world.rb +2 -2
- data/lib/cucumber/rb_support/snippet.rb +1 -1
- data/lib/cucumber/rspec/doubles.rb +1 -1
- data/lib/cucumber/running_test_case.rb +115 -0
- data/lib/cucumber/runtime.rb +5 -1
- data/lib/cucumber/runtime/for_programming_languages.rb +2 -2
- data/lib/cucumber/runtime/support_code.rb +18 -8
- data/spec/cucumber/formatter/junit_spec.rb +212 -156
- data/spec/cucumber/formatter/legacy_api/adapter_spec.rb +89 -1
- data/spec/cucumber/formatter/pretty_spec.rb +13 -0
- data/spec/cucumber/project_initializer_spec.rb +87 -0
- data/spec/cucumber/rb_support/rb_step_definition_spec.rb +21 -3
- data/spec/cucumber/rb_support/snippet_spec.rb +6 -6
- data/spec/cucumber/running_test_case_spec.rb +83 -0
- data/spec/spec_helper.rb +1 -4
- metadata +35 -18
- data/bin/cuke +0 -60
- data/features/docs/report_called_undefined_steps.feature +0 -57
- data/features/docs/wire_protocol.feature +0 -337
- data/lib/cucumber/ast/facade.rb +0 -117
@@ -0,0 +1,26 @@
|
|
1
|
+
Cucumber's wire protocol allows step definitions to be
|
2
|
+
implemented and invoked on any platform.
|
3
|
+
|
4
|
+
Communication is over a TCP socket, which Cucumber connects to when it finds
|
5
|
+
a definition file with the .wire extension in the step_definitions folder
|
6
|
+
(or other load path). Note that these files are rendered with ERB when loaded.
|
7
|
+
|
8
|
+
A WirePacket flowing in either direction is formatted as a JSON-encoded
|
9
|
+
string, with a newline character signaling the end of a packet. See the
|
10
|
+
specs for Cucumber::WireSupport::WirePacket for more details.
|
11
|
+
|
12
|
+
Cucumber sends the following request messages out over the wire:
|
13
|
+
|
14
|
+
* `step_matches` - Find out whether the wire server has a definition for a step
|
15
|
+
* `invoke` - Ask for a step definition to be invoked
|
16
|
+
* `begin_scenario` - signals that cucumber is about to execute a scenario
|
17
|
+
* `end_scenario` - signals that cucumber has finished executing a scenario
|
18
|
+
* `snippet_text` - requests a snippet for an undefined step
|
19
|
+
|
20
|
+
Every message supports two standard responses:
|
21
|
+
|
22
|
+
* `success` - expects different arguments (sometimes none at all) depending
|
23
|
+
on the request that was sent.
|
24
|
+
* `fail` - causes a Cucumber::WireSupport::WireException to be raised.
|
25
|
+
|
26
|
+
Some messages support more responses - see individual scenarios for details.
|
@@ -0,0 +1,51 @@
|
|
1
|
+
@wire
|
2
|
+
Feature: Snippets message
|
3
|
+
|
4
|
+
If a step doesn't match, Cucumber will ask the wire server to return a snippet of code for a
|
5
|
+
step definition.
|
6
|
+
|
7
|
+
Background:
|
8
|
+
Given a file named "features/wired.feature" with:
|
9
|
+
"""
|
10
|
+
Feature: High strung
|
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: Wire server returns snippets for a step that didn't match
|
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",[]] |
|
27
|
+
| ["snippet_text",{"step_keyword":"Given","multiline_arg_class":"","step_name":"we're all wired"}] | ["success","foo()\n bar;\nbaz"] |
|
28
|
+
| ["begin_scenario"] | ["success"] |
|
29
|
+
| ["end_scenario"] | ["success"] |
|
30
|
+
When I run `cucumber -f pretty`
|
31
|
+
Then the stderr should not contain anything
|
32
|
+
And it should pass with:
|
33
|
+
"""
|
34
|
+
Feature: High strung
|
35
|
+
|
36
|
+
Scenario: Wired # features/wired.feature:2
|
37
|
+
Given we're all wired # features/wired.feature:3
|
38
|
+
|
39
|
+
1 scenario (1 undefined)
|
40
|
+
1 step (1 undefined)
|
41
|
+
"""
|
42
|
+
And the output should contain:
|
43
|
+
"""
|
44
|
+
|
45
|
+
You can implement step definitions for undefined steps with these snippets:
|
46
|
+
|
47
|
+
foo()
|
48
|
+
bar;
|
49
|
+
baz
|
50
|
+
|
51
|
+
"""
|
@@ -0,0 +1,81 @@
|
|
1
|
+
@wire
|
2
|
+
Feature: Step matches message
|
3
|
+
|
4
|
+
When the features have been parsed, Cucumber will send a `step_matches`
|
5
|
+
message to ask the wire server if it can match a step name. This happens for
|
6
|
+
each of the steps in each of the features.
|
7
|
+
|
8
|
+
The wire server replies with an array of StepMatch objects.
|
9
|
+
|
10
|
+
When each StepMatch is returned, it contains the following data:
|
11
|
+
|
12
|
+
* `id` - identifier for the step definition to be used later when if it
|
13
|
+
needs to be invoked. The identifier can be any string value and
|
14
|
+
is simply used for the wire server's own reference.
|
15
|
+
* `args` - any argument values as captured by the wire end's own regular
|
16
|
+
expression (or other argument matching) process.
|
17
|
+
|
18
|
+
Background:
|
19
|
+
Given a file named "features/wired.feature" with:
|
20
|
+
"""
|
21
|
+
Feature: High strung
|
22
|
+
Scenario: Wired
|
23
|
+
Given we're all wired
|
24
|
+
|
25
|
+
"""
|
26
|
+
And a file named "features/step_definitions/some_remote_place.wire" with:
|
27
|
+
"""
|
28
|
+
host: localhost
|
29
|
+
port: 54321
|
30
|
+
|
31
|
+
"""
|
32
|
+
|
33
|
+
Scenario: Dry run finds no step match
|
34
|
+
Given there is a wire server running on port 54321 which understands the following protocol:
|
35
|
+
| request | response |
|
36
|
+
| ["step_matches",{"name_to_match":"we're all wired"}] | ["success",[]] |
|
37
|
+
When I run `cucumber --dry-run --no-snippets -f progress`
|
38
|
+
And it should pass with:
|
39
|
+
"""
|
40
|
+
U
|
41
|
+
|
42
|
+
1 scenario (1 undefined)
|
43
|
+
1 step (1 undefined)
|
44
|
+
|
45
|
+
"""
|
46
|
+
|
47
|
+
Scenario: Dry run finds a step match
|
48
|
+
Given there is a wire server running on port 54321 which understands the following protocol:
|
49
|
+
| request | response |
|
50
|
+
| ["step_matches",{"name_to_match":"we're all wired"}] | ["success",[{"id":"1", "args":[]}]] |
|
51
|
+
When I run `cucumber --dry-run -f progress`
|
52
|
+
And it should pass with:
|
53
|
+
"""
|
54
|
+
-
|
55
|
+
|
56
|
+
1 scenario (1 skipped)
|
57
|
+
1 step (1 skipped)
|
58
|
+
|
59
|
+
"""
|
60
|
+
|
61
|
+
Scenario: Step matches returns details about the remote step definition
|
62
|
+
|
63
|
+
Optionally, the StepMatch can also contain a source reference, and a native
|
64
|
+
regexp string which will be used by some formatters.
|
65
|
+
|
66
|
+
Given there is a wire server running on port 54321 which understands the following protocol:
|
67
|
+
| request | response |
|
68
|
+
| ["step_matches",{"name_to_match":"we're all wired"}] | ["success",[{"id":"1", "args":[], "source":"MyApp.MyClass:123", "regexp":"we.*"}]] |
|
69
|
+
When I run `cucumber -f stepdefs --dry-run`
|
70
|
+
Then it should pass with:
|
71
|
+
"""
|
72
|
+
-
|
73
|
+
|
74
|
+
we.* # MyApp.MyClass:123
|
75
|
+
|
76
|
+
1 scenario (1 skipped)
|
77
|
+
1 step (1 skipped)
|
78
|
+
|
79
|
+
"""
|
80
|
+
And the stderr should not contain anything
|
81
|
+
|
@@ -148,7 +148,7 @@ Feature: Cucumber --work-in-progress switch
|
|
148
148
|
The --wip switch was used, so I didn't expect anything to pass. These scenarios passed:
|
149
149
|
(::) passed scenarios (::)
|
150
150
|
|
151
|
-
features/passing_outline.feature:7:in `Scenario Outline: Passing, Examples (
|
151
|
+
features/passing_outline.feature:7:in `Scenario Outline: Passing, Examples (#1)'
|
152
152
|
|
153
153
|
|
154
154
|
"""
|
@@ -227,3 +227,34 @@ Feature: Around hooks
|
|
227
227
|
2 steps (2 passed)
|
228
228
|
|
229
229
|
"""
|
230
|
+
|
231
|
+
Scenario: Around Hooks and the Custom World
|
232
|
+
Given a file named "features/step_definitions/steps.rb" with:
|
233
|
+
"""
|
234
|
+
Then /^the world should be available in the hook$/ do
|
235
|
+
$previous_world = self
|
236
|
+
expect($hook_world).to eq(self)
|
237
|
+
end
|
238
|
+
|
239
|
+
Then /^what$/ do
|
240
|
+
expect($hook_world).not_to eq($previous_world)
|
241
|
+
end
|
242
|
+
"""
|
243
|
+
And a file named "features/support/hooks.rb" with:
|
244
|
+
"""
|
245
|
+
Around do |scenario, block|
|
246
|
+
$hook_world = self
|
247
|
+
block.call
|
248
|
+
end
|
249
|
+
"""
|
250
|
+
And a file named "features/f.feature" with:
|
251
|
+
"""
|
252
|
+
Feature: Around hooks
|
253
|
+
Scenario: using hook
|
254
|
+
Then the world should be available in the hook
|
255
|
+
|
256
|
+
Scenario: using the same hook
|
257
|
+
Then what
|
258
|
+
"""
|
259
|
+
When I run `cucumber features/f.feature`
|
260
|
+
Then it should pass
|
@@ -25,7 +25,8 @@ Feature: Before Hook
|
|
25
25
|
"""
|
26
26
|
NAMES:
|
27
27
|
Feature name
|
28
|
-
Scenario
|
28
|
+
Scenario name
|
29
|
+
|
29
30
|
"""
|
30
31
|
|
31
32
|
Scenario: Examine names of scenario outline and feature
|
@@ -46,7 +47,8 @@ Feature: Before Hook
|
|
46
47
|
Before do |scenario|
|
47
48
|
names << scenario.scenario_outline.feature.name.split("\n").first
|
48
49
|
names << scenario.scenario_outline.name.split("\n").first
|
49
|
-
|
50
|
+
names << scenario.name.split("\n").first
|
51
|
+
if(names.size == 3)
|
50
52
|
raise "NAMES:\n" + names.join("\n") + "\n"
|
51
53
|
end
|
52
54
|
end
|
@@ -56,6 +58,8 @@ Feature: Before Hook
|
|
56
58
|
"""
|
57
59
|
NAMES:
|
58
60
|
Feature name
|
59
|
-
Scenario Outline
|
61
|
+
Scenario Outline name, Examples Table name (#1)
|
62
|
+
Scenario Outline name, Examples Table name (#1)
|
63
|
+
|
60
64
|
"""
|
61
65
|
|
@@ -4,7 +4,7 @@ Feature: Tagged hooks
|
|
4
4
|
Given the standard step definitions
|
5
5
|
And a file named "features/support/hooks.rb" with:
|
6
6
|
"""
|
7
|
-
Before('~@no-boom') do
|
7
|
+
Before('~@no-boom') do
|
8
8
|
raise 'boom'
|
9
9
|
end
|
10
10
|
"""
|
@@ -17,12 +17,23 @@ Feature: Tagged hooks
|
|
17
17
|
@no-boom
|
18
18
|
Scenario: omitting hook
|
19
19
|
Given this step passes
|
20
|
+
|
21
|
+
Scenario Outline: omitting hook on specified examples
|
22
|
+
Given this step passes
|
23
|
+
|
24
|
+
Examples:
|
25
|
+
| Value |
|
26
|
+
| Irrelevant |
|
27
|
+
|
28
|
+
@no-boom
|
29
|
+
Examples:
|
30
|
+
| Value |
|
31
|
+
| Also Irrelevant |
|
20
32
|
"""
|
21
33
|
|
22
|
-
|
23
|
-
Scenario: Invoke tagged hook
|
34
|
+
Scenario: omit tagged hook
|
24
35
|
When I run `cucumber features/f.feature:2`
|
25
|
-
Then it should fail with:
|
36
|
+
Then it should fail with exactly:
|
26
37
|
"""
|
27
38
|
Feature: With and without hooks
|
28
39
|
|
@@ -36,12 +47,13 @@ Feature: Tagged hooks
|
|
36
47
|
|
37
48
|
1 scenario (1 failed)
|
38
49
|
1 step (1 skipped)
|
50
|
+
0m0.012s
|
39
51
|
|
40
52
|
"""
|
41
53
|
|
42
|
-
Scenario:
|
54
|
+
Scenario: omit tagged hook
|
43
55
|
When I run `cucumber features/f.feature:6`
|
44
|
-
Then it should pass with:
|
56
|
+
Then it should pass with exactly:
|
45
57
|
"""
|
46
58
|
Feature: With and without hooks
|
47
59
|
|
@@ -51,7 +63,33 @@ Feature: Tagged hooks
|
|
51
63
|
|
52
64
|
1 scenario (1 passed)
|
53
65
|
1 step (1 passed)
|
66
|
+
0m0.012s
|
54
67
|
|
55
68
|
"""
|
69
|
+
Scenario: Omit example hook
|
70
|
+
When I run `cucumber features/f.feature:12`
|
71
|
+
Then it should fail with exactly:
|
72
|
+
"""
|
73
|
+
Feature: With and without hooks
|
74
|
+
|
75
|
+
Scenario Outline: omitting hook on specified examples # features/f.feature:9
|
76
|
+
Given this step passes # features/f.feature:10
|
77
|
+
|
78
|
+
Examples:
|
79
|
+
| Value |
|
80
|
+
boom (RuntimeError)
|
81
|
+
./features/support/hooks.rb:2:in `Before'
|
82
|
+
| Irrelevant |
|
56
83
|
|
84
|
+
Failing Scenarios:
|
85
|
+
cucumber features/f.feature:14 # Scenario Outline: omitting hook on specified examples, Examples (#1)
|
86
|
+
|
87
|
+
1 scenario (1 failed)
|
88
|
+
1 step (1 skipped)
|
89
|
+
0m0.012s
|
90
|
+
|
91
|
+
"""
|
92
|
+
Scenario:
|
93
|
+
When I run `cucumber features/f.feature:17`
|
94
|
+
Then it should pass
|
57
95
|
|
@@ -17,9 +17,26 @@ Given /^I have environment variable (\w+) set to "([^"]*)"$/ do |variable, value
|
|
17
17
|
set_env(variable, value)
|
18
18
|
end
|
19
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
|
+
|
20
24
|
module WireHelper
|
25
|
+
attr_reader :messages_received
|
26
|
+
|
21
27
|
def start_wire_server
|
22
|
-
@
|
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
|
23
40
|
at_exit { stop_wire_server }
|
24
41
|
end
|
25
42
|
|
@@ -7,9 +7,9 @@ class FakeWireServer
|
|
7
7
|
@delays = {}
|
8
8
|
end
|
9
9
|
|
10
|
-
def run
|
10
|
+
def run(io)
|
11
11
|
@server = TCPServer.open(@port)
|
12
|
-
loop { handle_connections }
|
12
|
+
loop { handle_connections(io) }
|
13
13
|
end
|
14
14
|
|
15
15
|
def delay_response(message, delay)
|
@@ -18,13 +18,14 @@ class FakeWireServer
|
|
18
18
|
|
19
19
|
private
|
20
20
|
|
21
|
-
def handle_connections
|
22
|
-
Thread.start(@server.accept) { |socket| open_session_on socket }
|
21
|
+
def handle_connections(io)
|
22
|
+
Thread.start(@server.accept) { |socket| open_session_on socket, io }
|
23
23
|
end
|
24
24
|
|
25
|
-
def open_session_on(socket)
|
25
|
+
def open_session_on(socket, io)
|
26
26
|
begin
|
27
|
-
|
27
|
+
on_message = lambda { |message| io.puts message }
|
28
|
+
SocketSession.new(socket, @protocol_table, @delays, on_message).start
|
28
29
|
rescue Exception => e
|
29
30
|
raise e
|
30
31
|
ensure
|
@@ -33,10 +34,11 @@ class FakeWireServer
|
|
33
34
|
end
|
34
35
|
|
35
36
|
class SocketSession
|
36
|
-
def initialize(socket, protocol, delays)
|
37
|
+
def initialize(socket, protocol, delays, on_message)
|
37
38
|
@socket = socket
|
38
39
|
@protocol = protocol
|
39
40
|
@delays = delays
|
41
|
+
@on_message = on_message
|
40
42
|
end
|
41
43
|
|
42
44
|
def start
|
@@ -50,6 +52,7 @@ class FakeWireServer
|
|
50
52
|
def handle(data)
|
51
53
|
if protocol_entry = response_to(data.strip)
|
52
54
|
sleep delay(data)
|
55
|
+
@on_message.call(MultiJson.load(protocol_entry['request'])[0])
|
53
56
|
send_response(protocol_entry['response'])
|
54
57
|
else
|
55
58
|
serialized_exception = { :message => "Not understood: #{data}", :backtrace => [] }
|
data/lib/cucumber.rb
CHANGED
@@ -1,7 +1,5 @@
|
|
1
|
-
$:.unshift(File.dirname(__FILE__)) unless
|
2
|
-
$:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
|
3
|
-
|
4
1
|
require 'yaml'
|
2
|
+
require 'cucumber/encoding'
|
5
3
|
require 'cucumber/platform'
|
6
4
|
require 'cucumber/runtime'
|
7
5
|
require 'cucumber/cli/main'
|
data/lib/cucumber/cli/options.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
require 'cucumber/cli/profile_loader'
|
2
2
|
require 'cucumber/formatter/ansicolor'
|
3
3
|
require 'cucumber/rb_support/rb_language'
|
4
|
+
require 'cucumber/project_initializer'
|
4
5
|
|
5
6
|
module Cucumber
|
6
7
|
module Cli
|
@@ -126,6 +127,12 @@ module Cucumber
|
|
126
127
|
*FORMAT_HELP) do |v|
|
127
128
|
@options[:formats] << [v, @out_stream]
|
128
129
|
end
|
130
|
+
opts.on('--init',
|
131
|
+
'Initializes folder structure and generates conventional files for',
|
132
|
+
'a Cucumber project.') do |v|
|
133
|
+
ProjectInitializer.new.run
|
134
|
+
Kernel.exit(0)
|
135
|
+
end
|
129
136
|
opts.on("-o", "--out [FILE|DIR]",
|
130
137
|
"Write output to a file/directory instead of STDOUT. This option",
|
131
138
|
"applies to the previously specified --format, or the",
|