cucumber 0.4.2 → 0.4.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.
- data/History.txt +30 -4
- data/Rakefile +11 -8
- data/VERSION.yml +1 -1
- data/bin/cucumber +1 -2
- data/cucumber.gemspec +40 -38
- data/examples/i18n/da/features/{summering.feature → sammenlaegning.feature} +5 -5
- data/examples/i18n/da/features/step_definitons/{kalkulator_steps.rb → lommeregner_steps.rb} +3 -3
- data/examples/i18n/da/lib/{kalkulator.rb → lommeregner.rb} +2 -2
- data/examples/tickets/Rakefile +5 -1
- data/examples/tickets/features.html +138 -0
- data/examples/watir/Rakefile +4 -0
- data/examples/watir/features/{step_definitons → step_definitions}/search_steps.rb +0 -0
- data/examples/watir/features/support/screenshots.rb +45 -0
- data/features/announce.feature +122 -0
- data/features/html_formatter/a.html +1 -1
- data/features/step_definitions/cucumber_steps.rb +1 -1
- data/features/step_definitions/wire_steps.rb +14 -0
- data/features/support/env.rb +1 -1
- data/features/support/fake_wire_server.rb +63 -0
- data/features/tag_logic.feature +226 -0
- data/features/wire_protocol.feature +177 -0
- data/lib/cucumber/ast/examples.rb +4 -0
- data/lib/cucumber/ast/feature_element.rb +2 -1
- data/lib/cucumber/ast/scenario_outline.rb +4 -0
- data/lib/cucumber/ast/table.rb +13 -8
- data/lib/cucumber/ast/tags.rb +56 -12
- data/lib/cucumber/ast/tree_walker.rb +6 -0
- data/lib/cucumber/cli/main.rb +7 -5
- data/lib/cucumber/cli/options.rb +19 -10
- data/lib/cucumber/filter.rb +1 -2
- data/lib/cucumber/formatter/ansicolor.rb +17 -2
- data/lib/cucumber/formatter/console.rb +50 -32
- data/lib/cucumber/formatter/html.rb +21 -1
- data/lib/cucumber/formatter/junit.rb +8 -0
- data/lib/cucumber/formatter/pretty.rb +3 -0
- data/lib/cucumber/formatter/summary.rb +35 -0
- data/lib/cucumber/formatter/usage.rb +4 -4
- data/lib/cucumber/rails/active_record.rb +18 -10
- data/lib/cucumber/rb_support/rb_language.rb +7 -2
- data/lib/cucumber/rb_support/rb_world.rb +4 -0
- data/lib/cucumber/step_match.rb +3 -2
- data/lib/cucumber/step_mother.rb +6 -1
- data/lib/cucumber/wire_support/connection.rb +42 -0
- data/lib/cucumber/wire_support/request_handler.rb +19 -0
- data/lib/cucumber/wire_support/wire_exception.rb +10 -0
- data/lib/cucumber/wire_support/wire_language.rb +52 -0
- data/lib/cucumber/wire_support/wire_packet.rb +34 -0
- data/lib/cucumber/wire_support/wire_protocol.rb +64 -0
- data/lib/cucumber/wire_support/wire_step_definition.rb +21 -0
- data/rails_generators/cucumber/cucumber_generator.rb +7 -4
- data/rails_generators/cucumber/templates/cucumber_environment.rb +4 -4
- data/rails_generators/cucumber/templates/version_check.rb +6 -4
- data/spec/cucumber/ast/table_spec.rb +11 -1
- data/spec/cucumber/ast/tags_spec.rb +29 -0
- data/spec/cucumber/cli/options_spec.rb +8 -4
- data/spec/cucumber/formatter/junit_spec.rb +11 -0
- data/spec/cucumber/step_match_spec.rb +11 -0
- data/spec/cucumber/wire_support/wire_language_spec.rb +47 -0
- data/spec/cucumber/wire_support/wire_packet_spec.rb +26 -0
- metadata +38 -36
- data/examples/cs/.gitignore +0 -1
- data/examples/cs/README.textile +0 -1
- data/examples/cs/Rakefile +0 -12
- data/examples/cs/compile.bat +0 -1
- data/examples/cs/features/addition.feature +0 -16
- data/examples/cs/features/step_definitons/calculator_steps.rb +0 -19
- data/examples/cs/src/demo/Calculator.cs +0 -20
- data/examples/java/.gitignore +0 -1
- data/examples/java/README.textile +0 -18
- data/examples/java/build.xml +0 -33
- data/examples/java/features/hello.feature +0 -11
- data/examples/java/features/step_definitons/hello_steps.rb +0 -23
- data/examples/java/features/step_definitons/tree_steps.rb +0 -14
- data/examples/java/features/tree.feature +0 -9
- data/examples/java/src/.gitignore +0 -1
- data/examples/java/src/cucumber/demo/.gitignore +0 -1
- data/examples/java/src/cucumber/demo/Hello.java +0 -16
- data/examples/pure_java/README.textile +0 -5
@@ -0,0 +1,177 @@
|
|
1
|
+
@wire
|
2
|
+
Feature: Wire Protocol
|
3
|
+
In order to be allow Cucumber to touch my app in intimate places
|
4
|
+
As a developer on platform which doesn't support Ruby
|
5
|
+
I want a low-level protocol which Cucumber can use to run steps within my app
|
6
|
+
|
7
|
+
#
|
8
|
+
# Cucumber's wire protocol is an implementation of Cucumber's internal 'programming language' abstraction,
|
9
|
+
# and allows step definitions to be implemented and invoked on any platform.
|
10
|
+
#
|
11
|
+
# Communication is over a TCP socket, which Cucumber connects to when it finds a definition file with the
|
12
|
+
# .wire extension in the step_definitions folder (or other load path).
|
13
|
+
#
|
14
|
+
# There are currently two messages which Cucumber sends over the wire:
|
15
|
+
#
|
16
|
+
# * step_matches : this is used to find out whether the wire end has a definition for a given step
|
17
|
+
# * invoke : this is used to ask for a step definition to be invoked
|
18
|
+
#
|
19
|
+
# Message packets are formatted as JSON-encoded strings, with a newline character signalling the end of a
|
20
|
+
# packet. These messages are described below, with examples.
|
21
|
+
#
|
22
|
+
|
23
|
+
Background:
|
24
|
+
Given a standard Cucumber project directory structure
|
25
|
+
And a file named "features/wired.feature" with:
|
26
|
+
"""
|
27
|
+
Scenario: Wired
|
28
|
+
Given we're all wired
|
29
|
+
|
30
|
+
"""
|
31
|
+
And a file named "features/step_definitions/some_remote_place.wire" with:
|
32
|
+
"""
|
33
|
+
host: localhost
|
34
|
+
port: 98989
|
35
|
+
|
36
|
+
"""
|
37
|
+
|
38
|
+
|
39
|
+
#
|
40
|
+
# step_matches
|
41
|
+
#
|
42
|
+
# When the features have been parsed, Cucumber will send a step_matches message to ask the wire end
|
43
|
+
# if it can match a step name. This happens for each of the steps in each of the features.
|
44
|
+
# The wire end replies with a step_match array, containing the IDs of any step definitions that could
|
45
|
+
# be invoked for the given step name.
|
46
|
+
|
47
|
+
Scenario: Dry run finds no step match
|
48
|
+
Given there is a wire server running on port 98989 which understands the following protocol:
|
49
|
+
| request | response |
|
50
|
+
| ["step_matches",{"name_to_match":"we're all wired"}] | ["step_matches",[]] |
|
51
|
+
When I run cucumber --dry-run -f progress features
|
52
|
+
And it should pass with
|
53
|
+
"""
|
54
|
+
U
|
55
|
+
|
56
|
+
1 scenario (1 undefined)
|
57
|
+
1 step (1 undefined)
|
58
|
+
|
59
|
+
"""
|
60
|
+
|
61
|
+
# When a step match is returned, it contains an identifier for the step definition to be used
|
62
|
+
# later when referring to this step definition again if it needs to be invoked. The identifier
|
63
|
+
# can take any form (as long as it's within a string) and is simply used for the wire end's own
|
64
|
+
# reference.
|
65
|
+
# The step match also contains any argument values as parsed out by the wire end's own regular
|
66
|
+
# expression or other argument matching process.
|
67
|
+
Scenario: Dry run finds a step match
|
68
|
+
Given there is a wire server running on port 98989 which understands the following protocol:
|
69
|
+
| request | response |
|
70
|
+
| ["step_matches",{"name_to_match":"we're all wired"}] | ["step_matches",[{"id":"1", "args":[]}]] |
|
71
|
+
When I run cucumber --dry-run -f progress features
|
72
|
+
And it should pass with
|
73
|
+
"""
|
74
|
+
-
|
75
|
+
|
76
|
+
1 scenario (1 skipped)
|
77
|
+
1 step (1 skipped)
|
78
|
+
|
79
|
+
"""
|
80
|
+
|
81
|
+
|
82
|
+
#
|
83
|
+
# invoke
|
84
|
+
#
|
85
|
+
# Assuming a step_match was returned for a given step name, when it's time to invoke that
|
86
|
+
# step definition, Cucumber will send an invoke message.
|
87
|
+
# The message contains the ID of the step definition, as returned by the wire end from the
|
88
|
+
# step_matches call, along with the arguments that were parsed from the step name during the
|
89
|
+
# same step_matches call.
|
90
|
+
# The wire end will reply with either a step_failed or a success message.
|
91
|
+
|
92
|
+
Scenario: Invoke a step definition which passes
|
93
|
+
Given there is a wire server running on port 98989 which understands the following protocol:
|
94
|
+
| request | response |
|
95
|
+
| ["step_matches",{"name_to_match":"we're all wired"}] | ["step_matches",[{"id":"1", "args":[]}]] |
|
96
|
+
| ["begin_scenario",null] | ["success",null] |
|
97
|
+
| ["invoke",{"id":"1","args":[]}] | ["success",null] |
|
98
|
+
| ["end_scenario",null] | ["success",null] |
|
99
|
+
When I run cucumber -f progress --backtrace features
|
100
|
+
And it should pass with
|
101
|
+
"""
|
102
|
+
.
|
103
|
+
|
104
|
+
1 scenario (1 passed)
|
105
|
+
1 step (1 passed)
|
106
|
+
|
107
|
+
"""
|
108
|
+
|
109
|
+
# When a step definition fails, it can return details of the exception in the reply to invoke. These
|
110
|
+
# will then be passed by Cucumber to the formatters for display to the user.
|
111
|
+
#
|
112
|
+
Scenario: Invoke a step definition which fails
|
113
|
+
Given there is a wire server running on port 98989 which understands the following protocol:
|
114
|
+
| request | response |
|
115
|
+
| ["step_matches",{"name_to_match":"we're all wired"}] | ["step_matches",[{"id":"1", "args":[]}]] |
|
116
|
+
| ["begin_scenario",null] | ["success",null] |
|
117
|
+
| ["invoke",{"id":"1","args":[]}] | ["step_failed",{"message":"The wires are down"}] |
|
118
|
+
| ["end_scenario",null] | ["success",null] |
|
119
|
+
When I run cucumber -f progress features
|
120
|
+
Then STDERR should be empty
|
121
|
+
And it should fail with
|
122
|
+
"""
|
123
|
+
F
|
124
|
+
|
125
|
+
(::) failed steps (::)
|
126
|
+
|
127
|
+
The wires are down (Cucumber::WireSupport::WireException)
|
128
|
+
features/wired.feature:2:in `Given we're all wired'
|
129
|
+
|
130
|
+
Failing Scenarios:
|
131
|
+
cucumber features/wired.feature:1 # Scenario: Wired
|
132
|
+
|
133
|
+
1 scenario (1 failed)
|
134
|
+
1 step (1 failed)
|
135
|
+
|
136
|
+
"""
|
137
|
+
|
138
|
+
# Imagine we have a step definition like:
|
139
|
+
#
|
140
|
+
# Given /we're all (.*)/ do |what_we_are|
|
141
|
+
# end
|
142
|
+
#
|
143
|
+
# When this step definition matches the step name in our feature, the word 'wired' will be parsed as an
|
144
|
+
# argument.
|
145
|
+
#
|
146
|
+
# Cucumber expects this StepArgument to be returned in the StepMatch. The keys have the following meanings:
|
147
|
+
# * val : the value of the string captured for that argument from the step name passed in step_matches
|
148
|
+
# * pos : the position within the step name that the argument was matched (used for formatter highlighting)
|
149
|
+
#
|
150
|
+
Scenario: Invoke a step definition which takes arguments (and passes)
|
151
|
+
Given there is a wire server running on port 98989 which understands the following protocol:
|
152
|
+
| request | response |
|
153
|
+
| ["step_matches",{"name_to_match":"we're all wired"}] | ["step_matches",[{"id":"1", "args":[{"val":"wired", "pos":10}]}]] |
|
154
|
+
| ["begin_scenario",null] | ["success",null] |
|
155
|
+
| ["invoke",{"id":"1","args":["wired"]}] | ["success",null] |
|
156
|
+
| ["end_scenario",null] | ["success",null] |
|
157
|
+
When I run cucumber -f progress --backtrace features
|
158
|
+
Then STDERR should be empty
|
159
|
+
And it should pass with
|
160
|
+
"""
|
161
|
+
.
|
162
|
+
|
163
|
+
1 scenario (1 passed)
|
164
|
+
1 step (1 passed)
|
165
|
+
|
166
|
+
"""
|
167
|
+
|
168
|
+
|
169
|
+
Scenario: Unexpected response
|
170
|
+
Given there is a wire server running on port 98989 which understands the following protocol:
|
171
|
+
| request | response |
|
172
|
+
| ["begin_scenario",null] | ["yikes"] |
|
173
|
+
When I run cucumber -f progress features
|
174
|
+
Then STDERR should match
|
175
|
+
"""
|
176
|
+
undefined method `handle_yikes'
|
177
|
+
"""
|
@@ -49,7 +49,8 @@ module Cucumber
|
|
49
49
|
end
|
50
50
|
|
51
51
|
def accept_hook?(hook)
|
52
|
-
|
52
|
+
parsed_hook_tag_names = hook.tag_names.map {|tag_string| Cli::Options.parse_tag_arguments(tag_string)}
|
53
|
+
Tags.matches?(source_tag_names, parsed_hook_tag_names)
|
53
54
|
end
|
54
55
|
|
55
56
|
def source_tag_names
|
data/lib/cucumber/ast/table.rb
CHANGED
@@ -39,7 +39,8 @@ module Cucumber
|
|
39
39
|
"table"
|
40
40
|
end
|
41
41
|
|
42
|
-
# Creates a new instance. +raw+ should be an Array of Array of String
|
42
|
+
# Creates a new instance. +raw+ should be an Array of Array of String
|
43
|
+
# or an Array of Hash (similar to what #hashes returns).
|
43
44
|
# You don't typically create your own Table objects - Cucumber will do
|
44
45
|
# it internally and pass them to your Step Definitions.
|
45
46
|
#
|
@@ -47,12 +48,18 @@ module Cucumber
|
|
47
48
|
@cells_class = Cells
|
48
49
|
@cell_class = Cell
|
49
50
|
|
51
|
+
raw = ensure_array_of_array(raw)
|
50
52
|
# Verify that it's square
|
51
53
|
transposed = raw.transpose
|
52
54
|
create_cell_matrix(raw)
|
53
55
|
@conversion_procs = conversion_procs
|
54
56
|
end
|
55
57
|
|
58
|
+
# JSON representation
|
59
|
+
def to_json
|
60
|
+
raw.to_json
|
61
|
+
end
|
62
|
+
|
56
63
|
# Creates a copy of this table, inheriting any column mappings.
|
57
64
|
# registered with #map_headers!
|
58
65
|
#
|
@@ -261,7 +268,7 @@ module Cucumber
|
|
261
268
|
# objects in their cells, you may want to use #map_column! before calling
|
262
269
|
# #diff!. You can use #map_column! on either of the tables.
|
263
270
|
#
|
264
|
-
#
|
271
|
+
# A Different error is raised if there are missing rows or columns, or
|
265
272
|
# surplus rows. An error is <em>not</em> raised for surplus columns.
|
266
273
|
# Whether to raise or not raise can be changed by setting values in
|
267
274
|
# +options+ to true or false:
|
@@ -524,20 +531,18 @@ module Cucumber
|
|
524
531
|
|
525
532
|
def ensure_table(table_or_array) #:nodoc:
|
526
533
|
return table_or_array if Table === table_or_array
|
527
|
-
table_or_array = hashes_to_array(table_or_array) if Hash === table_or_array[0]
|
528
|
-
table_or_array = enumerable_to_array(table_or_array) unless Array == table_or_array[0]
|
529
534
|
Table.new(table_or_array)
|
530
535
|
end
|
531
536
|
|
537
|
+
def ensure_array_of_array(array)
|
538
|
+
Hash === array[0] ? hashes_to_array(array) : array
|
539
|
+
end
|
540
|
+
|
532
541
|
def hashes_to_array(hashes) #:nodoc:
|
533
542
|
header = hashes[0].keys
|
534
543
|
[header] + hashes.map{|hash| header.map{|key| hash[key]}}
|
535
544
|
end
|
536
545
|
|
537
|
-
def enumerable_to_array(rows) #:nodoc:
|
538
|
-
rows.map{|row| row.map{|cell| cell}}
|
539
|
-
end
|
540
|
-
|
541
546
|
def ensure_green! #:nodoc:
|
542
547
|
each_cell{|cell| cell.status = :passed}
|
543
548
|
end
|
data/lib/cucumber/ast/tags.rb
CHANGED
@@ -1,5 +1,8 @@
|
|
1
|
+
require 'set'
|
2
|
+
|
1
3
|
module Cucumber
|
2
4
|
module Ast
|
5
|
+
|
3
6
|
# Holds the names of tags parsed from a feature file:
|
4
7
|
#
|
5
8
|
# @invoice @release_2
|
@@ -7,33 +10,74 @@ module Cucumber
|
|
7
10
|
# This gets stored internally as <tt>["invoice", "release_2"]</tt>
|
8
11
|
#
|
9
12
|
class Tags #:nodoc:
|
13
|
+
|
14
|
+
class And #:nodoc:
|
15
|
+
def initialize(tag_names)
|
16
|
+
@negative_tags, @positive_tags = tag_names.partition{|tag_name| Tags.exclude_tag?(tag_name)}
|
17
|
+
@negative_tags = Tags.strip_negative_char(@negative_tags)
|
18
|
+
end
|
19
|
+
|
20
|
+
def matches?(tag_names)
|
21
|
+
included?(tag_names) && !excluded?(tag_names)
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def excluded?(tag_names)
|
27
|
+
(@negative_tags & tag_names).any?
|
28
|
+
end
|
29
|
+
|
30
|
+
def included?(tag_names)
|
31
|
+
positive_tag_set = Set.new(@positive_tags)
|
32
|
+
tag_names_set = Set.new(tag_names)
|
33
|
+
positive_tag_set.subset?(tag_names_set)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
class Or #:nodoc:
|
38
|
+
def initialize(tag_exp)
|
39
|
+
@exp = tag_exp
|
40
|
+
end
|
41
|
+
|
42
|
+
def matches?(tag_names)
|
43
|
+
@exp.inject(false){|matches, tag_exp| matches || tag_exp.matches?(tag_names)}
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
10
47
|
class << self
|
11
48
|
EXCLUDE_PATTERN = /^~/
|
12
49
|
|
13
50
|
def matches?(source_tag_names, tag_names)
|
14
|
-
|
15
|
-
|
16
|
-
check_at_sign_prefix(exclude_tag_names + include_tag_names)
|
17
|
-
!excluded?(source_tag_names, exclude_tag_names) && included?(source_tag_names, include_tag_names)
|
51
|
+
validate_tags(tag_names)
|
52
|
+
tag_names.empty? ? true : check_if_tags_match(source_tag_names, tag_names)
|
18
53
|
end
|
19
54
|
|
20
55
|
def exclude_tag?(tag_name)
|
21
56
|
tag_name =~ EXCLUDE_PATTERN
|
22
57
|
end
|
23
|
-
|
58
|
+
|
59
|
+
def strip_negative_char(tag_names)
|
60
|
+
tag_names.map{|name| name[1..-1]}
|
61
|
+
end
|
62
|
+
|
24
63
|
private
|
25
64
|
|
26
|
-
def
|
27
|
-
|
65
|
+
def validate_tags(tag_name_list)
|
66
|
+
all_tag_names = tag_name_list.flatten
|
67
|
+
exclude_tag_names, include_tag_names = all_tag_names.partition{|tag_name| exclude_tag?(tag_name)}
|
68
|
+
exclude_tag_names = strip_negative_char(exclude_tag_names)
|
69
|
+
check_at_sign_prefix(exclude_tag_names + include_tag_names)
|
28
70
|
end
|
29
71
|
|
30
|
-
def
|
31
|
-
|
72
|
+
def check_if_tags_match(source_tag_names, tag_names)
|
73
|
+
tag_exp = Or.new(tag_names.map{|tag_name_list| And.new(tag_name_list) })
|
74
|
+
tag_exp.matches?(source_tag_names)
|
32
75
|
end
|
33
|
-
|
34
|
-
def
|
35
|
-
|
76
|
+
|
77
|
+
def check_at_sign_prefix(tag_names)
|
78
|
+
tag_names.each{|tag_name| raise "Tag names must start with an @ sign. The following tag name didn't: #{tag_name}" unless tag_name[0..0] == '@'}
|
36
79
|
end
|
80
|
+
|
37
81
|
end
|
38
82
|
|
39
83
|
attr_reader :tag_names
|
@@ -146,6 +146,12 @@ module Cucumber
|
|
146
146
|
def announce(announcement)
|
147
147
|
broadcast(announcement)
|
148
148
|
end
|
149
|
+
|
150
|
+
# Embed +file+ of +mime_type+ in the formatter. This method can be called from within StepDefinitions.
|
151
|
+
# For most formatters this is a no-op.
|
152
|
+
def embed(file, mime_type)
|
153
|
+
broadcast(file, mime_type)
|
154
|
+
end
|
149
155
|
|
150
156
|
private
|
151
157
|
|
data/lib/cucumber/cli/main.rb
CHANGED
@@ -69,11 +69,13 @@ module Cucumber
|
|
69
69
|
|
70
70
|
def exceeded_tag_limts?(features)
|
71
71
|
exceeded = false
|
72
|
-
configuration.options[:tag_names].each do |
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
72
|
+
configuration.options[:tag_names].each do |tag_list|
|
73
|
+
tag_list.each do |tag_name, limit|
|
74
|
+
if !Ast::Tags.exclude_tag?(tag_name) && limit
|
75
|
+
tag_count = features.tag_count(tag_name)
|
76
|
+
if tag_count > limit.to_i
|
77
|
+
exceeded = true
|
78
|
+
end
|
77
79
|
end
|
78
80
|
end
|
79
81
|
end
|
data/lib/cucumber/cli/options.rb
CHANGED
@@ -6,14 +6,14 @@ module Cucumber
|
|
6
6
|
BUILTIN_FORMATS = {
|
7
7
|
'html' => ['Cucumber::Formatter::Html', 'Generates a nice looking HTML report.'],
|
8
8
|
'pretty' => ['Cucumber::Formatter::Pretty', 'Prints the feature as is - in colours.'],
|
9
|
-
'pdf' => ['Cucumber::Formatter::Pdf', "Generates a PDF report. You need to have the\n" +
|
10
|
-
"#{' ' * 51}prawn gem installed. Will pick up logo from\n" +
|
9
|
+
'pdf' => ['Cucumber::Formatter::Pdf', "Generates a PDF report. You need to have the\n" +
|
10
|
+
"#{' ' * 51}prawn gem installed. Will pick up logo from\n" +
|
11
11
|
"#{' ' * 51}features/support/logo.png or\n" +
|
12
12
|
"#{' ' * 51}features/support/logo.jpg if present."],
|
13
13
|
'progress' => ['Cucumber::Formatter::Progress', 'Prints one character per scenario.'],
|
14
14
|
'rerun' => ['Cucumber::Formatter::Rerun', 'Prints failing files with line numbers.'],
|
15
15
|
'usage' => ['Cucumber::Formatter::Usage', "Prints where step definitions are used.\n" +
|
16
|
-
"#{' ' * 51}The slowest step definitions (with duration) are\n" +
|
16
|
+
"#{' ' * 51}The slowest step definitions (with duration) are\n" +
|
17
17
|
"#{' ' * 51}listed first. If --dry-run is used the duration\n" +
|
18
18
|
"#{' ' * 51}is not shown, and step definitions are sorted by\n" +
|
19
19
|
"#{' ' * 51}filename instead."],
|
@@ -48,6 +48,10 @@ module Cucumber
|
|
48
48
|
new(out_stream, error_stream, options).parse!(args)
|
49
49
|
end
|
50
50
|
|
51
|
+
def self.parse_tag_arguments(tags_string)
|
52
|
+
tags_string.split(',')
|
53
|
+
end
|
54
|
+
|
51
55
|
def initialize(out_stream = STDOUT, error_stream = STDERR, options = {})
|
52
56
|
@out_stream = out_stream
|
53
57
|
@error_stream = error_stream
|
@@ -141,13 +145,18 @@ module Cucumber
|
|
141
145
|
opts.on("-t TAGS", "--tags TAGS",
|
142
146
|
"Only execute the features or scenarios with the specified tags.",
|
143
147
|
"TAGS must be comma-separated without spaces. Example: --tags @dev\n",
|
148
|
+
"You can select tags using logical AND or logical OR:",
|
149
|
+
"To execute anything that is tagged with both @dev AND @prod\n",
|
150
|
+
"Example: --tags @dev,@prod",
|
151
|
+
"To execute anything that is tagged with @dev OR @prod\n",
|
152
|
+
"Example: --tags @dev --tags @prod\n",
|
144
153
|
"Negative tags: Prefix tags with ~ to exclude features or scenarios",
|
145
154
|
"having that tag. Example: --tags ~@slow\n",
|
146
155
|
"Limit WIP: Positive tags can be given a threshold to limit the",
|
147
156
|
"number of occurrences. Example: --tags @qa:3 will fail if there",
|
148
157
|
"are more than 3 occurrences of the @qa tag.") do |v|
|
149
158
|
tag_names = parse_tags(v)
|
150
|
-
@options[:tag_names]
|
159
|
+
@options[:tag_names] << tag_names
|
151
160
|
end
|
152
161
|
opts.on("-n NAME", "--name NAME",
|
153
162
|
"Only execute the feature elements which match part of the given name.",
|
@@ -158,7 +167,7 @@ module Cucumber
|
|
158
167
|
opts.on("-e", "--exclude PATTERN", "Don't run feature files or require ruby files matching PATTERN") do |v|
|
159
168
|
@options[:excludes] << Regexp.new(v)
|
160
169
|
end
|
161
|
-
opts.on(PROFILE_SHORT_FLAG, "#{PROFILE_LONG_FLAG} PROFILE",
|
170
|
+
opts.on(PROFILE_SHORT_FLAG, "#{PROFILE_LONG_FLAG} PROFILE",
|
162
171
|
"Pull commandline arguments from cucumber.yml which can be defined as",
|
163
172
|
"strings or arrays. When a 'default' profile is defined and no profile",
|
164
173
|
"is specified it is always used. (Unless disabled, see -P below.)",
|
@@ -241,7 +250,7 @@ module Cucumber
|
|
241
250
|
Kernel.exit(0)
|
242
251
|
end
|
243
252
|
end.parse!
|
244
|
-
|
253
|
+
|
245
254
|
if @quiet
|
246
255
|
@options[:snippets] = @options[:source] = false
|
247
256
|
else
|
@@ -283,10 +292,10 @@ module Cucumber
|
|
283
292
|
end
|
284
293
|
|
285
294
|
def parse_tags(tag_string)
|
286
|
-
tag_names =
|
295
|
+
tag_names = Options.parse_tag_arguments(tag_string)
|
287
296
|
parse_tag_limits(tag_names)
|
288
297
|
end
|
289
|
-
|
298
|
+
|
290
299
|
def parse_tag_limits(tag_names)
|
291
300
|
tag_names.inject({}) do |dict, tag|
|
292
301
|
tag, limit = tag.split(':')
|
@@ -331,7 +340,7 @@ module Cucumber
|
|
331
340
|
@options[:require] += other_options[:require]
|
332
341
|
@options[:excludes] += other_options[:excludes]
|
333
342
|
@options[:name_regexps] += other_options[:name_regexps]
|
334
|
-
@options[:tag_names]
|
343
|
+
@options[:tag_names] += other_options[:tag_names]
|
335
344
|
@options[:env_vars] = other_options[:env_vars].merge(@options[:env_vars])
|
336
345
|
if @options[:paths].empty?
|
337
346
|
@options[:paths] = other_options[:paths]
|
@@ -384,7 +393,7 @@ module Cucumber
|
|
384
393
|
:dry_run => false,
|
385
394
|
:formats => [],
|
386
395
|
:excludes => [],
|
387
|
-
:tag_names =>
|
396
|
+
:tag_names => [],
|
388
397
|
:name_regexps => [],
|
389
398
|
:env_vars => {},
|
390
399
|
:diff_enabled => true
|