Skip to content

Commit ee317ba

Browse files
committed
Use tag expressions from the cucumber-tag_expressions gem.
1 parent cd3fd63 commit ee317ba

File tree

10 files changed

+104
-49
lines changed

10 files changed

+104
-49
lines changed

cucumber.yml

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,19 @@ cucumber_pro_opts = ENV['ENABLE_CUCUMBER_PRO'] ? "--format Cucumber::Pro --out /
33
rerun = File.file?('rerun.txt') ? IO.read('rerun.txt') : ""
44
rerun_opts = rerun.to_s.strip.empty? ? "--format progress features" : "--format pretty #{rerun}"
55
std_opts = "--format progress features -r features --strict #{cucumber_pro_opts}"
6-
std_opts << " --tags ~@wip-jruby" if defined?(JRUBY_VERSION)
6+
std_opts << " --tags 'not @wip-jruby'" if defined?(JRUBY_VERSION)
77
8-
wip_opts = "--color -r features --tags @wip"
9-
wip_opts << ",@wip-jruby" if defined?(JRUBY_VERSION)
8+
wip_opts = "--color -r features"
9+
wip_opts << " --tags @wip" if !defined?(JRUBY_VERSION)
10+
wip_opts << " --tags '@wip or @wip-jruby'" if defined?(JRUBY_VERSION)
1011
%>
11-
default: <%= std_opts %> --tags ~@jruby
12-
jruby: <%= std_opts %> --tags ~@wire
13-
jruby_win: <%= std_opts %> --tags ~@wire CUCUMBER_FORWARD_SLASH_PATHS=true
14-
windows_mri: <%= std_opts %> --tags ~@jruby --tags ~@wire --tags ~@needs-many-fonts CUCUMBER_FORWARD_SLASH_PATHS=true
15-
ruby_1_9: <%= std_opts %> --tags ~@jruby
16-
ruby_2_0: <%= std_opts %> --tags ~@jruby
17-
ruby_2_1: <%= std_opts %> --tags ~@jruby
12+
default: <%= std_opts %> --tags 'not @jruby'
13+
jruby: <%= std_opts %> --tags 'not @wire'
14+
jruby_win: <%= std_opts %> --tags 'not @wire' CUCUMBER_FORWARD_SLASH_PATHS=true
15+
windows_mri: <%= std_opts %> --tags 'not @jruby and not @wire and not @needs-many-fonts' CUCUMBER_FORWARD_SLASH_PATHS=true
16+
ruby_1_9: <%= std_opts %> --tags 'not @jruby'
17+
ruby_2_0: <%= std_opts %> --tags 'not @jruby'
18+
ruby_2_1: <%= std_opts %> --tags 'not @jruby'
1819
wip: --wip <%= wip_opts %> features <%= cucumber_pro_opts %>
1920
none: --format pretty --format Cucumber::Pro --out /dev/null
20-
rerun: <%= rerun_opts %> --format rerun --out rerun.txt --strict --tag ~@wip-new-core <%= cucumber_pro_opts %>
21+
rerun: <%= rerun_opts %> --format rerun --out rerun.txt --strict --tags 'not @wip-new-core' <%= cucumber_pro_opts %>

features/docs/cli/execute_with_tag_filter.feature

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ Feature: Tag logic
2626
"""
2727

2828
Scenario: ANDing tags
29-
When I run `cucumber -q -t @one -t @three features/test.feature`
29+
When I run `cucumber -q -t '@one and @three' features/test.feature`
3030
Then it should pass with:
3131
"""
3232
@feature
@@ -42,7 +42,7 @@ Feature: Tag logic
4242
"""
4343

4444
Scenario: ORing tags
45-
When I run `cucumber -q -t @one,@three features/test.feature`
45+
When I run `cucumber -q -t '@one or @three' features/test.feature`
4646
Then it should pass with:
4747
"""
4848
@feature
@@ -66,7 +66,7 @@ Feature: Tag logic
6666
"""
6767

6868
Scenario: Negative tags
69-
When I run `cucumber -q -t ~@three features/test.feature`
69+
When I run `cucumber -q -t 'not @three' features/test.feature`
7070
Then it should pass with:
7171
"""
7272
@feature
@@ -104,7 +104,7 @@ Feature: Tag logic
104104
"""
105105

106106
Scenario: Run with limited tag count using negative tag, blowing it via a tag that is not run
107-
When I run `cucumber -q --no-source --tags ~@one:1 features/test.feature`
107+
When I run `cucumber -q --no-source --tags 'not @one:1' features/test.feature`
108108
Then it fails before running features with:
109109
"""
110110
@one occurred 2 times, but the limit was set to 1

