Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use tag expressions from the cucumber-tag_expressions gem. #116

Merged
merged 1 commit into from
Feb 15, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions cucumber-core.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ Gem::Specification.new do |s|
s.required_ruby_version = ">= 1.9.3"

s.add_dependency 'gherkin', '~> 4.0'
s.add_dependency 'cucumber-tag_expressions', '~> 1.0'
s.add_dependency 'backports', '~> 3.6'

s.add_development_dependency 'bundler', '>= 1.3.5'
Expand Down
15 changes: 14 additions & 1 deletion lib/cucumber/core/test/case.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# frozen_string_literal: true
require 'cucumber/core/test/result'
require 'cucumber/tag_expressions'
require 'cucumber/core/gherkin/tag_expression'
require 'cucumber/core/ast/location'

Expand Down Expand Up @@ -59,7 +60,7 @@ def tags
end

def match_tags?(*expressions)
Cucumber::Core::Gherkin::TagExpression.new(expressions.flatten).evaluate(tags)
expressions.flatten.all? { |expression| match_single_tag_expression?(expression) }
end

def match_name?(name_regexp)
Expand Down Expand Up @@ -120,6 +121,18 @@ def compose_around_hooks(visitor, *args, &block)
end.call
end

def match_single_tag_expression?(expression)
if old_style_tag_expression?(expression)
Cucumber::Core::Gherkin::TagExpression.new([expression]).evaluate(tags)
else
Cucumber::TagExpressions::Parser.new.parse(expression).evaluate(tags.map(&:name))
end
end

def old_style_tag_expression?(expression)
expression.include?(',') || expression.include?('~')
end

class NameBuilder
attr_reader :result
attr_reader :keyword
Expand Down
64 changes: 0 additions & 64 deletions lib/cucumber/core/test/filters/tag_filter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ def test_case(test_case)
end

def done
tag_limits.enforce(test_cases)
receiver.done
self
end
Expand All @@ -26,10 +25,6 @@ def test_cases
@test_cases ||= TestCases.new
end

def tag_limits
@tag_limits ||= TagLimits.new(filter_expressions)
end

class TestCases
attr_reader :test_cases_by_tag_name
private :test_cases_by_tag_name
Expand All @@ -48,65 +43,6 @@ def with_tag_name(tag_name)
test_cases_by_tag_name[tag_name]
end
end

class TagLimits
TAG_MATCHER = /^
(?:~)? #The tag negation symbol "~". This is optional and not captured.
(?<tag_name>\@\w+) #Captures the tag name including the "@" symbol.
\: #The seperator, ":", between the tag name and the limit.
(?<limit>\d+) #Caputres the limit number.
$/x

attr_reader :limit_list
private :limit_list
def initialize(filter_expressions)
@limit_list = Array(filter_expressions).flat_map do |raw_expression|
raw_expression.split(/\s*,\s*/)
end.map do |filter_expression|
TAG_MATCHER.match(filter_expression)
end.compact.each_with_object({}) do |matchdata, limit_list|
limit_list[matchdata[:tag_name]] = Integer(matchdata[:limit])
end
end

def enforce(test_cases)
limit_breaches = limit_list.reduce([]) do |breaches, (tag_name, limit)|
tag_count = test_cases.with_tag_name(tag_name).count
if tag_count > limit
tag_locations = test_cases.with_tag_name(tag_name).map(&:location)
breaches << TagLimitBreach.new(
tag_count,
limit,
tag_name,
tag_locations
)
end
breaches
end
raise TagExcess.new(limit_breaches) if limit_breaches.any?
self
end
end

TagLimitBreach = Struct.new(
:tag_count,
:tag_limit,
:tag_name,
:tag_locations
) do

def message
"#{tag_name} occurred #{tag_count} times, but the limit was set to #{tag_limit}\n " +
tag_locations.map(&:to_s).join("\n ")
end
alias :to_s :message
end

class TagExcess < StandardError
def initialize(limit_breaches)
super(limit_breaches.map(&:to_s).join("\n"))
end
end
end
end
end
Expand Down
56 changes: 55 additions & 1 deletion spec/cucumber/core/test/case_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,42 @@ module Test
end

describe "matching tags" do
it "matches tags using tag expressions" do
gherkin = gherkin do
feature tags: ['@a', '@b'] do
scenario tags: ['@c'] do
step
end
end
end
receiver = double.as_null_object
expect( receiver ).to receive(:test_case) do |test_case|
expect( test_case.match_tags?(['@a and @b']) ).to be_truthy
expect( test_case.match_tags?(['@a or @d']) ).to be_truthy
expect( test_case.match_tags?(['not @d']) ).to be_truthy
expect( test_case.match_tags?(['@a and @d']) ).to be_falsy
end
compile [gherkin], receiver
end

