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.
Files changed (61) hide show
  1. checksums.yaml +4 -4
  2. data/History.md +74 -52
  3. data/examples/i18n/ht/Rakefile +6 -0
  4. data/examples/i18n/ht/features/adisyon.feature +17 -0
  5. data/examples/i18n/ht/features/divizyon.feature +10 -0
  6. data/examples/i18n/ht/features/step_definitions/kalkilatris_steps.rb +25 -0
  7. data/examples/i18n/ht/lib/kalkilatris.rb +14 -0
  8. data/features/docs/cli/dry_run.feature +43 -0
  9. data/features/docs/cli/strict_mode.feature +24 -1
  10. data/features/docs/defining_steps/nested_steps.feature +49 -0
  11. data/features/docs/exception_in_after_step_hook.feature +1 -1
  12. data/features/docs/exception_in_around_hook.feature +80 -0
  13. data/features/docs/formatters/json_formatter.feature +65 -1
  14. data/features/docs/formatters/junit_formatter.feature +40 -0
  15. data/features/docs/{wire_protocol_erb.feature → wire_protocol/erb_configuration.feature} +2 -2
  16. data/features/docs/wire_protocol/handle_unexpected_response.feature +30 -0
  17. data/features/docs/wire_protocol/invoke_message.feature +216 -0
  18. data/features/docs/wire_protocol/readme.md +26 -0
  19. data/features/docs/wire_protocol/snippets_message.feature +51 -0
  20. data/features/docs/wire_protocol/step_matches_message.feature +81 -0
  21. data/features/docs/{wire_protocol_table_diffing.feature → wire_protocol/table_diffing.feature} +1 -0
  22. data/features/docs/{wire_protocol_tags.feature → wire_protocol/tags.feature} +1 -0
  23. data/features/docs/{wire_protocol_timeouts.feature → wire_protocol/timeouts.feature} +1 -0
  24. data/features/docs/work_in_progress.feature +1 -1
  25. data/features/docs/writing_support_code/around_hooks.feature +31 -0
  26. data/features/docs/writing_support_code/before_hook.feature +7 -3
  27. data/features/docs/writing_support_code/tagged_hooks.feature +44 -6
  28. data/features/lib/step_definitions/wire_steps.rb +18 -1
  29. data/features/lib/support/fake_wire_server.rb +10 -7
  30. data/lib/cucumber.rb +1 -3
  31. data/lib/cucumber/cli/options.rb +7 -0
  32. data/lib/cucumber/encoding.rb +5 -0
  33. data/lib/cucumber/errors.rb +8 -0
  34. data/lib/cucumber/filters/prepare_world.rb +13 -5
  35. data/lib/cucumber/formatter/console.rb +1 -1
  36. data/lib/cucumber/formatter/gherkin_formatter_adapter.rb +9 -6
  37. data/lib/cucumber/formatter/junit.rb +95 -0
  38. data/lib/cucumber/formatter/legacy_api/adapter.rb +39 -3
  39. data/lib/cucumber/platform.rb +1 -1
  40. data/lib/cucumber/project_initializer.rb +43 -0
  41. data/lib/cucumber/rb_support/rb_step_definition.rb +11 -2
  42. data/lib/cucumber/rb_support/rb_world.rb +2 -2
  43. data/lib/cucumber/rb_support/snippet.rb +1 -1
  44. data/lib/cucumber/rspec/doubles.rb +1 -1
  45. data/lib/cucumber/running_test_case.rb +115 -0
  46. data/lib/cucumber/runtime.rb +5 -1
  47. data/lib/cucumber/runtime/for_programming_languages.rb +2 -2
  48. data/lib/cucumber/runtime/support_code.rb +18 -8
  49. data/spec/cucumber/formatter/junit_spec.rb +212 -156
  50. data/spec/cucumber/formatter/legacy_api/adapter_spec.rb +89 -1
  51. data/spec/cucumber/formatter/pretty_spec.rb +13 -0
  52. data/spec/cucumber/project_initializer_spec.rb +87 -0
  53. data/spec/cucumber/rb_support/rb_step_definition_spec.rb +21 -3
  54. data/spec/cucumber/rb_support/snippet_spec.rb +6 -6
  55. data/spec/cucumber/running_test_case_spec.rb +83 -0
  56. data/spec/spec_helper.rb +1 -4
  57. metadata +35 -18
  58. data/bin/cuke +0 -60
  59. data/features/docs/report_called_undefined_steps.feature +0 -57
  60. data/features/docs/wire_protocol.feature +0 -337
  61. 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
+
@@ -1,5 +1,6 @@
1
1
  @wire
2
2
  Feature: Wire protocol table diffing
3
+
3
4
  In order to use the amazing functionality in the Cucumber table object
4
5
  As a wire server
5
6
  I want to be able to ask for a table diff during a step definition invocation
@@ -1,5 +1,6 @@
1
1
  @wire
2
2
  Feature: Wire protocol tags
3
+
3
4
  In order to use Before and After hooks in a wire server, we send tags with the
4
5
  scenario in the begin_scenario and end_scenario messages
5
6
 
@@ -1,5 +1,6 @@
1
1
  @wire
2
2
  Feature: Wire protocol timeouts
3
+
3
4
  We don't want Cucumber to hang forever on a wire server that's not even there,
4
5
  but equally we need to give the user the flexibility to allow step definitions
5
6
  to take a while to execute, if that's what they need.
@@ -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 (row 1)'
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: Scenario name
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
- if(names.size == 2)
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: Scenario Outline name
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
- @spawn
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: Omit tagged hook
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
- @wire_pid = fork { @server.run }
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
- SocketSession.new(socket, @protocol_table, @delays).start
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 => [] }
@@ -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'
@@ -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",