From fb1f1df69c47ca5f8b77b2706cdcbee3d4160241 Mon Sep 17 00:00:00 2001
From: Luke Hill <20105237+luke-hill@users.noreply.github.com>
Date: Tue, 10 Sep 2024 15:30:45 +0100
Subject: [PATCH] Bugfix/invalid conversion (#285)

* Fix invalid conversion for time

* AF: Lint/Symbolconversion

* Fix line spacing for some specs
Fix situation where an invalid context let was redundant

* Fix reference to helpers message uuid being wrong

* Bump minimum messages requirement

* Add changelog entry

* Bump minimum ruby and max messages requirements

* Bump rubocop config min version

* Set min patch of rubocop-rspec to 5

* Update changelog

* Drop ruby 2.7 from testing matrix and run on system rubies / bundlers by default!
---
 .github/workflows/rubocop.yml                 |  3 +-
 .github/workflows/test.yml                    |  4 +--
 .rubocop.yml                                  |  2 +-
 CHANGELOG.md                                  |  8 +++++
 cucumber-core.gemspec                         |  6 ++--
 lib/cucumber/core/compiler.rb                 |  2 +-
 lib/cucumber/core/event.rb                    |  2 +-
 lib/cucumber/core/filter.rb                   |  4 +--
 lib/cucumber/core/test/result.rb              |  6 ++--
 spec/cucumber/core/event_bus_spec.rb          |  7 ++--
 .../core/test/locations_filter_spec.rb        |  7 ++--
 spec/cucumber/core/test/runner_spec.rb        | 32 +++++++++----------
 12 files changed, 43 insertions(+), 40 deletions(-)

diff --git a/.github/workflows/rubocop.yml b/.github/workflows/rubocop.yml
index b0c6950f..2a4d407d 100644
--- a/.github/workflows/rubocop.yml
+++ b/.github/workflows/rubocop.yml
@@ -15,7 +15,6 @@ jobs:
       - uses: actions/checkout@v4
       - uses: ruby/setup-ruby@v1
         with:
-          ruby-version: 2.7
-          rubygems: latest
+          ruby-version: 3.0
           bundler-cache: true
       - run: bundle exec rubocop
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index bfcf585b..bfe83922 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -18,7 +18,7 @@ jobs:
       fail-fast: false
       matrix:
         os: [ubuntu-latest]
-        ruby: ['2.7', '3.0', '3.1', '3.2', '3.3']
+        ruby: ['3.0', '3.1', '3.2', '3.3']
         include:
           - os: ubuntu-latest
             ruby: jruby
@@ -33,8 +33,6 @@ jobs:
       - uses: ruby/setup-ruby@v1
         with:
           ruby-version: ${{ matrix.ruby }}
-          bundler: 2.4.19
-          rubygems: latest
           bundler-cache: true
       - name: Run tests
         run: bundle exec rake
diff --git a/.rubocop.yml b/.rubocop.yml
index 28844b33..e40791bd 100644
--- a/.rubocop.yml
+++ b/.rubocop.yml
@@ -10,7 +10,7 @@ require:
 
 AllCops:
   # Keep this inline with the gemspec
-  TargetRubyVersion: 2.7
+  TargetRubyVersion: 3.0
   DisplayCopNames: true
   DisplayStyleGuide: true
   NewCops: enable
diff --git a/CHANGELOG.md b/CHANGELOG.md
index c53053e8..a944aca9 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -9,6 +9,14 @@ This document is formatted according to the principles of [Keep A CHANGELOG](htt
 Please visit [cucumber/CONTRIBUTING.md](https://github.com/cucumber/cucumber/blob/master/CONTRIBUTING.md) for more info on how to contribute to Cucumber.
 
 ## [Unreleased]
+### Changed
+- Permit usage of gherkin up to v29 and messages up to v28
+
+### Fixed
+- References to the Time Conversion and UUID helpers needed altering to use the `Helpers` namespace
+
+### Removed
+- Remove support for ruby 2.7 and below. 3.0 or higher is required now (Owing to messages bump)
 
 ## [14.0.0] - 2024-08-08
 ### Changed
diff --git a/cucumber-core.gemspec b/cucumber-core.gemspec
index 860a51f5..2ef22acb 100644
--- a/cucumber-core.gemspec
+++ b/cucumber-core.gemspec
@@ -12,7 +12,7 @@ Gem::Specification.new do |s|
   s.homepage    = 'https://cucumber.io'
   s.platform    = Gem::Platform::RUBY
   s.license     = 'MIT'
-  s.required_ruby_version = '>= 2.7'
+  s.required_ruby_version = '>= 3.0'
   s.required_rubygems_version = '>= 3.2.8'
 
   s.metadata = {
@@ -24,7 +24,7 @@ Gem::Specification.new do |s|
   }
 
   s.add_dependency 'cucumber-gherkin', '> 27', '< 30'
-  s.add_dependency 'cucumber-messages', '> 22', '< 27'
+  s.add_dependency 'cucumber-messages', '> 25', '< 28'
   s.add_dependency 'cucumber-tag-expressions', '> 5', '< 7'
 
   s.add_development_dependency 'rake', '~> 13.2'
@@ -32,7 +32,7 @@ Gem::Specification.new do |s|
   s.add_development_dependency 'rubocop', '~> 1.61.0'
   s.add_development_dependency 'rubocop-packaging', '~> 0.5.2'
   s.add_development_dependency 'rubocop-rake', '~> 0.6.0'
-  s.add_development_dependency 'rubocop-rspec', '~> 3.0.1'
+  s.add_development_dependency 'rubocop-rspec', '~> 3.0.5'
 
   s.files            = Dir['CHANGELOG.md', 'README.md', 'LICENSE', 'lib/**/*']
   s.rdoc_options     = ['--charset=UTF-8']
diff --git a/lib/cucumber/core/compiler.rb b/lib/cucumber/core/compiler.rb
index 51c5f2e4..25608999 100644
--- a/lib/cucumber/core/compiler.rb
+++ b/lib/cucumber/core/compiler.rb
@@ -18,7 +18,7 @@ class Compiler
 
       def initialize(receiver, gherkin_query, event_bus = nil)
         @receiver = receiver
-        @id_generator = Cucumber::Messages::IdGenerator::UUID.new
+        @id_generator = Cucumber::Messages::Helpers::IdGenerator::UUID.new
         @gherkin_query = gherkin_query
         @event_bus = event_bus
       end
diff --git a/lib/cucumber/core/event.rb b/lib/cucumber/core/event.rb
index 382c961b..6787835f 100644
--- a/lib/cucumber/core/event.rb
+++ b/lib/cucumber/core/event.rb
@@ -19,7 +19,7 @@ def self.new(*events)
 
           define_method(:initialize) do |*attributes|
             events.zip(attributes) do |name, value|
-              instance_variable_set("@#{name}".to_sym, value)
+              instance_variable_set(:"@#{name}", value)
             end
           end
 
diff --git a/lib/cucumber/core/filter.rb b/lib/cucumber/core/filter.rb
index 7be58afb..2b65bca9 100644
--- a/lib/cucumber/core/filter.rb
+++ b/lib/cucumber/core/filter.rb
@@ -48,7 +48,7 @@ def self.new(*attributes, &block)
 
           define_method(:initialize) do |*args|
             attributes.zip(args) do |name, value|
-              instance_variable_set("@#{name}".to_sym, value)
+              instance_variable_set(:"@#{name}", value)
             end
           end
 
@@ -63,7 +63,7 @@ def done
           end
 
           define_method(:with_receiver) do |new_receiver|
-            args = attributes.map { |name| instance_variable_get("@#{name}".to_sym) }
+            args = attributes.map { |name| instance_variable_get(:"@#{name}") }
             args[-1] = new_receiver
             self.class.new(*args)
           end
diff --git a/lib/cucumber/core/test/result.rb b/lib/cucumber/core/test/result.rb
index ce3962af..7043f060 100644
--- a/lib/cucumber/core/test/result.rb
+++ b/lib/cucumber/core/test/result.rb
@@ -1,7 +1,7 @@
 # frozen_string_literal: true
 
 require 'cucumber/messages'
-require 'cucumber/messages/time_conversion'
+require 'cucumber/messages/helpers/time_conversion'
 
 module Cucumber
   module Core
@@ -404,7 +404,7 @@ def increment_total(status)
         end
 
         class Duration
-          include Cucumber::Messages::TimeConversion
+          include Cucumber::Messages::Helpers::TimeConversion
 
           attr_reader :nanoseconds
 
@@ -425,7 +425,7 @@ def to_message_duration
         end
 
         class UnknownDuration
-          include Cucumber::Messages::TimeConversion
+          include Cucumber::Messages::Helpers::TimeConversion
 
           def tap
             self
diff --git a/spec/cucumber/core/event_bus_spec.rb b/spec/cucumber/core/event_bus_spec.rb
index aafc40cf..532124fe 100644
--- a/spec/cucumber/core/event_bus_spec.rb
+++ b/spec/cucumber/core/event_bus_spec.rb
@@ -22,6 +22,7 @@ class TestEvent < Core::Event.new(:some_attribute)
           called = false
           event_bus.on(:test_event) { called = true }
           event_bus.test_event
+
           expect(called).to be true
         end
 
@@ -29,15 +30,13 @@ class TestEvent < Core::Event.new(:some_attribute)
           called = false
           event_bus.on(:test_event) { called = true }
           event_bus.broadcast(Events::TestEvent.new(:some_attribute))
+
           expect(called).to be true
         end
 
         it 'calls a subscriber for an event, passing details of the event' do
           received_payload = nil
-          event_bus.on :test_event do |event|
-            received_payload = event
-          end
-
+          event_bus.on(:test_event) { |event| received_payload = event }
           event_bus.test_event :some_attribute
 
           expect(received_payload.some_attribute).to eq(:some_attribute)
diff --git a/spec/cucumber/core/test/locations_filter_spec.rb b/spec/cucumber/core/test/locations_filter_spec.rb
index e03777f4..18828b81 100644
--- a/spec/cucumber/core/test/locations_filter_spec.rb
+++ b/spec/cucumber/core/test/locations_filter_spec.rb
@@ -254,14 +254,11 @@ def test_case_named(name)
       end
 
       context 'with a docstring' do
-        let(:test_case) do
-          test_cases.detect { |tc| tc.name == 'with docstring' }
-        end
-
         it 'matches a location at the start the docstring' do
           location = Cucumber::Core::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])
         end
 
@@ -269,6 +266,7 @@ def test_case_named(name)
           location = Cucumber::Core::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])
         end
 
