Skip to content

Commit

Permalink
Merge 1ddf9ec into f9a856b
Browse files Browse the repository at this point in the history
  • Loading branch information
vincent-psarga committed Nov 18, 2019
2 parents f9a856b + 1ddf9ec commit 9349308
Show file tree
Hide file tree
Showing 5 changed files with 313 additions and 15 deletions.
41 changes: 28 additions & 13 deletions lib/cucumber/core/compiler.rb
Expand Up @@ -18,8 +18,8 @@ def initialize(receiver)
@receiver = receiver
end

def pickle(pickle)
test_case = create_test_case(pickle)
def pickle(pickle, location_query)
test_case = create_test_case(pickle, location_query)
test_case.describe_to(receiver)
end

Expand All @@ -30,35 +30,50 @@ def done

private

def create_test_case(pickle)
def create_test_case(pickle, location_query)
uri = pickle.uri
test_steps = pickle.steps.map { |step| create_test_step(step, uri) }
lines = pickle.locations.map { |location| location.line }.sort.reverse
tags = pickle.tags.map { |tag| Test::Tag.new(Test::Location.new(uri, tag.location.line), tag.name) }
test_steps = pickle.steps.map { |step| create_test_step(step, uri, location_query) }

lines = location_query.pickle_locations(pickle).map do |location|
location.line
end.sort.reverse

tags = pickle.tags.map do |tag|
Test::Tag.new(
Test::Location.new(uri, location_query.pickle_tag_location(tag).line),
tag.name
)
end

Test::Case.new(pickle.name, test_steps, Test::Location.new(uri, lines), tags, pickle.language)
end

def create_test_step(pickle_step, uri)
lines = pickle_step.locations.map { |location| location.line }.sort.reverse
multiline_arg = create_multiline_arg(pickle_step, uri)
def create_test_step(pickle_step, uri, location_query)
lines = location_query.pickle_step_locations(pickle_step).map do |location|
location.line
end.sort.reverse

multiline_arg = create_multiline_arg(pickle_step, uri, location_query)
Test::Step.new(pickle_step.text, Test::Location.new(uri, lines), multiline_arg)
end

def create_multiline_arg(pickle_step, uri)
def create_multiline_arg(pickle_step, uri, location_query)
argumentLocation = location_query.pickle_step_argument_location(pickle_step)
line = argumentLocation ? argumentLocation.line : 0

if pickle_step.argument
if pickle_step.argument.doc_string
doc_string = pickle_step.argument.doc_string
Test::DocString.new(
doc_string.content,
doc_string.contentType,
Test::Location.new(uri, doc_string.location.line)
Test::Location.new(uri, line)
)
elsif pickle_step.argument.data_table
data_table = pickle_step.argument.data_table
first_cell = data_table.rows.first.cells.first
Test::DataTable.new(
data_table.rows.map { |row| row.cells.map { |cell| cell.value } },
Test::Location.new(uri, first_cell.location.line)
Test::Location.new(uri, line)
)
end
else
Expand Down
113 changes: 113 additions & 0 deletions lib/cucumber/core/gherkin/location_query.rb
@@ -0,0 +1,113 @@
# frozen_string_literal: true

# Note: this may move to cucumber-query later on

module Cucumber
module Core
module Gherkin
class LocationQuery
def process(message)
if message.gherkinDocument
process_gherkin_document(message.gherkinDocument)
elsif message.pickle
process_pickle(message.pickle)
end
end

def pickles
@pickles ||= []
end

def pickle_locations(pickle)
[
scenario_by_id[pickle.sourceIds[0]],
table_row_by_id[pickle.sourceIds[1]]
].compact.map(&:location)
end

def pickle_step_locations(pickle_step)
[
scenario_step_by_id[pickle_step.sourceIds[0]],
table_row_by_id[pickle_step.sourceIds[1]]
].compact.map(&:location)
end

def pickle_step_argument_location(pickle_step)
scenario_step = scenario_step_by_id[pickle_step.sourceIds[0]]
if scenario_step
case scenario_step.argument
when :doc_string
return scenario_step.doc_string.location
when :data_table
return scenario_step.data_table.location
end
end
end

def pickle_tag_location(pickle_tag)
tag_by_id[pickle_tag.sourceId].location
end

private

def process_gherkin_document(document)
if document.feature
process_children(document.feature.children)
process_tags(document.feature.tags)
end
end

def process_children(children)
children.each do |child|
process_scenario(child.scenario) if child.scenario
process_children(child.rule.children) if child.respond_to?(:rule) && child.rule
end
end

def process_scenario(scenario)
scenario_by_id[scenario.id] = scenario
process_tags(scenario.tags)
scenario.steps.each do |step|
scenario_step_by_id[step.id] = step
end
process_examples(scenario.examples) if scenario.examples
end

def process_examples(examples)
examples.each do |example|
process_tags(example.tags)
example.table_body.each do |table_row|
table_row_by_id[table_row.id] = table_row
end
end
end

def process_tags(tags)
tags.each do |tag|
tag_by_id[tag.id] = tag
end
end