features/docs/writing_support_code/tagged_hooks.feature

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ Feature: Tagged hooks
44
Given the standard step definitions
55
And a file named "features/support/hooks.rb" with:
66
"""
7-
Before('~@no-boom') do
7+
Before('not @no-boom') do
88
raise 'boom'
99
end
1010
"""

features/lib/support/env.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
Aruba.process = Aruba::SpawnProcess
99
end
1010

11-
Before('~@spawn') do
11+
Before('not @spawn') do
1212
Aruba::InProcess.main_class = Cucumber::Cli::Main
1313
Aruba.process = Aruba::InProcess
1414
end

lib/cucumber/cli/configuration.rb

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
require 'cucumber/cli/options'
44
require 'cucumber/cli/rerun_file'
55
require 'cucumber/constantize'
6-
require 'cucumber/core/gherkin/tag_expression'
76
require 'cucumber'
87

98
module Cucumber
@@ -28,8 +27,6 @@ def parse!(args)
2827
@options.parse!(args)
2928
arrange_formats
3029
raise("You can't use both --strict and --wip") if strict? && wip?
31-
# todo: remove
32-
@options[:tag_expression] = Cucumber::Core::Gherkin::TagExpression.new(@options[:tag_expressions])
3330
set_environment_variables
3431
end
3532

@@ -85,13 +82,8 @@ def log
8582
logger
8683
end
8784

88-
# todo: remove
89-
def tag_expression
90-
Cucumber::Core::Gherkin::TagExpression.new(@options[:tag_expressions])
91-
end
92-
9385
def tag_limits
94-
tag_expression.limits.to_hash
86+
@options[:tag_limits]
9587
end
9688

9789
def tag_expressions

lib/cucumber/cli/options.rb

Lines changed: 39 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ class Options
5454
'-l', '--lines', '--port',
5555
'-I', '--snippet-type']
5656
ORDER_TYPES = %w{defined random}
57+
TAG_LIMIT_MATCHER = /(?<tag_name>\@\w+):(?<limit>\d+)/x
5758

