Skip to content

Commit

Permalink
Use tag expressions from the cucumber-tag_expressions gem.
Browse files Browse the repository at this point in the history
  • Loading branch information
brasmusson committed Nov 13, 2016
1 parent cd3fd63 commit ce88ae7
Show file tree
Hide file tree
Showing 8 changed files with 89 additions and 48 deletions.
23 changes: 12 additions & 11 deletions cucumber.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,19 @@ cucumber_pro_opts = ENV['ENABLE_CUCUMBER_PRO'] ? "--format Cucumber::Pro --out /
rerun = File.file?('rerun.txt') ? IO.read('rerun.txt') : ""
rerun_opts = rerun.to_s.strip.empty? ? "--format progress features" : "--format pretty #{rerun}"
std_opts = "--format progress features -r features --strict #{cucumber_pro_opts}"
std_opts << " --tags ~@wip-jruby" if defined?(JRUBY_VERSION)
std_opts << " --tags 'not @wip-jruby'" if defined?(JRUBY_VERSION)
wip_opts = "--color -r features --tags @wip"
wip_opts << ",@wip-jruby" if defined?(JRUBY_VERSION)
wip_opts = "--color -r features"
wip_opts << " --tags @wip" if !defined?(JRUBY_VERSION)
wip_opts << " --tags '@wip or @wip-jruby'" if defined?(JRUBY_VERSION)
%>
default: <%= std_opts %> --tags ~@jruby
jruby: <%= std_opts %> --tags ~@wire
jruby_win: <%= std_opts %> --tags ~@wire CUCUMBER_FORWARD_SLASH_PATHS=true
windows_mri: <%= std_opts %> --tags ~@jruby --tags ~@wire --tags ~@needs-many-fonts CUCUMBER_FORWARD_SLASH_PATHS=true
ruby_1_9: <%= std_opts %> --tags ~@jruby
ruby_2_0: <%= std_opts %> --tags ~@jruby
ruby_2_1: <%= std_opts %> --tags ~@jruby
default: <%= std_opts %> --tags 'not @jruby'
jruby: <%= std_opts %> --tags 'not @wire'
jruby_win: <%= std_opts %> --tags 'not @wire' CUCUMBER_FORWARD_SLASH_PATHS=true
windows_mri: <%= std_opts %> --tags 'not @jruby and not @wire and not @needs-many-fonts' CUCUMBER_FORWARD_SLASH_PATHS=true
ruby_1_9: <%= std_opts %> --tags 'not @jruby'
ruby_2_0: <%= std_opts %> --tags 'not @jruby'
ruby_2_1: <%= std_opts %> --tags 'not @jruby'
wip: --wip <%= wip_opts %> features <%= cucumber_pro_opts %>
none: --format pretty --format Cucumber::Pro --out /dev/null
rerun: <%= rerun_opts %> --format rerun --out rerun.txt --strict --tag ~@wip-new-core <%= cucumber_pro_opts %>
rerun: <%= rerun_opts %> --format rerun --out rerun.txt --strict --tags 'not @wip-new-core' <%= cucumber_pro_opts %>
8 changes: 4 additions & 4 deletions features/docs/cli/execute_with_tag_filter.feature
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ Feature: Tag logic
"""

Scenario: ANDing tags
When I run `cucumber -q -t @one -t @three features/test.feature`
When I run `cucumber -q -t '@one and @three' features/test.feature`
Then it should pass with:
"""
@feature
Expand All @@ -42,7 +42,7 @@ Feature: Tag logic
"""

Scenario: ORing tags
When I run `cucumber -q -t @one,@three features/test.feature`
When I run `cucumber -q -t '@one or @three' features/test.feature`
Then it should pass with:
"""
@feature
Expand All @@ -66,7 +66,7 @@ Feature: Tag logic
"""

Scenario: Negative tags
When I run `cucumber -q -t ~@three features/test.feature`
When I run `cucumber -q -t 'not @three' features/test.feature`
Then it should pass with:
"""
@feature
Expand Down Expand Up @@ -104,7 +104,7 @@ Feature: Tag logic
"""

Scenario: Run with limited tag count using negative tag, blowing it via a tag that is not run
When I run `cucumber -q --no-source --tags ~@one:1 features/test.feature`
When I run `cucumber -q --no-source --tags 'not @one:1' features/test.feature`
Then it fails before running features with:
"""
@one occurred 2 times, but the limit was set to 1
Expand Down
2 changes: 1 addition & 1 deletion features/docs/writing_support_code/tagged_hooks.feature
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ Feature: Tagged hooks
Given the standard step definitions
And a file named "features/support/hooks.rb" with:
"""
Before('~@no-boom') do
Before('not @no-boom') do
raise 'boom'
end
"""
Expand Down
10 changes: 1 addition & 9 deletions lib/cucumber/cli/configuration.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
require 'cucumber/cli/options'
require 'cucumber/cli/rerun_file'
require 'cucumber/constantize'
require 'cucumber/core/gherkin/tag_expression'
require 'cucumber'

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

Expand Down Expand Up @@ -85,13 +82,8 @@ def log
logger
end

# todo: remove
def tag_expression
Cucumber::Core::Gherkin::TagExpression.new(@options[:tag_expressions])
end

def tag_limits
tag_expression.limits.to_hash
@options[:tag_limits]
end