def process_pickle(pickle)
pickles << pickle
end

def scenario_by_id
@scenario_by_id ||= {}
end

def scenario_step_by_id
@scenario_step_by_id ||= {}
end

def table_row_by_id
@table_row_by_id ||= {}
end

def tag_by_id
@tag_by_id ||= {}
end
end
end
end
end
5 changes: 4 additions & 1 deletion lib/cucumber/core/gherkin/parser.rb
@@ -1,5 +1,6 @@
# frozen_string_literal: true
require 'gherkin'
require 'cucumber/core/gherkin/location_query'

module Cucumber
module Core
Expand All @@ -13,15 +14,17 @@ class Parser
def initialize(receiver, event_bus)
@receiver = receiver
@event_bus = event_bus
@location_query = LocationQuery.new
end

def document(document)
messages = ::Gherkin.from_source(document.uri, document.body, gherkin_options(document))
messages.each do |message|
@location_query.process(message)
if !message.gherkinDocument.nil?
event_bus.gherkin_source_parsed(message.gherkinDocument)
elsif !message.pickle.nil?
receiver.pickle(message.pickle)
receiver.pickle(message.pickle, @location_query)
elsif !message.attachment.nil?
# Parse error
raise Core::Gherkin::ParseError.new("#{document.uri}: #{message.attachment.data}")
Expand Down
165 changes: 165 additions & 0 deletions spec/cucumber/core/gherkin/location_query_spec.rb
@@ -0,0 +1,165 @@
# -*- encoding: utf-8 -*-
# frozen_string_literal: true
require 'cucumber/core/gherkin/location_query'
require 'gherkin'

module Cucumber
module Core
module Gherkin
describe LocationQuery do
let(:subject) {
lq = LocationQuery.new
messages.each do |message|
lq.process(message)
end
lq
}

let(:content) {
<<-FEATURE
Feature: my simple feature
Scenario: A scenario
Given a passed step
FEATURE
}

let(:messages) { ::Gherkin.from_source(
'some.feature',
content, {
include_gherkin_document: true,
include_pickles: true
}
)}

let(:pickle) {
subject.pickles.first
}

describe 'pickle_locations' do
it 'returns the Location of source scenario' do
location = subject.pickle_locations(pickle).first

expect(location.line).to eq(3)
end

context 'when generated from a Examples table' do
let(:content) {
<<-FEATURE
Feature: my simple feature
Scenario: A scenario
Given a <status> step
Examples:
| status |
| passed |
| failed |
FEATURE
}

it 'returns the Locations of the source scenario and example used' do
locations = subject.pickle_locations(pickle)

expect(locations[0].line).to eq(3)
expect(locations[1].line).to eq(8)
end
end
end

describe 'pickle_step_locations' do
let(:pickle_step) { pickle.steps.first }

it 'returns the Location of the scenario step' do
expect(subject.pickle_step_locations(pickle_step).first.line).to eq(4)
end

context 'when generated from a Examples table' do
let(:content) {
<<-FEATURE
Feature: my simple feature
Scenario: A scenario
Given a <status> step
Examples:
| status |
| passed |
| failed |
FEATURE
}

it 'returns the Locations of the scenario step and example used' do
locations = subject.pickle_step_locations(pickle_step)

expect(locations[0].line).to eq(4)
expect(locations[1].line).to eq(8)
end
end
end

describe 'pickle_step_argument_location' do
let(:pickle_step) { pickle.steps.first }

context 'when there is a docstring argument' do
let(:content) {
<<-FEATURE
Feature: my simple feature
Scenario: A scenario
Given a passed step
"""
This is my DocString
"""
FEATURE
}

it 'returns the location of the doc string' do
expect(subject.pickle_step_argument_location(pickle_step).line).to eq(5)
end
end

context 'when there is a datatable argument' do
let(:content) {
<<-FEATURE
Feature: my simple feature
Scenario: A scenario
Given a passed step
| name | value |
| plic | ploc |
FEATURE
}

it 'returns the location of the datatable' do
expect(subject.pickle_step_argument_location(pickle_step).line).to eq(5)
end
end

context 'when there is no argument' do
it 'returns nil' do
expect(subject.pickle_step_argument_location(pickle_step)).to be_nil
end
end
end

describe 'pickle_tag_location' do
let(:content) {
<<-FEATURE
Feature: With tags
@acceptance
Scenario: With a single tag
Given a passed step
FEATURE
}
let(:pickle_tag) { pickle.tags.first }

it 'returns the Location of the tag' do
expect(subject.pickle_tag_location(pickle_tag).line).to eq(3)
end
end
end
end
end
end
4 changes: 3 additions & 1 deletion spec/cucumber/core/gherkin/parser_spec.rb
Expand Up @@ -69,7 +69,9 @@ def self.source(&block)
end

it "the pickles have the correct language" do
expect( receiver ).to receive(:pickle).with(pickle_with_language('ja'))
expect( receiver )
.to receive(:pickle)
.with(pickle_with_language('ja'), kind_of(LocationQuery))
parse
end
end
Expand Down

0 comments on commit 9349308

Please sign in to comment.