it "matches handles multiple expressions" do
gherkin = gherkin do
feature tags: ['@a', '@b'] do
scenario tags: ['@c'] do
step
end
end
end
receiver = double.as_null_object
expect( receiver ).to receive(:test_case) do |test_case|
expect( test_case.match_tags?(['@a and @b', 'not @d']) ).to be_truthy
expect( test_case.match_tags?(['@a and @b', 'not @c']) ).to be_falsy
end
compile [gherkin], receiver
end
end

describe "matching tags (old style)" do
it "matches boolean expressions of tags" do
gherkin = gherkin do
feature tags: ['@a', '@b'] do
Expand All @@ -195,7 +231,25 @@ module Test
end
receiver = double.as_null_object
expect( receiver ).to receive(:test_case) do |test_case|
expect( test_case.match_tags?('@a') ).to be_truthy
expect( test_case.match_tags?(['@a', '@b']) ).to be_truthy
expect( test_case.match_tags?(['@a, @d']) ).to be_truthy
expect( test_case.match_tags?(['~@d']) ).to be_truthy
expect( test_case.match_tags?(['@a', '@d']) ).to be_falsy
end
compile [gherkin], receiver
end

it "handles mixing old and new style expressions" do
gherkin = gherkin do
feature tags: ['@a', '@b'] do
scenario tags: ['@c'] do
step
end
end
end
receiver = double.as_null_object
expect( receiver ).to receive(:test_case) do |test_case|
expect( test_case.match_tags?(['@a and @b', '~@d']) ).to be_truthy
end
compile [gherkin], receiver
end
Expand Down
121 changes: 0 additions & 121 deletions spec/cucumber/core_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -77,127 +77,6 @@ module Cucumber

compile [gherkin], visitor, [Cucumber::Core::Test::TagFilter.new(['@a', '@b'])]
end

describe 'with tag filters that have limits' do
let(:visitor) { double.as_null_object }
let(:gherkin_doc) do
gherkin do
feature tags: '@feature' do
scenario tags: '@one @three' do
step
end

scenario tags: '@one' do
step
end

scenario_outline do
step '<arg>'

examples tags: '@three'do
row 'arg'
row 'x'
end
end

scenario tags: '@ignore' do
step
end
end
end
end

require 'unindent'
def expect_tag_excess(error_message)
expect {
compile [gherkin_doc], visitor, tag_filters
}.to raise_error(
Cucumber::Core::Test::TagFilter::TagExcess, error_message.unindent.chomp
)
end

context 'on scenarios' do
let(:tag_filters) {
[ Cucumber::Core::Test::TagFilter.new(['@one:1']) ]
}

it 'raises a tag excess error with the location of the test cases' do
expect_tag_excess <<-STR
@one occurred 2 times, but the limit was set to 1
features/test.feature:5
features/test.feature:9
STR
end
end

context 'on scenario outlines' do
let(:tag_filters) {
[ Cucumber::Core::Test::TagFilter.new(['@three:1']) ]
}

it 'raises a tag excess error with the location of the test cases' do
expect_tag_excess <<-STR
@three occurred 2 times, but the limit was set to 1
features/test.feature:5
features/test.feature:18
STR
end
end

context 'on a feature with scenarios' do
let(:tag_filters) {
[ Cucumber::Core::Test::TagFilter.new(['@feature:2']) ]
}

it 'raises a tag excess error with the location of the test cases' do
expect_tag_excess <<-STR
@feature occurred 4 times, but the limit was set to 2
features/test.feature:5
features/test.feature:9
features/test.feature:18
features/test.feature:21
STR
end
end

context 'with negated tags' do
let(:tag_filters) {
[ Cucumber::Core::Test::TagFilter.new(['~@one:1']) ]
}

it 'raises a tag excess error with the location of the test cases' do
expect_tag_excess <<-STR
@one occurred 2 times, but the limit was set to 1
features/test.feature:5
features/test.feature:9
STR
end
end

context 'whith multiple tag limits' do
let(:tag_filters) {
[ Cucumber::Core::Test::TagFilter.new(['@one:1, @three:1', '~@feature:3']) ]
}

it 'raises a tag excess error with the location of the test cases' do
expect_tag_excess <<-STR
@one occurred 2 times, but the limit was set to 1
features/test.feature:5
features/test.feature:9
@three occurred 2 times, but the limit was set to 1
features/test.feature:5
features/test.feature:18
@feature occurred 4 times, but the limit was set to 3
features/test.feature:5
features/test.feature:9
features/test.feature:18
features/test.feature:21
STR
end
end

end

end

describe "executing a test suite" do
Expand Down