def tag_expressions
Expand Down
48 changes: 39 additions & 9 deletions lib/cucumber/cli/options.rb
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ class Options
'-l', '--lines', '--port',
'-I', '--snippet-type']
ORDER_TYPES = %w{defined random}
TAG_MATCHER = /(?<tag_name>\@\w+):(?<limit>\d+)/x

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

def add_tag(value)
warn("Deprecated: Support for '~@tag' will be removed from the next release of Cucumber. Please use the 'not @tag' instead.") if value.include?('~')
warn("Deprecated: Support for '@tag1,@tag2' will be removed from the next release of Cucumber. Please use the '@tag or @tag2' instead.") if value.include?(',')
@options[:tag_expressions] << value.gsub(/(@\w+)(:\d+)?/, '\1')
add_tag_limits(value)
end

def add_tag_limits(value)
value.split(/[, ]/).map { |part| TAG_MATCHER.match(part) }.compact.each do |matchdata|
add_tag_limit(@options[:tag_limits], matchdata[:tag_name], matchdata[:limit].to_i)
end
end

def add_tag_limit(tag_limits, tag_name, limit)
if tag_limits[tag_name] && tag_limits[tag_name] != limit
raise "Inconsistent tag limits for #{tag_name}: #{tag_limits[tag_name]} and #{limit}"
end
tag_limits[tag_name] = limit
end

def set_color(color)
Cucumber::Term::ANSIColor.coloring = color
end
Expand Down Expand Up @@ -433,6 +457,7 @@ def reverse_merge(other_options)
@options[:excludes] += other_options[:excludes]
@options[:name_regexps] += other_options[:name_regexps]
@options[:tag_expressions] += other_options[:tag_expressions]
merge_tag_limits(@options[:tag_limits], other_options[:tag_limits])
@options[:env_vars] = other_options[:env_vars].merge(@options[:env_vars])
if @options[:paths].empty?
@options[:paths] = other_options[:paths]
Expand All @@ -458,6 +483,10 @@ def reverse_merge(other_options)
self
end

def merge_tag_limits(option_limits, other_limits)
other_limits.each { |key, value| add_tag_limit(option_limits, key, value) }
end

def indicate_invalid_language_and_exit(lang)
@out_stream.write("Invalid language '#{lang}'. Available languages are:\n")
list_languages_and_exit
Expand Down Expand Up @@ -512,6 +541,7 @@ def default_options
:formats => [],
:excludes => [],
:tag_expressions => [],
:tag_limits => {},
:name_regexps => [],
:env_vars => {},
:diff_enabled => true,
Expand Down
8 changes: 1 addition & 7 deletions lib/cucumber/configuration.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
require 'cucumber/events'
require 'cucumber/core/event_bus'
require 'forwardable'
require 'cucumber/core/gherkin/tag_expression'
require 'cucumber'

module Cucumber
Expand Down Expand Up @@ -130,13 +129,8 @@ def feature_dirs
with_default_features_path(dirs)
end

# todo: remove
def tag_expression
Cucumber::Core::Gherkin::TagExpression.new(@options[:tag_expressions])
end

def tag_limits
tag_expression.limits.to_hash
@options[:tag_limits]
end

def tag_expressions
Expand Down
8 changes: 3 additions & 5 deletions spec/cucumber/cli/configuration_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -354,19 +354,17 @@ def reset_config
expect(config.paths).not_to include('RAILS_ENV=selenium')
end

describe '#tag_expression' do
include RSpec::WorkInProgress

describe '#tag_expressions' do
it 'returns an empty expression when no tags are specified' do
config.parse!([])

expect(config.tag_expression).to be_empty
expect(config.tag_expressions).to be_empty
end

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

expect(config.tag_expression).not_to be_empty
expect(config.tag_expressions).not_to be_empty
end
end

Expand Down
30 changes: 28 additions & 2 deletions spec/cucumber/cli/options_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -143,13 +143,26 @@ def after_parsing(args)
end

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

it 'stores tags passed with different --tags seperately' do
after_parsing('--tags @foo --tags @bar') { expect(options[:tag_expressions]).to eq ['@foo', '@bar'] }
end

it 'strips tag limits from the tag expressions stored' do
after_parsing(['--tags', 'not @foo:2 or @bar:3']) { expect(options[:tag_expressions]).to eq ['not @foo or @bar'] }
end

it 'stores tag limits separately' do
after_parsing(['--tags', 'not @foo:2 or @bar:3']) { expect(options[:tag_limits]).to eq Hash('@foo' => 2, '@bar' => 3) }
end

it 'raise exception for inconsistent tag limits' do
expect{ after_parsing('--tags @foo:2 --tags @foo:3') }.to raise_error(RuntimeError, 'Inconsistent tag limits for @foo: 2 and 3')

end
end

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

it 'combines the tag limits of both' do
given_cucumber_yml_defined_as('baz' => %w[-t @bar:2])
options.parse!(%w[--tags @foo:3 -p baz])

expect(options[:tag_limits]).to eq Hash('@foo' => 3, '@bar' => 2)
end

it 'raise exceptions for inconsistent tag limits' do
given_cucumber_yml_defined_as('baz' => %w[-t @bar:2])

expect{ options.parse!(%w[--tags @bar:3 -p baz]) }.to raise_error(RuntimeError, 'Inconsistent tag limits for @bar: 3 and 2')
end

it 'only takes the paths from the original options, and disgregards the profiles' do
given_cucumber_yml_defined_as('baz' => %w[features])
options.parse!(%w[my.feature -p baz])
Expand Down

0 comments on commit ce88ae7

Please sign in to comment.