diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index 9ea989f0..2d1e3801 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -134,6 +134,8 @@ Metrics/ModuleLength: # Configuration parameters: CountKeywordArgs, MaxOptionalParameters. Metrics/ParameterLists: Max: 7 + Exclude: + - 'lib/cucumber/core/test/case.rb' # Offense count: 4 # This cop supports safe auto-correction (--auto-correct). @@ -227,6 +229,8 @@ RSpec/MultipleExpectations: # Configuration parameters: AllowSubject. RSpec/MultipleMemoizedHelpers: Max: 20 + Exclude: + - 'spec/cucumber/core/test/runner_spec.rb' # Offense count: 13 RSpec/NestedGroups: diff --git a/CHANGELOG.md b/CHANGELOG.md index 1c87863d..ab68ad1a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,9 @@ Please visit [cucumber/CONTRIBUTING.md](https://github.com/cucumber/cucumber/blo ([#261](https://github.com/cucumber/cucumber-ruby-core/pull/261)) - Permit usage of gherkin v27 +### Fixed +- Restore support for matching a scenario by its Feature, Background, and Rule line numbers. ([#247](https://github.com/cucumber/cucumber-ruby-core/pull/237)) + ## [12.0.0] - 2023-09-06 ### Changed - Update gherkin and messages minimum dependencies diff --git a/cucumber-core.gemspec b/cucumber-core.gemspec index b8688660..6e6f4966 100644 --- a/cucumber-core.gemspec +++ b/cucumber-core.gemspec @@ -23,7 +23,7 @@ Gem::Specification.new do |s| 'source_code_uri' => 'https://github.com/cucumber/cucumber-ruby-core' } - s.add_dependency 'cucumber-gherkin', '>= 25', '< 28' + s.add_dependency 'cucumber-gherkin', '>= 27', '< 28' s.add_dependency 'cucumber-messages', '>= 20', '< 23' s.add_dependency 'cucumber-tag-expressions', '~> 5.0', '>= 5.0.4' diff --git a/lib/cucumber/core/compiler.rb b/lib/cucumber/core/compiler.rb index 0a00b420..2e8af89a 100644 --- a/lib/cucumber/core/compiler.rb +++ b/lib/cucumber/core/compiler.rb @@ -37,17 +37,18 @@ def done def create_test_case(pickle) uri = pickle.uri test_steps = pickle.steps.map { |step| create_test_step(step, uri) } - lines = source_lines_for_pickle(pickle).sort.reverse - tags = source_lines_for_all_pickle_tags(pickle, uri) - test_case = Test::Case.new(id_generator.new_id, pickle.name, test_steps, Test::Location.new(uri, lines), tags, pickle.language) + location = location_from_pickle(pickle) + parent_locations = parent_locations_from_pickle(pickle) + tags = tags_from_pickle(pickle, uri) + test_case = Test::Case.new(id_generator.new_id, pickle.name, test_steps, location, parent_locations, tags, pickle.language) @event_bus&.test_case_created(test_case, pickle) test_case end def create_test_step(pickle_step, uri) - lines = source_lines_for_pickle_step(pickle_step).sort.reverse + location = location_from_pickle_step(pickle_step, uri) multiline_arg = create_multiline_arg(pickle_step, uri) - step = Test::Step.new(id_generator.new_id, pickle_step.text, Test::Location.new(uri, lines), multiline_arg) + step = Test::Step.new(id_generator.new_id, pickle_step.text, location, multiline_arg) @event_bus&.test_step_created(step, pickle_step) step end @@ -55,36 +56,42 @@ def create_test_step(pickle_step, uri) def create_multiline_arg(pickle_step, _uri) if pickle_step.argument if pickle_step.argument.doc_string - pickle_step_for_doc_string(pickle_step) + doc_string_from_pickle_step(pickle_step) elsif pickle_step.argument.data_table - pickle_step_for_data_table(pickle_step) + data_table_from_pickle_step(pickle_step) end else Test::EmptyMultilineArgument.new end end - def source_lines_for_pickle(pickle) - pickle.ast_node_ids.map { |id| source_line(id) } + def location_from_pickle(pickle) + lines = pickle.ast_node_ids.map { |id| source_line(id) } + Test::Location.new(pickle.uri, lines.sort.reverse) end - def source_lines_for_pickle_step(pickle_step) - pickle_step.ast_node_ids.map { |id| source_line(id) } + def parent_locations_from_pickle(pickle) + parent_lines = gherkin_query.scenario_parent_locations(pickle.ast_node_ids[0]).map(&:line) + Test::Location.new(pickle.uri, parent_lines) end - def source_lines_for_all_pickle_tags(pickle, uri) - pickle.tags.map { |tag| Test::Tag.new(Test::Location.new(uri, source_line_for_pickle_tag(tag)), tag.name) } + def location_from_pickle_step(pickle_step, uri) + lines = pickle_step.ast_node_ids.map { |id| source_line(id) } + Test::Location.new(uri, lines.sort.reverse) end - def source_line_for_pickle_tag(tag) - source_line(tag.ast_node_id) + def tags_from_pickle(pickle, uri) + pickle.tags.map do |tag| + location = Test::Location.new(uri, source_line(tag.ast_node_id)) + Test::Tag.new(location, tag.name) + end end def source_line(id) gherkin_query.location(id).line end - def pickle_step_for_doc_string(pickle_step) + def doc_string_from_pickle_step(pickle_step) doc_string = pickle_step.argument.doc_string Test::DocString.new( doc_string.content, @@ -92,11 +99,11 @@ def pickle_step_for_doc_string(pickle_step) ) end - def pickle_step_for_data_table(pickle_step) + def data_table_from_pickle_step(pickle_step) data_table = pickle_step.argument.data_table Test::DataTable.new( data_table.rows.map do |row| - row.cells.map { |cell| cell.value } + row.cells.map(&:value) end ) end diff --git a/lib/cucumber/core/test/case.rb b/lib/cucumber/core/test/case.rb index 0270f24b..132073df 100644 --- a/lib/cucumber/core/test/case.rb +++ b/lib/cucumber/core/test/case.rb @@ -7,14 +7,15 @@ module Cucumber module Core module Test class Case - attr_reader :id, :name, :test_steps, :location, :tags, :language, :around_hooks + attr_reader :id, :name, :test_steps, :location, :parent_locations, :tags, :language, :around_hooks - def initialize(id, name, test_steps, location, tags, language, around_hooks = []) + def initialize(id, name, test_steps, location, parent_locations, tags, language, around_hooks = []) raise ArgumentError.new("test_steps should be an Array but is a #{test_steps.class}") unless test_steps.is_a?(Array) @id = id @name = name @test_steps = test_steps @location = location + @parent_locations = parent_locations @tags = tags @language = language @around_hooks = around_hooks @@ -36,11 +37,11 @@ def describe_to(visitor, *args) end def with_steps(test_steps) - self.class.new(id, name, test_steps, location, tags, language, around_hooks) + self.class.new(id, name, test_steps, location, parent_locations, tags, language, around_hooks) end def with_around_hooks(around_hooks) - self.class.new(id, name, test_steps, location, tags, language, around_hooks) + self.class.new(id, name, test_steps, location, parent_locations, tags, language, around_hooks) end def match_tags?(*expressions) @@ -61,6 +62,7 @@ def match_locations?(queried_locations) def matching_locations [ + parent_locations, location, tags.map(&:location), test_steps.map(&:matching_locations) diff --git a/spec/cucumber/core/test/case_spec.rb b/spec/cucumber/core/test/case_spec.rb index ab01f17c..f9693269 100644 --- a/spec/cucumber/core/test/case_spec.rb +++ b/spec/cucumber/core/test/case_spec.rb @@ -13,9 +13,10 @@ let(:id) { double } let(:name) { double } let(:location) { double } + let(:parent_locations) { double } let(:tags) { double } let(:language) { double } - let(:test_case) { described_class.new(id, name, test_steps, location, tags, language) } + let(:test_case) { described_class.new(id, name, test_steps, location, parent_locations, tags, language) } let(:test_steps) { [double, double] } context 'describing itself' do @@ -42,7 +43,7 @@ expect(first_hook).to receive(:describe_to).ordered.and_yield expect(second_hook).to receive(:describe_to).ordered.and_yield around_hooks = [first_hook, second_hook] - described_class.new(id, name, [], location, tags, language, around_hooks).describe_to(visitor, double) + described_class.new(id, name, [], location, parent_locations, tags, language, around_hooks).describe_to(visitor, double) end end diff --git a/spec/cucumber/core/test/filters/locations_filter_spec.rb b/spec/cucumber/core/test/filters/locations_filter_spec.rb index 6887e20a..43355fbf 100644 --- a/spec/cucumber/core/test/filters/locations_filter_spec.rb +++ b/spec/cucumber/core/test/filters/locations_filter_spec.rb @@ -36,7 +36,7 @@ module Core ] filter = described_class.new(locations) compile([doc], receiver, [filter]) - expect(receiver.test_case_locations).to eq locations + expect(receiver.test_case_locations).to eq(locations) end it 'works with wildcard locations' do @@ -45,10 +45,10 @@ module Core ] filter = described_class.new(locations) compile([doc], receiver, [filter]) - expect(receiver.test_case_locations).to eq [ - Test::Location.new('features/test.feature', 3), - Test::Location.new('features/test.feature', 6) - ] + expect(receiver.test_case_locations).to eq([ + Test::Location.new('features/test.feature', 3), + Test::Location.new('features/test.feature', 6) + ]) end it "filters out scenarios that don't match" do @@ -57,7 +57,7 @@ module Core ] filter = described_class.new(locations) compile([doc], receiver, [filter]) - expect(receiver.test_case_locations).to eq locations + expect(receiver.test_case_locations).to eq(locations) end describe 'matching location' do @@ -102,6 +102,22 @@ module Core | 1 | 2 | | 3 | 4 | + Rule: A rule with a background + Background: A background rule + Given background + + Scenario: with a rule and background + Given a rule with a background + + Scenario: another with a rule and background + Given a rule with a background + + Rule: A rule without a background + Scenario: with a rule and no background + Given a rule without a background + + Scenario: another with a rule and no background + Given a rule without a background FEATURE end @@ -109,54 +125,112 @@ def test_case_named(name) test_cases.find { |c| c.name == name } end - it 'matches the location on a background step to all scenarios' do + it 'matches the feature location to all scenarios' do + location = Test::Location.new(file, 1) + filter = described_class.new([location]) + compile [doc], receiver, [filter] + expect(receiver.test_case_locations).to eq(test_cases.map(&:location)) + end + + it 'matches the feature background location to all scenarios' do + location = Test::Location.new(file, 2) + filter = described_class.new([location]) + compile [doc], receiver, [filter] + expect(receiver.test_case_locations).to eq(test_cases.map(&:location)) + end + + it 'matches a feature background step location to all scenarios' do location = Test::Location.new(file, 3) filter = described_class.new([location]) compile([doc], receiver, [filter]) - expect(receiver.test_case_locations).to eq test_cases.map(&:location) + expect(receiver.test_case_locations).to eq(test_cases.map(&:location)) + end + + it "matches a rule location (containing a background) to all of the rule's scenarios" do + location = Test::Location.new(file, 29) + filter = described_class.new([location]) + compile [doc], receiver, [filter] + expect(receiver.test_case_locations).to eq([ + test_case_named('with a rule and background').location, + test_case_named('another with a rule and background').location + ]) end - it 'matches the precise location of the scenario' do + it "matches the rule background location to all of the rule's scenarios" do + location = Test::Location.new(file, 30) + filter = described_class.new([location]) + compile [doc], receiver, [filter] + expect(receiver.test_case_locations).to eq([ + test_case_named('with a rule and background').location, + test_case_named('another with a rule and background').location + ]) + end + + it "matches a rule background step location to all of the rule's scenarios" do + location = Test::Location.new(file, 31) + filter = described_class.new([location]) + compile [doc], receiver, [filter] + expect(receiver.test_case_locations).to eq([ + test_case_named('with a rule and background').location, + test_case_named('another with a rule and background').location + ]) + end + + it "matches a rule location (without a background) to all of the rule's scenarios" do + location = Test::Location.new(file, 39) + filter = described_class.new([location]) + compile [doc], receiver, [filter] + expect(receiver.test_case_locations).to eq([ + test_case_named('with a rule and no background').location, + test_case_named('another with a rule and no background').location + ]) + end + + it 'matches a scenario location to the scenario' do location = test_case_named('two').location filter = described_class.new([location]) compile([doc], receiver, [filter]) - expect(receiver.test_case_locations).to eq [test_case_named('two').location] + expect(receiver.test_case_locations).to eq([test_case_named('two').location]) end - it 'matches multiple locations' do - good_location = Test::Location.new(file, 10) - bad_location = Test::Location.new(file, 7) - filter = described_class.new([good_location, bad_location]) + it 'matches multiple locations, ignoring whitespace locations' do + scenario_location = Test::Location.new(file, 5) + another_scenario_location = Test::Location.new(file, 10) + whitespace_location = Test::Location.new(file, 7) + filter = described_class.new([scenario_location, another_scenario_location, whitespace_location]) compile([doc], receiver, [filter]) - expect(receiver.test_case_locations).to eq [test_case_named('two').location] + expect(receiver.test_case_locations).to eq([ + test_case_named('one').location, + test_case_named('two').location + ]) end - it 'matches a location on the first step of the scenario' do + it 'matches the first scenario step location to the scenario' do location = Test::Location.new(file, 11) filter = described_class.new([location]) compile([doc], receiver, [filter]) - expect(receiver.test_case_locations).to eq [test_case_named('two').location] + expect(receiver.test_case_locations).to eq([test_case_named('two').location]) end - it 'matches a location on the last step of the scenario' do + it 'matches the last scenario step location to the scenario' do location = Test::Location.new(file, 12) filter = described_class.new([location]) compile([doc], receiver, [filter]) - expect(receiver.test_case_locations).to eq [test_case_named('two').location] + expect(receiver.test_case_locations).to eq([test_case_named('two').location]) end - it "matches a location on the scenario's tags" do + it "matches a scenario's tag location to the scenario" do location = Test::Location.new(file, 9) filter = described_class.new([location]) compile([doc], receiver, [filter]) - expect(receiver.test_case_locations).to eq [test_case_named('two').location] + expect(receiver.test_case_locations).to eq([test_case_named('two').location]) end - it 'does not return a matched location on a whitespace line' do + it 'does not match a whitespace location to any scenarios' do location = Test::Location.new(file, 13) filter = described_class.new([location]) compile([doc], receiver, [filter]) - expect(receiver.test_case_locations).to eq [] + expect(receiver.test_case_locations).to eq([]) end context 'with a docstring' do @@ -168,21 +242,21 @@ def test_case_named(name) location = Test::Location.new(file, 17) filter = described_class.new([location]) compile([doc], receiver, [filter]) - expect(receiver.test_case_locations).to eq [test_case_named('with docstring').location] + expect(receiver.test_case_locations).to eq([test_case_named('with docstring').location]) end it 'matches a location in the middle of the docstring' do location = Test::Location.new(file, 18) filter = described_class.new([location]) compile([doc], receiver, [filter]) - expect(receiver.test_case_locations).to eq [test_case_named('with docstring').location] + expect(receiver.test_case_locations).to eq([test_case_named('with docstring').location]) end it 'matches a location at the end of the docstring' do location = Test::Location.new(file, 19) filter = described_class.new([location]) compile([doc], receiver, [filter]) - expect(receiver.test_case_locations).to eq [test_case_named('with docstring').location] + expect(receiver.test_case_locations).to eq([test_case_named('with docstring').location]) end end @@ -195,19 +269,19 @@ def test_case_named(name) it 'matches a location at the start of the table' do filter = described_class.new([starting_location]) compile([doc], receiver, [filter]) - expect(receiver.test_case_locations).to eq [test_case_named('with a table').location] + expect(receiver.test_case_locations).to eq([test_case_named('with a table').location]) end it 'matches a location at the middle of the table' do filter = described_class.new([midpoint_location]) compile([doc], receiver, [filter]) - expect(receiver.test_case_locations).to eq [test_case_named('with a table').location] + expect(receiver.test_case_locations).to eq([test_case_named('with a table').location]) end it 'matches a location at the end of the table' do filter = described_class.new([ending_location]) compile([doc], receiver, [filter]) - expect(receiver.test_case_locations).to eq [test_case_named('with a table').location] + expect(receiver.test_case_locations).to eq([test_case_named('with a table').location]) end end @@ -218,7 +292,7 @@ def test_case_named(name) location_last_step_tc_two = Test::Location.new(file, 12) filter = described_class.new([location_tc_two, location_tc_one, location_last_step_tc_two]) compile([doc], receiver, [filter]) - expect(receiver.test_case_locations).to eq [test_case_named('two').location, test_case_named('one').location] + expect(receiver.test_case_locations).to eq([test_case_named('two').location, test_case_named('one').location]) end end end @@ -257,15 +331,22 @@ def test_case_named(name) end let(:test_case) { test_cases.find { |c| c.name == 'two b' } } + let(:feature_location) { Test::Location.new(file, 1) } let(:row_location) { Test::Location.new(file, 19) } let(:start_of_outline_location) { Test::Location.new(file, 8) } let(:middle_of_outline_location) { Test::Location.new(file, 10) } let(:outline_tags_location) { Test::Location.new(file, 7) } + it 'matches the feature line to all scenarios' do + filter = described_class.new([feature_location]) + compile [doc], receiver, [filter] + expect(receiver.test_case_locations).to eq(test_cases.map(&:location)) + end + it 'matches row location to the test case of the row' do filter = described_class.new([row_location]) compile([doc], receiver, [filter]) - expect(receiver.test_case_locations).to eq [test_case.location] + expect(receiver.test_case_locations).to eq([test_case.location]) end it 'matches outline location with the all test cases of all the tables' do @@ -277,20 +358,20 @@ def test_case_named(name) it 'matches a location on a step of the scenario outline with all test cases of all the tables' do filter = described_class.new([middle_of_outline_location]) compile([doc], receiver, [filter]) - expect(receiver.test_case_locations.map(&:line)).to eq [19, 23, 24] + expect(receiver.test_case_locations.map(&:line)).to eq([19, 23, 24]) end it "matches a location on the scenario outline's tags with all test cases of all the tables" do filter = described_class.new([outline_tags_location]) compile([doc], receiver, [filter]) - expect(receiver.test_case_locations.map(&:line)).to eq [19, 23, 24] + expect(receiver.test_case_locations.map(&:line)).to eq([19, 23, 24]) end it "doesn't match the location of the examples line" do location = Test::Location.new(file, 17) filter = described_class.new([location]) compile([doc], receiver, [filter]) - expect(receiver.test_case_locations).to eq [] + expect(receiver.test_case_locations).to eq([]) end end end @@ -330,7 +411,7 @@ def test_case_named(name) Timeout.timeout(max_duration_ms / 1000.0) do compile(docs, receiver, [filter]) end - expect(receiver.test_cases.length).to eq num_features * num_scenarios_per_feature + expect(receiver.test_cases.length).to eq(num_features * num_scenarios_per_feature) end end end diff --git a/spec/cucumber/core/test/runner_spec.rb b/spec/cucumber/core/test/runner_spec.rb index 1503a9cc..7bbaf7e6 100644 --- a/spec/cucumber/core/test/runner_spec.rb +++ b/spec/cucumber/core/test/runner_spec.rb @@ -7,22 +7,23 @@ require 'cucumber/core/test/duration_matcher' describe Cucumber::Core::Test::Runner do - let(:step_id) { double } - let(:test_id) { double } - let(:name) { double } - let(:location) { double } - let(:tags) { double } - let(:language) { double } - let(:test_case) { Cucumber::Core::Test::Case.new(test_id, name, test_steps, location, tags, language) } - let(:text) { double } - let(:runner) { described_class.new(event_bus) } - let(:event_bus) { double.as_null_object } - let(:passing) { Cucumber::Core::Test::Step.new(step_id, text, location, location).with_action { :no_op } } - let(:failing) { Cucumber::Core::Test::Step.new(step_id, text, location, location).with_action { raise exception } } - let(:pending) { Cucumber::Core::Test::Step.new(step_id, text, location, location).with_action { raise Cucumber::Core::Test::Result::Pending.new('TODO') } } - let(:skipping) { Cucumber::Core::Test::Step.new(step_id, text, location, location).with_action { raise Cucumber::Core::Test::Result::Skipped.new } } - let(:undefined) { Cucumber::Core::Test::Step.new(step_id, text, location, location) } - let(:exception) { StandardError.new('test error') } + let(:step_id) { double } + let(:test_id) { double } + let(:name) { double } + let(:location) { double } + let(:parent_locations) { double } + let(:tags) { double } + let(:language) { double } + let(:test_case) { Cucumber::Core::Test::Case.new(test_id, name, test_steps, location, parent_locations, tags, language) } + let(:text) { double } + let(:runner) { described_class.new(event_bus) } + let(:event_bus) { double.as_null_object } + let(:passing) { Cucumber::Core::Test::Step.new(step_id, text, location, location).with_action { :no_op } } + let(:failing) { Cucumber::Core::Test::Step.new(step_id, text, location, location).with_action { raise exception } } + let(:pending) { Cucumber::Core::Test::Step.new(step_id, text, location, location).with_action { raise Cucumber::Core::Test::Result::Pending.new('TODO') } } + let(:skipping) { Cucumber::Core::Test::Step.new(step_id, text, location, location).with_action { raise Cucumber::Core::Test::Result::Skipped.new } } + let(:undefined) { Cucumber::Core::Test::Step.new(step_id, text, location, location) } + let(:exception) { StandardError.new('test error') } before do allow(event_bus).to receive(:test_case_started) @@ -226,8 +227,8 @@ context 'with multiple test cases' do context 'when the first test case fails' do - let(:first_test_case) { Cucumber::Core::Test::Case.new(test_id, name, [failing], location, tags, language) } - let(:last_test_case) { Cucumber::Core::Test::Case.new(test_id, name, [passing], location, tags, language) } + let(:first_test_case) { Cucumber::Core::Test::Case.new(test_id, name, [failing], location, parent_locations, tags, language) } + let(:last_test_case) { Cucumber::Core::Test::Case.new(test_id, name, [passing], location, parent_locations, tags, language) } let(:test_cases) { [first_test_case, last_test_case] } it 'reports the results correctly for the following test case' do @@ -260,7 +261,7 @@ it "passes normally when around hooks don't fail" do around_hook = Cucumber::Core::Test::AroundHook.new { |block| block.call } - test_case = Cucumber::Core::Test::Case.new(test_id, name, [passing_step], location, tags, language, [around_hook]) + test_case = Cucumber::Core::Test::Case.new(test_id, name, [passing_step], location, parent_locations, tags, language, [around_hook]) expect(event_bus).to receive(:test_case_finished).with(test_case, anything) do |_reported_test_case, result| expect(result).to be_passed end @@ -269,7 +270,7 @@ it 'gets a failed result if the Around hook fails before the test case is run' do around_hook = Cucumber::Core::Test::AroundHook.new { |_block| raise exception } - test_case = Cucumber::Core::Test::Case.new(test_id, name, [passing_step], location, tags, language, [around_hook]) + test_case = Cucumber::Core::Test::Case.new(test_id, name, [passing_step], location, parent_locations, tags, language, [around_hook]) expect(event_bus).to receive(:test_case_finished).with(test_case, anything) do |_reported_test_case, result| expect(result).to be_failed expect(result.exception).to eq exception @@ -279,7 +280,7 @@ it 'gets a failed result if the Around hook fails after the test case is run' do around_hook = Cucumber::Core::Test::AroundHook.new { |block| block.call; raise exception } - test_case = Cucumber::Core::Test::Case.new(test_id, name, [passing_step], location, tags, language, [around_hook]) + test_case = Cucumber::Core::Test::Case.new(test_id, name, [passing_step], location, parent_locations, tags, language, [around_hook]) expect(event_bus).to receive(:test_case_finished).with(test_case, anything) do |_reported_test_case, result| expect(result).to be_failed expect(result.exception).to eq exception @@ -290,7 +291,7 @@ it 'fails when a step fails if the around hook works' do around_hook = Cucumber::Core::Test::AroundHook.new { |block| block.call } failing_step = Cucumber::Core::Test::Step.new(step_id, text, location, location).with_action { raise exception } - test_case = Cucumber::Core::Test::Case.new(test_id, name, [failing_step], location, tags, language, [around_hook]) + test_case = Cucumber::Core::Test::Case.new(test_id, name, [failing_step], location, parent_locations, tags, language, [around_hook]) expect(event_bus).to receive(:test_case_finished).with(test_case, anything) do |_reported_test_case, result| expect(result).to be_failed expect(result.exception).to eq exception @@ -300,7 +301,7 @@ it 'sends after_test_step for a step interrupted by (a timeout in) the around hook' do around_hook = Cucumber::Core::Test::AroundHook.new { |block| block.call; raise exception } - test_case = Cucumber::Core::Test::Case.new(test_id, name, [], location, tags, language, [around_hook]) + test_case = Cucumber::Core::Test::Case.new(test_id, name, [], location, parent_locations, tags, language, [around_hook]) allow(runner).to receive(:running_test_step).and_return(passing_step) expect(event_bus).to receive(:test_step_finished).with(passing_step, anything) do |_reported_test_case, result| expect(result).to be_failed