Skip to content

Commit

Permalink
Merge pull request #5 from codegram/feature/add_background_support
Browse files Browse the repository at this point in the history
Feature/add background support
  • Loading branch information
Josep M. Bach committed Nov 9, 2011
2 parents fc53537 + 7aef876 commit a4f74b4
Show file tree
Hide file tree
Showing 7 changed files with 140 additions and 23 deletions.
9 changes: 7 additions & 2 deletions Readme.md
Expand Up @@ -30,6 +30,13 @@ class MyVisitor
scenario.each { |step| step.accept(self) }
end

def visit_Background(background)
# Do something nasty with the background
# And keep visiting its children:

background.each { |step| step.accept(self) }
end

def visit_Tag(tag)
# Do something nasty with the tag
end
Expand All @@ -47,6 +54,4 @@ visitor.visit(ast)

## Todo

* Backgrounds
* Some optimization

22 changes: 20 additions & 2 deletions lib/gherkin/ast.rb
Expand Up @@ -10,14 +10,15 @@ def accept(visitor)
end

class Feature < Node
attr_reader :name, :scenarios
attr_reader :name, :background, :scenarios

include Enumerable

def initialize(name, scenarios=[])
def initialize(name, scenarios=[], background=nil)
@line, @column = name.line_and_column

@name = name.to_s
@background = background
@scenarios = scenarios
end

Expand All @@ -26,6 +27,23 @@ def each
end
end

class Background < Node
attr_reader :steps

include Enumerable

def initialize(steps=[])
@line = steps.first.line - 1
@column = 3

@steps = steps
end

def each
@steps.each
end
end

class Scenario < Node
attr_reader :name, :steps, :tags

Expand Down
5 changes: 4 additions & 1 deletion lib/gherkin/parser.rb
Expand Up @@ -14,6 +14,7 @@ def indent(num=2)

rule(:feature_line) { str('Feature:') >> space? >> text.as(:name) }
rule(:scenario_line) { indent(2) >> str('Scenario:') >> space? >> text.as(:name) }
rule(:background_line) { indent(2) >> str('Background:') }

rule(:step_keyword) { str('Given') | str('When') | str('Then') | str('And') | str('But') }

Expand All @@ -28,7 +29,9 @@ def indent(num=2)
rule(:scenario) { (tags.as(:tags) >> newline).maybe >> scenario_line >> newline >> steps.as(:steps) }
rule(:scenarios) { (scenario.as(:scenario) >> newline.maybe).repeat }

rule(:feature) { feature_line >> newline >> scenarios.as(:scenarios) }
rule(:background) { background_line >> newline >> steps.as(:steps) }

rule(:feature) { feature_line >> newline >> background.as(:background).maybe >> scenarios.as(:scenarios) }

rule(:main) { feature.as(:feature) }

Expand Down
14 changes: 13 additions & 1 deletion lib/gherkin/transform.rb
@@ -1,11 +1,23 @@
module Gherkin
class Transform < Parslet::Transform
# Match feature with background
rule(
feature: {
name: simple(:name),
background: {
steps: subtree(:background_steps)
},
scenarios: subtree(:scenarios)
}
) { AST::Feature.new(name, scenarios) }
) { AST::Feature.new(name, scenarios, AST::Background.new(background_steps)) }

# Match feature without background
rule(
feature: {
name: simple(:name),
scenarios: subtree(:scenarios)
}
) { AST::Feature.new(name, scenarios, AST::Background.new([])) }