@@ -276,6 +274,7 @@ def test_case_named(name)
           location = Cucumber::Core::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])
         end
       end
diff --git a/spec/cucumber/core/test/runner_spec.rb b/spec/cucumber/core/test/runner_spec.rb
index 8b2de694..93db9a17 100644
--- a/spec/cucumber/core/test/runner_spec.rb
+++ b/spec/cucumber/core/test/runner_spec.rb
@@ -240,9 +240,18 @@
   end
 
   context 'with around hooks' do
+    let(:passing_around_hook) do
+      Cucumber::Core::Test::AroundHook.new { |block| block.call }
+    end
+    let(:failing_around_hook) do
+      Cucumber::Core::Test::AroundHook.new do |block|
+        block.call
+        raise StandardError
+      end
+    end
+
     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(double, double, [passing], double, double, double, double, [around_hook])
+      test_case = Cucumber::Core::Test::Case.new(double, double, [passing], double, double, double, double, [passing_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
@@ -260,34 +269,25 @@
     end
 
     it 'gets a failed result if the Around hook fails after the test case is run' do
-      around_hook = Cucumber::Core::Test::AroundHook.new do |block|
-        block.call
-        raise StandardError
-      end
-      test_case = Cucumber::Core::Test::Case.new(double, double, [passing], double, double, double, double, [around_hook])
+      test_case = Cucumber::Core::Test::Case.new(double, double, [passing], double, double, double, double, [failing_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 be_a StandardError
+        expect(result.exception).to be_a(StandardError)
       end
       test_case.describe_to runner
     end
 
     it 'fails when a step fails if the around hook works' do
-      around_hook = Cucumber::Core::Test::AroundHook.new { |block| block.call }
-      test_case = Cucumber::Core::Test::Case.new(double, double, [failing], double, double, double, double, [around_hook])
+      test_case = Cucumber::Core::Test::Case.new(double, double, [failing], double, double, double, double, [passing_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 be_a StandardError
+        expect(result.exception).to be_a(StandardError)
       end
       test_case.describe_to runner
     end
 
     it 'sends after_test_step for a step interrupted by (a timeout in) the around hook' do
-      around_hook = Cucumber::Core::Test::AroundHook.new do |block|
-        block.call
-        raise StandardError
-      end
-      test_case = Cucumber::Core::Test::Case.new(double, double, [], double, double, double, double, [around_hook])
+      test_case = Cucumber::Core::Test::Case.new(double, double, [], double, double, double, double, [failing_around_hook])
       allow(runner).to receive(:running_test_step).and_return(passing)
       expect(event_bus).to receive(:test_step_finished).with(passing, anything) do |_reported_test_case, result|
         expect(result).to be_failed