Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Added support for ANDed, ORed and negated tags on scenarios.

  • Loading branch information...
commit 62d1b232c86ba5f81c6eb094690daba9379ae2ad 1 parent abfd50a
Robin Roestenburg robinroestenburg authored
8 lib/spinach/cli.rb
View
@@ -75,8 +75,14 @@ def parse_options
reporter_options[:backtrace] = show_backtrace
end
+ opts.on('-t', '--tags TAG',
+ 'Run all scenarios for given tag.') do |tag|
+ config[:tag] ||= []
+ config[:tag] << tag.delete('@').split(',')
+ end
+
opts.on('-g', '--generate',
- 'Auto-generate the feeature steps files') do
+ 'Auto-generate the feature steps files') do
Spinach::Generators.bind
end
14 lib/spinach/config.rb
View
@@ -20,8 +20,14 @@ def self.config
# to run.
#
class Config
- attr_writer :features_path, :step_definitions_path, :default_reporter, :support_path,
- :failure_exceptions, :config_path, :save_and_open_page_on_failure
+ attr_writer :features_path,
+ :step_definitions_path,
+ :default_reporter,
+ :support_path,
+ :failure_exceptions,
+ :config_path,
+ :tag,
+ :save_and_open_page_on_failure
# The "features path" holds the place where your features will be
# searched for. Defaults to 'features'
@@ -113,6 +119,10 @@ def save_and_open_page_on_failure
@save_and_open_page_on_failure ||= false
end
+ def tag
+ @tag ||= []
+ end
+
# Parse options from the config file
#
# @return [Boolean]
15 lib/spinach/runner/feature_runner.rb
View
@@ -1,3 +1,5 @@
+require_relative 'tags_matcher'
+
module Spinach
class Runner
# A feature runner handles a particular feature run.
@@ -55,20 +57,25 @@ def run
def run_scenarios!
scenarios.each_with_index do |scenario, current_scenario_index|
- if match(current_scenario_index)
+ if run_scenario?(scenario, current_scenario_index)
success = ScenarioRunner.new(scenario).run
@failed = true unless success
end
end
end
- def match(current_scenario_index)
+ def run_scenario?(scenario, current_scenario_index)
+ match_line(current_scenario_index) && TagsMatcher.new(scenario).match
+ end
+
+ def match_line(current_scenario_index)
return true unless @line
- return false if @line<scenarios[current_scenario_index].line
+ return false if @line < scenarios[current_scenario_index].line
next_scenario = scenarios[current_scenario_index+1]
- !next_scenario || @line<next_scenario.line
+ !next_scenario || @line < next_scenario.line
end
+
def undefined_steps!
Spinach.hooks.run_on_undefined_feature @feature
@failed = true
37 lib/spinach/runner/tags_matcher.rb
View
@@ -0,0 +1,37 @@
+module Spinach
+
+ class TagsMatcher
+
+ NEGATION_SIGN = '~'
+
+ def initialize(scenario)
+ @tags = scenario.tags
+ end
+
+ def match
+ return true if tag_groups.empty?
+
+ tag_groups.all? { |tag_group| match_tag_group(tag_group) }
+ end
+
+ private
+
+ def tag_groups
+ @tag_groups ||= Spinach.config.tag
+ end
+
+ def match_tag_group(tag_group)
+ tag_group.any? do |tag|
+ tag_matched = @tags.include?(tag.delete(NEGATION_SIGN))
+
+ negated?(tag) ? !tag_matched : tag_matched
+ end
+ end
+
+ def negated?(tag)
+ tag.start_with? NEGATION_SIGN
+ end
+
+ end
+
+end
28 test/spinach/cli_test.rb
View
@@ -30,6 +30,34 @@
end
end
+ describe 'tag' do
+ %w{-t --tag}.each do |opt|
+ it 'sets the given tag' do
+ config = Spinach::Config.new
+ Spinach.stubs(:config).returns(config)
+ cli = Spinach::Cli.new([opt,'wip'])
+ cli.options
+ config.tag.must_equal [['wip']]
+ end
+
+ it 'sets multiple tags' do
+ config = Spinach::Config.new
+ Spinach.stubs(:config).returns(config)
+ cli = Spinach::Cli.new([opt,'wip,javascript'])
+ cli.options
+ config.tag.must_equal [['wip', 'javascript']]
+ end
+ end
+
+ it 'sets multiple tags' do
+ config = Spinach::Config.new
+ Spinach.stubs(:config).returns(config)
+ cli = Spinach::Cli.new(['-t','javascript', '-t', 'wip'])
+ cli.options
+ config.tag.must_equal [['javascript'],['wip']]
+ end
+ end
+
describe 'generate' do
%w{-g --generate}.each do |opt|
it 'inits the generator if #{opt}' do
12 test/spinach/runner/feature_runner_test.rb
View
@@ -37,8 +37,8 @@
@feature = stub('feature', name: 'Feature')
Spinach.stubs(:find_step_definitions).returns(true)
@scenarios = [
- scenario = stub,
- another_scenario = stub
+ scenario = stub(tags: []),
+ another_scenario = stub(tags: [])
]
@feature.stubs(:scenarios).returns @scenarios
@runner = Spinach::Runner::FeatureRunner.new(@feature)
@@ -80,26 +80,30 @@
@feature = stub('feature', name: 'Feature')
Spinach.stubs(:find_step_definitions).returns(true)
@scenarios = [
- scenario = stub(line: 4),
- another_scenario = stub(line: 12)
+ scenario = stub(line: 4, tags: []),
+ another_scenario = stub(line: 12, tags: [])
]
@feature.stubs(:scenarios).returns @scenarios
end
+
it "runs exactly matching scenario" do
Spinach::Runner::ScenarioRunner.expects(:new).with(@scenarios[1]).returns stub(run: true)
@runner = Spinach::Runner::FeatureRunner.new(@feature, "12")
@runner.run
end
+
it "runs no scenario and returns false" do
Spinach::Runner::ScenarioRunner.expects(:new).never
@runner = Spinach::Runner::FeatureRunner.new(@feature, "3")
@runner.run
end
+
it "runs matching scenario" do
Spinach::Runner::ScenarioRunner.expects(:new).with(@scenarios[0]).returns stub(run: true)
@runner = Spinach::Runner::FeatureRunner.new(@feature, "8")
@runner.run
end
+
it "runs last scenario" do
Spinach::Runner::ScenarioRunner.expects(:new).with(@scenarios[1]).returns stub(run: true)
@runner = Spinach::Runner::FeatureRunner.new(@feature, "15")
164 test/spinach/runner/tags_matcher_test.rb
View
@@ -0,0 +1,164 @@
+
+require_relative '../../test_helper'
+
+describe Spinach::Runner::FeatureRunner do
+
+ describe '#match' do
+
+ before do
+ @config = Spinach::Config.new
+ Spinach.stubs(:config).returns(@config)
+ end
+
+ describe "when a single tag is passed" do
+
+ before do
+ @config.tag = [['wip']]
+ end
+
+ it "runs a scenario with matching tag" do
+ @scenario = stub(tags: ['wip'])
+ matcher = Spinach::TagsMatcher.new(@scenario)
+ matcher.match.must_equal true
+ end
+
+ it "skips a scenario without matching tag" do
+ @scenario = stub(tags: ['javascript'])
+ matcher = Spinach::TagsMatcher.new(@scenario)
+ matcher.match.must_equal false
+ end
+
+ it "skips a scenario without tags" do
+ @scenario = stub(tags: [])
+ matcher = Spinach::TagsMatcher.new(@scenario)
+ matcher.match.must_equal false
+ end
+ end
+
+ describe 'when a single negated tag is passed' do
+
+ before do
+ @config.tag = [['~wip']]
+ end
+
+ it "skips a scenario with matching tags" do
+ @scenario = stub(tags: ['wip'])
+ matcher = Spinach::TagsMatcher.new(@scenario)
+ matcher.match.must_equal false
+ end
+
+ it "runs a scenario without matching tags" do
+ @scenario = stub(tags: ['javascript'])
+ matcher = Spinach::TagsMatcher.new(@scenario)
+ matcher.match.must_equal true
+ end
+
+ it "runs a scenario without tags" do
+ @scenario = stub(tags: [])
+ matcher = Spinach::TagsMatcher.new(@scenario)
+ matcher.match.must_equal true
+ end
+ end
+
+ describe 'when combining ORed and ANDend tags' do
+ # Running scenarios which match: (@billing OR @WIP) AND @important
+ # cucumber --tags @billing,@wip --tags @important
+
+ before do
+ @config.tag = [['billing', 'wip'], ['important']]
+ end
+
+ it "runs a scenario with matching tags" do
+ @scenario = stub(tags: ['wip', 'important'])
+ matcher = Spinach::TagsMatcher.new(@scenario)
+ matcher.match.must_equal true
+ end
+
+ it "runs a scenario with matching tags" do
+ @scenario = stub(tags: ['billing', 'important'])
+ matcher = Spinach::TagsMatcher.new(@scenario)
+ matcher.match.must_equal true
+ end
+
+ it "skips a scenario with one matching tags" do
+ @scenario = stub(tags: ['important'])
+ matcher = Spinach::TagsMatcher.new(@scenario)
+ matcher.match.must_equal false
+ end
+
+ it "skips a scenario with one matching tags" do
+ @scenario = stub(tags: ['billing'])
+ matcher = Spinach::TagsMatcher.new(@scenario)
+ matcher.match.must_equal false
+ end
+
+ it "skips a scenario without matching tags" do
+ @scenario = stub(tags: ['javascript'])
+ matcher = Spinach::TagsMatcher.new(@scenario)
+ matcher.match.must_equal false
+ end
+ end
+
+ describe "when multiple tags are passed as separate params" do
+
+ before do
+ @config.tag = [['wip'], ['javascript']]
+ end
+
+ it "runs a scenario with matching tags" do
+ @scenario = stub(line: 4, tags: ['wip', 'javascript'])
+ matcher = Spinach::TagsMatcher.new(@scenario)
+ matcher.match.must_equal true
+ end
+
+ it "skips a scenario without all matching tags" do
+ @scenario = stub(line: 4, tags: ['javascript'])
+ matcher = Spinach::TagsMatcher.new(@scenario)
+ matcher.match.must_equal false
+ end
+
+ it "skips a scenario without matching tags" do
+ @scenario = stub(line: 4, tags: ['foo'])
+ matcher = Spinach::TagsMatcher.new(@scenario)
+ matcher.match.must_equal false
+ end
+
+ it "skips a scenario without tags" do
+ @scenario = stub(line: 4, tags: [])
+ matcher = Spinach::TagsMatcher.new(@scenario)
+ matcher.match.must_equal false
+ end
+ end
+
+ describe "when multiple tags are passed as a comma-separated list" do
+
+ before do
+ @config.tag = [['wip', 'javascript']]
+ end
+
+ it "runs a scenario with all matching tags" do
+ @scenario = stub(tags: ['wip', 'javascript'])
+ matcher = Spinach::TagsMatcher.new(@scenario)
+ matcher.match.must_equal true
+ end
+
+ it "runs a scenario with at least one matching tag" do
+ @scenario = stub(tags: ['javascript'])
+ matcher = Spinach::TagsMatcher.new(@scenario)
+ matcher.match.must_equal true
+ end
+
+ it "skips a scenario without matching tags" do
+ @scenario = stub(tags: ['foo'])
+ matcher = Spinach::TagsMatcher.new(@scenario)
+ matcher.match.must_equal false
+ end
+
+ it "skips a scenario without tags" do
+ @scenario = stub(tags: [])
+ matcher = Spinach::TagsMatcher.new(@scenario)
+ matcher.match.must_equal false
+ end
+ end
+ end
+end
Please sign in to comment.
Something went wrong with that request. Please try again.