Skip to content

Commit

Permalink
ruby: introduce Gherkin::Query#parent_locations. (#89)
Browse files Browse the repository at this point in the history
Cucumber::Core::Compiler will use this to tell Cucumber::Core::Test::Case the
locations of its parent lines, such as `Feature:`, `Background:`, and `Rule:`,
so that it will correctly match them during location filtering.

Co-authored-by: Micah Geisel <micah@botandrose.com>
  • Loading branch information
botandrose and botandrose-machine authored Sep 12, 2023
1 parent 9739c0d commit f37bb92
Show file tree
Hide file tree
Showing 3 changed files with 75 additions and 9 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ This document is formatted according to the principles of [Keep A CHANGELOG](htt
- (i18n) Added Danish translation of "Rule"
- (i18n) Added Dutch translation of "Rule"
- (i18n) Added Esperanto translation of "Rule"
- [Ruby] Added `Gherkin::Query#parent_locations` for determining a scenario's parents' line numbers ([#89](https://github.com/cucumber/gherkin/pull/89))

## [26.2.0] - 2023-04-07
### Changed
Expand Down
32 changes: 23 additions & 9 deletions ruby/lib/gherkin/query.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,19 @@ module Gherkin
class Query
def initialize
@ast_node_locations = {}
@scenario_parent_locations = {}
@background_locations = {}
end

def update(message)
update_feature(message.gherkin_document.feature) if message.gherkin_document
end

def scenario_parent_locations(scenario_node_id)
return @scenario_parent_locations[scenario_node_id] if @scenario_parent_locations.has_key?(scenario_node_id)
raise AstNodeNotLocatedException, "No scenario parent locations found for #{scenario_node_id} }. Known: #{@scenario_parent_locations.keys}"
end

def location(ast_node_id)
return @ast_node_locations[ast_node_id] if @ast_node_locations.has_key?(ast_node_id)
raise AstNodeNotLocatedException, "No location found for #{ast_node_id} }. Known: #{@ast_node_locations.keys}"
Expand All @@ -20,34 +27,41 @@ def update_feature(feature)
store_nodes_location(feature.tags)

feature.children.each do |child|
update_rule(child.rule) if child.rule
update_background(child.background) if child.background
update_scenario(child.scenario) if child.scenario
update_rule(feature, child.rule) if child.rule
update_background(feature, child.background) if child.background
update_scenario(feature, child.rule, child.scenario) if child.scenario
end
end

def update_rule(rule)
def update_rule(feature, rule)
return if rule.nil?
store_nodes_location(rule.tags)

rule.children.each do |child|
update_background(child.background) if child.background
update_scenario(child.scenario) if child.scenario
update_background(rule, child.background) if child.background
update_scenario(feature, rule, child.scenario) if child.scenario
end
end

def update_background(background)
def update_background(parent, background)
update_steps(background.steps)
@background_locations[parent] = background.location
end

def update_scenario(scenario)
def update_scenario(feature, rule, scenario)
store_node_location(scenario)
store_nodes_location(scenario.tags)
update_steps(scenario.steps)
scenario.examples.each do |examples|
store_nodes_location(examples.tags || [])
store_nodes_location(examples.table_body || [])
end

@scenario_parent_locations[scenario.id] = [
feature.location,
@background_locations[feature],
rule&.location,
@background_locations[rule],
].compact
end

def update_steps(steps)
Expand Down
51 changes: 51 additions & 0 deletions ruby/spec/gherkin/query_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ def find_message_by_attribute(messages, attribute)
Examples:
| Status |
| passed |
| failed |
@rule-tag
Rule: this is a rule
Expand All @@ -71,6 +72,56 @@ def find_message_by_attribute(messages, attribute)
end
end

describe '#scenario_parent_locations' do
before do
messages.each { |message| subject.update(message) }
end

let(:background) { find_message_by_attribute(gherkin_document.feature.children, :background) }
let(:scenarios) { filter_messages_by_attribute(gherkin_document.feature.children, :scenario) }

context 'without rule' do
let(:scenario) { scenarios.first }

it 'provides the feature and background locations of a given scenario node id' do
expect(subject.scenario_parent_locations(scenario.id)).to eq([
gherkin_document.feature.location,
background.location,
])
end
end

context 'with rule' do
let(:rule) { find_message_by_attribute(gherkin_document.feature.children, :rule) }
let(:rule_background) { find_message_by_attribute(rule.children, :background) }
let(:scenario) { find_message_by_attribute(rule.children, :scenario) }

it 'provides the feature, background, rule, and rule background locations of a given scenario node id' do
expect(subject.scenario_parent_locations(scenario.id)).to eq([
gherkin_document.feature.location,
background.location,
rule.location,
rule_background.location,
])
end
end

context 'in a scenario outline' do
let(:scenario) { scenarios.last }

it 'provides the feature and background locations of a given scenario outline node id' do
expect(subject.scenario_parent_locations(scenario.id)).to eq([
gherkin_document.feature.location,
background.location,
])
end
end

it 'raises an exception if called with an invalid id' do
expect { subject.scenario_parent_locations("BAD") }.to raise_error(Gherkin::AstNodeNotLocatedException)
end
end

describe '#location' do
before do
messages.each { |message| subject.update(message) }
Expand Down

0 comments on commit f37bb92

Please sign in to comment.