5859
def self.parse(args, out_stream, error_stream, options = {})
5960
new(out_stream, error_stream, options).parse!(args)
@@ -101,7 +102,7 @@ def parse!(args)
101102
opts.on('-f FORMAT', '--format FORMAT', *format_msg, *FORMAT_HELP) {|v| add_option :formats, [v, @out_stream] }
102103
opts.on('--init', *init_msg) {|v| initialize_project }
103104
opts.on('-o', '--out [FILE|DIR]', *out_msg) {|v| set_out_stream v }
104-
opts.on('-t TAG_EXPRESSION', '--tags TAG_EXPRESSION', *tags_msg) {|v| add_option :tag_expressions, v }
105+
opts.on('-t TAG_EXPRESSION', '--tags TAG_EXPRESSION', *tags_msg) {|v| add_tag v }
105106
opts.on('-n NAME', '--name NAME', *name_msg) {|v| add_option :name_regexps, /#{v}/ }
106107
opts.on('-e', '--exclude PATTERN', *exclude_msg) {|v| add_option :excludes, Regexp.new(v) }
107108
opts.on(PROFILE_SHORT_FLAG, "#{PROFILE_LONG_FLAG} PROFILE", *profile_short_flag_msg) {|v| add_profile v }
@@ -253,17 +254,20 @@ def tags_msg
253254
[
254255
'Only execute the features or scenarios with tags matching TAG_EXPRESSION.',
255256
'Scenarios inherit tags declared on the Feature level. The simplest',
256-
'TAG_EXPRESSION is simply a tag. Example: --tags @dev. When a tag in a tag',
257-
'expression starts with a ~, this represents boolean NOT. Example: --tags ~@dev.',
258-
'A tag expression can have several tags separated by a comma, which represents',
259-
'logical OR. Example: --tags @dev,@wip. The --tags option can be specified',
260-
'several times, and this represents logical AND. Example: --tags @foo,~@bar --tags @zap.',
261-
'This represents the boolean expression (@foo || !@bar) && @zap.',
257+
'TAG_EXPRESSION is simply a tag. Example: --tags @dev. To represent',
258+
"boolean NOT preceed the tag with 'not '. Example: --tags 'not @dev'.",
259+
'A tag expression can have several tags separated by an or which represents',
260+
"logical OR. Example: --tags '@dev or @wip'. The --tags option can be specified",
261+
'A tag expression can have several tags separated by an and which represents',
262+
"logical AND. Example: --tags '@dev and @wip'. The --tags option can be specified",
263+
'several times, and this also represents logical AND.',
264+
"Example: --tags '@foo or not @bar' --tags @zap. This represents the boolean",
265+
'expression (@foo || !@bar) && @zap.',
262266
"\n",
263267
'Beware that if you want to use several negative tags to exclude several tags',
264-
'you have to use logical AND: --tags ~@fixme --tags ~@buggy.',
268+
"you have to use logical AND: --tags 'not @fixme and not @buggy'.",
265269
"\n",
266-
'Positive tags can be given a threshold to limit the number of occurrences.',
270+
'Tags can be given a threshold to limit the number of occurrences.',
267271
'Example: --tags @qa:3 will fail if there are more than 3 occurrences of the @qa tag.',
268272
'This can be practical if you are practicing Kanban or CONWIP.'
269273
]
@@ -343,6 +347,26 @@ def add_option(option, value)
343347
@options[option] << value
344348
end
345349

350+
def add_tag(value)
351+
warn("Deprecated: Found tags option '#{value}'. Support for '~@tag' will be removed from the next release of Cucumber. Please use 'not @tag' instead.") if value.include?('~')
352+
warn("Deprecated: Found tags option '#{value}'. Support for '@tag1,@tag2' will be removed from the next release of Cucumber. Please use '@tag or @tag2' instead.") if value.include?(',')
353+
@options[:tag_expressions] << value.gsub(/(@\w+)(:\d+)?/, '\1')
354+
add_tag_limits(value)
355+
end
356+
357+
def add_tag_limits(value)
358+
value.split(/[, ]/).map { |part| TAG_LIMIT_MATCHER.match(part) }.compact.each do |matchdata|
359+
add_tag_limit(@options[:tag_limits], matchdata[:tag_name], matchdata[:limit].to_i)
360+
end
361+
end
362+
363+
def add_tag_limit(tag_limits, tag_name, limit)
364+
if tag_limits[tag_name] && tag_limits[tag_name] != limit
365+
raise "Inconsistent tag limits for #{tag_name}: #{tag_limits[tag_name]} and #{limit}"
366+
end
367+
tag_limits[tag_name] = limit
368+
end
369+
346370
def set_color(color)
347371
Cucumber::Term::ANSIColor.coloring = color
348372
end
@@ -433,6 +457,7 @@ def reverse_merge(other_options)
433457
@options[:excludes] += other_options[:excludes]
434458
@options[:name_regexps] += other_options[:name_regexps]
435459
@options[:tag_expressions] += other_options[:tag_expressions]
460+
merge_tag_limits(@options[:tag_limits], other_options[:tag_limits])
436461
@options[:env_vars] = other_options[:env_vars].merge(@options[:env_vars])
437462
if @options[:paths].empty?
438463
@options[:paths] = other_options[:paths]
@@ -458,6 +483,10 @@ def reverse_merge(other_options)
458483
self
459484
end
460485

486+
def merge_tag_limits(option_limits, other_limits)
487+
other_limits.each { |key, value| add_tag_limit(option_limits, key, value) }
488+
end
489+
461490
def indicate_invalid_language_and_exit(lang)
462491
@out_stream.write("Invalid language '#{lang}'. Available languages are:\n")
463492
list_languages_and_exit
@@ -512,6 +541,7 @@ def default_options
512541
:formats => [],
513542
:excludes => [],
514543
:tag_expressions => [],
544+
:tag_limits => {},
515545
:name_regexps => [],
516546
:env_vars => {},
517547
:diff_enabled => true,

lib/cucumber/configuration.rb

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
require 'cucumber/events'
55
require 'cucumber/core/event_bus'
66
require 'forwardable'
7-
require 'cucumber/core/gherkin/tag_expression'
87
require 'cucumber'
98

109
module Cucumber
@@ -130,13 +129,8 @@ def feature_dirs
130129
with_default_features_path(dirs)
131130
end
132131

133-
# todo: remove
134-
def tag_expression
135-
Cucumber::Core::Gherkin::TagExpression.new(@options[:tag_expressions])
136-
end
137-
138132
def tag_limits
139-
tag_expression.limits.to_hash
133+
@options[:tag_limits]
140134
end
141135

142136
def tag_expressions

lib/cucumber/rb_support/rb_hook.rb

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,25 @@ def initialize(rb_language, tag_expressions, proc)
1010
@tag_expressions = tag_expressions
1111
@proc = proc
1212
@location = Cucumber::Core::Ast::Location.from_source_location(*@proc.source_location)
13+
warn_for_old_style_tag_expressions(tag_expressions)
1314
end
1415

1516
def invoke(pseudo_method, arguments, &block)
1617
@rb_language.current_world.cucumber_instance_exec(false, pseudo_method, *[arguments, block].flatten.compact, &@proc)
1718
end
19+
20+
private
21+
22+
def warn_for_old_style_tag_expressions(tag_expressions)
23+
tag_expressions.each do |tag_expression|
24+
if tag_expression.include?('~') && tag_expression != '~@no-clobber' # ~@no-clobber is used in aruba
25+
warn("Deprecated: Found tagged hook with '#{tag_expression}'. Support for '~@tag' will be removed from the next release of Cucumber. Please use 'not @tag' instead.")
26+
end
27+
if tag_expression.include?(',')
28+
warn("Deprecated: Found tagged hook with '#{tag_expression}'. Support for '@tag1,@tag2' will be removed from the next release of Cucumber. Please use '@tag or @tag2' instead.")
29+
end
30+
end
31+
end
1832
end
1933
end
2034
end

spec/cucumber/cli/configuration_spec.rb

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -354,19 +354,17 @@ def reset_config
354354
expect(config.paths).not_to include('RAILS_ENV=selenium')
355355
end
356356

357-
describe '#tag_expression' do
358-
include RSpec::WorkInProgress
359-
357+
describe '#tag_expressions' do
360358
it 'returns an empty expression when no tags are specified' do
361359
config.parse!([])
362360

363-
expect(config.tag_expression).to be_empty
361+
expect(config.tag_expressions).to be_empty
364362
end
365363

366364
it 'returns an expression when tags are specified' do
367365
config.parse!(['--tags','@foo'])
368366

369-
expect(config.tag_expression).not_to be_empty
367+
expect(config.tag_expressions).not_to be_empty
370368
end
371369
end
372370

spec/cucumber/cli/options_spec.rb

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -143,13 +143,26 @@ def after_parsing(args)
143143
end
144144

145145
context '-t TAGS --tags TAGS' do
146-
it 'designates tags prefixed with ~ as tags to be excluded' do
147-
after_parsing('--tags ~@foo,@bar') { expect(options[:tag_expressions]).to eq ['~@foo,@bar'] }
146+
it 'handles tag expressions as argument' do
147+
after_parsing(['--tags', 'not @foo or @bar']) { expect(options[:tag_expressions]).to eq ['not @foo or @bar'] }
148148
end
149149

150150
it 'stores tags passed with different --tags seperately' do
151151
after_parsing('--tags @foo --tags @bar') { expect(options[:tag_expressions]).to eq ['@foo', '@bar'] }
152152
end
153+
154+
it 'strips tag limits from the tag expressions stored' do
155+
after_parsing(['--tags', 'not @foo:2 or @bar:3']) { expect(options[:tag_expressions]).to eq ['not @foo or @bar'] }
156+
end
157+
158+
it 'stores tag limits separately' do
159+
after_parsing(['--tags', 'not @foo:2 or @bar:3']) { expect(options[:tag_limits]).to eq Hash('@foo' => 2, '@bar' => 3) }
160+
end
161+
162+
it 'raise exception for inconsistent tag limits' do
163+
expect{ after_parsing('--tags @foo:2 --tags @foo:3') }.to raise_error(RuntimeError, 'Inconsistent tag limits for @foo: 2 and 3')
164+
165+
end
153166
end
154167

155168
context '-n NAME or --name NAME' do
@@ -211,6 +224,19 @@ def after_parsing(args)
211224
expect(options[:tag_expressions]).to eq ['@foo', '@bar']
212225
end
213226

227+
it 'combines the tag limits of both' do
228+
given_cucumber_yml_defined_as('baz' => %w[-t @bar:2])
229+
options.parse!(%w[--tags @foo:3 -p baz])
230+
231+
expect(options[:tag_limits]).to eq Hash('@foo' => 3, '@bar' => 2)
232+
end
233+
234+
it 'raise exceptions for inconsistent tag limits' do
235+
given_cucumber_yml_defined_as('baz' => %w[-t @bar:2])
236+
237+
expect{ options.parse!(%w[--tags @bar:3 -p baz]) }.to raise_error(RuntimeError, 'Inconsistent tag limits for @bar: 3 and 2')
238+
end
239+
214240
it 'only takes the paths from the original options, and disgregards the profiles' do
215241
given_cucumber_yml_defined_as('baz' => %w[features])
216242
options.parse!(%w[my.feature -p baz])

0 commit comments

Comments
 (0)