From aeb3a2aa9f91df98a1e4aadc1e29ddd2243d55ab Mon Sep 17 00:00:00 2001 From: Micah Geisel Date: Wed, 18 Jan 2023 10:09:04 -0600 Subject: [PATCH 1/8] Teach Cucumber::Core::Test::Case to match on parent locations. This will allow it to match on lines like Feature:, Background:, and Rule:. --- CHANGELOG.md | 3 + cucumber-core.gemspec | 2 +- lib/cucumber/core/compiler.rb | 9 ++- lib/cucumber/core/test/case.rb | 10 ++- spec/cucumber/core/test/case_spec.rb | 5 +- .../test/filters/locations_filter_spec.rb | 77 +++++++++++++++++++ spec/cucumber/core/test/runner_spec.rb | 47 +++++------ 7 files changed, 122 insertions(+), 31 deletions(-) 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..3a2f5c5e 100644 --- a/lib/cucumber/core/compiler.rb +++ b/lib/cucumber/core/compiler.rb @@ -38,8 +38,11 @@ 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 + location = Test::Location.new(uri, lines) + parent_lines = source_lines_for_pickle_parents(pickle) + parent_locations = Test::Location.new(uri, parent_lines) 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) + 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 @@ -68,6 +71,10 @@ def source_lines_for_pickle(pickle) pickle.ast_node_ids.map { |id| source_line(id) } end + def source_lines_for_pickle_parents(pickle) + gherkin_query.scenario_parent_locations(pickle.ast_node_ids[0]).map(&:line) + end + def source_lines_for_pickle_step(pickle_step) pickle_step.ast_node_ids.map { |id| source_line(id) } 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..8df3dc67 100644 --- a/spec/cucumber/core/test/filters/locations_filter_spec.rb +++ b/spec/cucumber/core/test/filters/locations_filter_spec.rb @@ -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 rule a + + Scenario: another with a rule and background + Given rule b + + Rule: A rule without a background + Scenario: with a rule and no background + Given rule c + + Scenario: another with a rule and no background + Given rule d FEATURE end @@ -109,6 +125,20 @@ def test_case_named(name) test_cases.find { |c| c.name == name } end + it 'matches the feature line 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 background line 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 the location on a background step to all scenarios' do location = Test::Location.new(file, 3) filter = described_class.new([location]) @@ -116,6 +146,46 @@ def test_case_named(name) expect(receiver.test_case_locations).to eq test_cases.map(&:location) end + it 'matches the rule with background line to all of its 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 rule background line to all of the rules 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 the location on a rule background step to all of the rules 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 the rule without background line to all of its 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 the precise location of the scenario' do location = test_case_named('two').location filter = described_class.new([location]) @@ -257,11 +327,18 @@ 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]) 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 From 7260c90fc74fd5478b1918c79bc0bceb32d64d67 Mon Sep 17 00:00:00 2001 From: Micah Geisel Date: Wed, 18 Jan 2023 14:19:08 -0600 Subject: [PATCH 2/8] ignore rubocop's complaint about long parameter list. --- .rubocop_todo.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index 9ea989f0..063a1d3f 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). From c411d6e4f71e2a877ba39a5f1dbd963f26d45cd3 Mon Sep 17 00:00:00 2001 From: Micah Geisel Date: Sun, 17 Sep 2023 17:07:13 +0200 Subject: [PATCH 3/8] ignore rubocop's complaint about too many rspec let blocks. --- .rubocop_todo.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index 063a1d3f..2d1e3801 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -229,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: From c5e81ef2b565cf03909f599a2e04850ac27053c3 Mon Sep 17 00:00:00 2001 From: Micah Geisel Date: Fri, 6 Oct 2023 13:08:20 +0100 Subject: [PATCH 4/8] refactor Core::Compiler move more low-level details into private methods. --- lib/cucumber/core/compiler.rb | 48 +++++++++++++++++------------------ 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/lib/cucumber/core/compiler.rb b/lib/cucumber/core/compiler.rb index 3a2f5c5e..2e8af89a 100644 --- a/lib/cucumber/core/compiler.rb +++ b/lib/cucumber/core/compiler.rb @@ -37,20 +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 - location = Test::Location.new(uri, lines) - parent_lines = source_lines_for_pickle_parents(pickle) - parent_locations = Test::Location.new(uri, parent_lines) - tags = source_lines_for_all_pickle_tags(pickle, uri) + 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 @@ -58,40 +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_parents(pickle) - gherkin_query.scenario_parent_locations(pickle.ast_node_ids[0]).map(&:line) + 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_pickle_step(pickle_step) - pickle_step.ast_node_ids.map { |id| source_line(id) } + 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_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) } - 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, @@ -99,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 From 890faf1eae558515907362224b7339781e11d299 Mon Sep 17 00:00:00 2001 From: Micah Geisel Date: Fri, 6 Oct 2023 13:58:57 +0100 Subject: [PATCH 5/8] tighten up LocationsFilter spec nomenclature. --- .../test/filters/locations_filter_spec.rb | 44 ++++++++++--------- 1 file changed, 24 insertions(+), 20 deletions(-) diff --git a/spec/cucumber/core/test/filters/locations_filter_spec.rb b/spec/cucumber/core/test/filters/locations_filter_spec.rb index 8df3dc67..0d116e88 100644 --- a/spec/cucumber/core/test/filters/locations_filter_spec.rb +++ b/spec/cucumber/core/test/filters/locations_filter_spec.rb @@ -110,14 +110,14 @@ module Core Given rule a Scenario: another with a rule and background - Given rule b + Given rule a Rule: A rule without a background Scenario: with a rule and no background - Given rule c + Given rule a Scenario: another with a rule and no background - Given rule d + Given rule a FEATURE end @@ -125,28 +125,28 @@ def test_case_named(name) test_cases.find { |c| c.name == name } end - it 'matches the feature line 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 background line to all scenarios' do + 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 the location on a background step to all scenarios' do + 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) end - it 'matches the rule with background line to all of its scenarios' do + 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] @@ -156,7 +156,7 @@ def test_case_named(name) ] end - it 'matches the rule background line to all of the rules scenarios' 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] @@ -166,7 +166,7 @@ def test_case_named(name) ] end - it 'matches the location on a rule background step to all of the rules scenarios' do + 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] @@ -176,7 +176,7 @@ def test_case_named(name) ] end - it 'matches the rule without background line to all of its scenarios' do + 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] @@ -186,43 +186,47 @@ def test_case_named(name) ] end - it 'matches the precise location of the scenario' do + 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] 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] 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] 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] 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]) From 48c861f1427f4902112826745f6b854142997f02 Mon Sep 17 00:00:00 2001 From: Micah Geisel Date: Fri, 27 Oct 2023 10:40:57 +0100 Subject: [PATCH 6/8] update to style preference of using parens with #eq. --- .../test/filters/locations_filter_spec.rb | 70 +++++++++---------- 1 file changed, 35 insertions(+), 35 deletions(-) diff --git a/spec/cucumber/core/test/filters/locations_filter_spec.rb b/spec/cucumber/core/test/filters/locations_filter_spec.rb index 0d116e88..ce6d9b8b 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 [ + 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 @@ -129,68 +129,68 @@ def test_case_named(name) 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) + 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) + 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 [ + 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 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 [ + 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 [ + 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 [ + 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, ignoring whitespace locations' do @@ -199,38 +199,38 @@ def test_case_named(name) 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 [ + expect(receiver.test_case_locations).to eq([ test_case_named('one').location, test_case_named('two').location - ] + ]) end 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 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 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 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 @@ -242,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 @@ -269,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 @@ -292,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 @@ -340,13 +340,13 @@ def test_case_named(name) 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) + 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 @@ -358,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 @@ -411,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 From 3792f0dc02fff49bcdb0a45b0381680aa07f46b8 Mon Sep 17 00:00:00 2001 From: Micah Geisel Date: Fri, 27 Oct 2023 10:41:23 +0100 Subject: [PATCH 7/8] rename steps for better self-documentation. the text itself doesn't actually matter. --- spec/cucumber/core/test/filters/locations_filter_spec.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/spec/cucumber/core/test/filters/locations_filter_spec.rb b/spec/cucumber/core/test/filters/locations_filter_spec.rb index ce6d9b8b..e7377f1d 100644 --- a/spec/cucumber/core/test/filters/locations_filter_spec.rb +++ b/spec/cucumber/core/test/filters/locations_filter_spec.rb @@ -107,17 +107,17 @@ module Core Given background Scenario: with a rule and background - Given rule a + Given a rule with a background Scenario: another with a rule and background - Given rule a + Given a rule with a background Rule: A rule without a background Scenario: with a rule and no background - Given rule a + Given a rule without a background Scenario: another with a rule and no background - Given rule a + Given a rule without a background FEATURE end From 71140ab797855190f22055984cc172b5397a9f1f Mon Sep 17 00:00:00 2001 From: Micah Geisel Date: Fri, 27 Oct 2023 10:47:49 +0100 Subject: [PATCH 8/8] satisfy rubocop with dubious formatting. --- .../test/filters/locations_filter_spec.rb | 36 +++++++++---------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/spec/cucumber/core/test/filters/locations_filter_spec.rb b/spec/cucumber/core/test/filters/locations_filter_spec.rb index e7377f1d..43355fbf 100644 --- a/spec/cucumber/core/test/filters/locations_filter_spec.rb +++ b/spec/cucumber/core/test/filters/locations_filter_spec.rb @@ -46,9 +46,9 @@ 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) - ]) + Test::Location.new('features/test.feature', 3), + Test::Location.new('features/test.feature', 6) + ]) end it "filters out scenarios that don't match" do @@ -151,9 +151,9 @@ def test_case_named(name) 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 - ]) + test_case_named('with a rule and background').location, + test_case_named('another with a rule and background').location + ]) end it "matches the rule background location to all of the rule's scenarios" do @@ -161,9 +161,9 @@ def test_case_named(name) 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 - ]) + 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 @@ -171,9 +171,9 @@ def test_case_named(name) 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 - ]) + 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 @@ -181,9 +181,9 @@ def test_case_named(name) 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 - ]) + 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 @@ -200,9 +200,9 @@ def test_case_named(name) 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('one').location, - test_case_named('two').location - ]) + test_case_named('one').location, + test_case_named('two').location + ]) end it 'matches the first scenario step location to the scenario' do