# Match scenarios without tags
rule(
Expand Down
46 changes: 37 additions & 9 deletions test/gherkin/ast_test.rb
Expand Up @@ -44,21 +44,31 @@ def name.to_s; 'Name'; end
end
end

[Feature, Scenario].each do |node|
describe node do
it 'is Enumerable' do
name = OpenStruct.new(line_and_column: [2, 13])
def name.to_s; 'Name'; end
describe Feature do
it 'is Enumerable' do
name = OpenStruct.new(line_and_column: [2, 13])
def name.to_s; 'Name'; end

elements = ['foo', 'bar']
background = ['foo', 'bar']
elements = ['+foo', '+bar']

instance = node.new(name, elements)
instance.each.to_a.must_equal ['foo', 'bar']
end
instance = Feature.new(name, elements, background )
instance.background.each.to_a.must_equal ['foo', 'bar']
instance.each.to_a.must_equal ['+foo', '+bar']
end
end

describe Scenario do
it 'is Enumerable' do
name = OpenStruct.new(line_and_column: [2, 13])
def name.to_s; 'Name'; end

elements = ['foo', 'bar']

instance = Scenario.new(name, elements)
instance.each.to_a.must_equal ['foo', 'bar']
end

it 'has tags' do
name = OpenStruct.new(line_and_column: [2, 13])
def name.to_s; 'Name'; end
Expand All @@ -70,5 +80,23 @@ def name.to_s; 'Name'; end
instance.tags.must_equal tags
end
end

describe Background do
it 'is a Node' do
Background.ancestors.must_include Node
end

it 'is Enumerable' do
steps = [
OpenStruct.new(line: 4),
OpenStruct.new(line: 5),
]

instance = Background.new(steps)
instance.line.must_equal 3
instance.column.must_equal 3
instance.each.to_a.must_equal steps
end
end
end
end
40 changes: 38 additions & 2 deletions test/gherkin/parser_test.rb
Expand Up @@ -67,6 +67,15 @@ module Gherkin
end
end

describe 'Background parsing' do
it 'parses a background' do
background = " Background:\n When I do something\n Then blah"
steps = p(:background, background, :steps)
steps.first[:step][:name].must_equal 'I do something'
steps.last[:step][:name].must_equal 'blah'
end
end

describe 'Parses scenario objects' do
it 'parses a Scenario' do
parser = Gherkin::Parser.new
Expand All @@ -90,11 +99,11 @@ module Gherkin
end
end

describe 'Parses feature objects' do
describe 'Parses feature objects without background' do
it 'parses a Feature' do
parser = Gherkin::Parser.new
scenario = """Feature: My Feature
@javascript
@javascript @wip
Scenario: something happens
Given something happens
Then something cooler happens
Expand All @@ -108,6 +117,7 @@ module Gherkin
result[:feature][:name].must_equal 'My Feature'

result[:feature][:scenarios][0][:scenario][:tags].first[:tag].must_equal 'javascript'
result[:feature][:scenarios][0][:scenario][:tags].last[:tag].must_equal 'wip'
result[:feature][:scenarios][0][:scenario][:name].must_equal 'something happens'
result[:feature][:scenarios][0][:scenario][:steps][0][:step][:name].must_equal 'something happens'
result[:feature][:scenarios][0][:scenario][:steps][1][:step][:name].must_equal 'something cooler happens'
Expand All @@ -117,4 +127,30 @@ module Gherkin
result[:feature][:scenarios][1][:scenario][:steps][1][:step][:name].must_equal 'bar'
end
end

describe 'Parses feature objects with background' do
it 'parses a Feature' do
parser = Gherkin::Parser.new
scenario = """Feature: My Feature
Background:
Given something happens
And something cooler happens
Scenario: something else happens
Given foo
Then bar
"""
result = parser.parse(scenario)

result[:feature][:name].must_equal 'My Feature'

result[:feature][:background][:steps][0][:step][:name].must_equal 'something happens'
result[:feature][:background][:steps][1][:step][:name].must_equal 'something cooler happens'

result[:feature][:scenarios][0][:scenario][:name].must_equal 'something else happens'
result[:feature][:scenarios][0][:scenario][:steps][0][:step][:name].must_equal 'foo'
result[:feature][:scenarios][0][:scenario][:steps][1][:step][:name].must_equal 'bar'
end
end
end
27 changes: 21 additions & 6 deletions test/gherkin/transform_test.rb
Expand Up @@ -5,6 +5,11 @@ module Gherkin
describe 'Feature parsing' do
before do
@scenario = """Feature: My Feature
Background:
Given something happens before anything else happens
And more things happens before anything else happens
Scenario: something happens
Given something happens
Then something cooler happens
Expand All @@ -14,6 +19,7 @@ module Gherkin
Given foo
Then bar
"""

parser = Gherkin::Parser.new
result = parser.parse(@scenario)
transform = Gherkin::Transform.new
Expand All @@ -24,27 +30,36 @@ module Gherkin
@result.must_be_kind_of AST::Feature
@result.line.must_equal 1

background = @result.background
background.must_be_kind_of AST::Background
background.line.must_equal 3
background.column.must_equal 3
background.steps.first.name.must_equal 'something happens before anything else happens'
background.steps.first.line.must_equal 4
background.steps.last.name.must_equal 'more things happens before anything else happens'
background.steps.last.line.must_equal 5

first_scenario = @result.scenarios.first
first_scenario.must_be_kind_of AST::Scenario
first_scenario.line.must_equal 2
first_scenario.line.must_equal 7
first_scenario.name.must_equal 'something happens'
first_scenario.steps.first.name.must_equal 'something happens'
first_scenario.steps.first.line.must_equal 3
first_scenario.steps.first.line.must_equal 8
first_scenario.steps.last.name.must_equal 'something cooler happens'
first_scenario.steps.last.line.must_equal 4
first_scenario.steps.last.line.must_equal 9

last_scenario = @result.scenarios.last
last_scenario.must_be_kind_of AST::Scenario
last_scenario.line.must_equal 7
last_scenario.line.must_equal 12
last_scenario.name.must_equal 'something else happens'

last_scenario.tags.first.name.must_equal 'javascript'
last_scenario.tags.last.name.must_equal 'wip'

last_scenario.steps.first.name.must_equal 'foo'
last_scenario.steps.first.line.must_equal 8
last_scenario.steps.first.line.must_equal 13
last_scenario.steps.last.name.must_equal 'bar'
last_scenario.steps.last.line.must_equal 9
last_scenario.steps.last.line.must_equal 14
end
end
end

0 comments on commit a4f74b4

Please sign in to comment.