From 6e3d5178dd7ece835544204b1f3ce34c2776b2b3 Mon Sep 17 00:00:00 2001 From: Luke Hill <20105237+luke-hill@users.noreply.github.com> Date: Tue, 7 Apr 2026 19:21:12 +0100 Subject: [PATCH 1/3] V11/remove old guard rails (#1847) * Delete a portion of the RSpec double verification that is not auto enabled * Add in a bit of a split for some of the glue code --- lib/cucumber/glue/hook.rb | 1 - lib/cucumber/glue/multiple_world.rb | 26 +++++++++++++++++++ lib/cucumber/glue/nil_world.rb | 12 +++++++++ lib/cucumber/glue/registry_and_more.rb | 25 +++--------------- lib/cucumber/rspec/doubles.rb | 8 +----- spec/cucumber/glue/registry_and_more_spec.rb | 1 - .../rake/forked_cucumber_runner_spec.rb | 8 +++--- 7 files changed, 46 insertions(+), 35 deletions(-) create mode 100644 lib/cucumber/glue/multiple_world.rb create mode 100644 lib/cucumber/glue/nil_world.rb diff --git a/lib/cucumber/glue/hook.rb b/lib/cucumber/glue/hook.rb index bdbe4114d0..5f98bce5e6 100644 --- a/lib/cucumber/glue/hook.rb +++ b/lib/cucumber/glue/hook.rb @@ -4,7 +4,6 @@ module Cucumber module Glue - # TODO: Kill pointless wrapper for Before, After and AfterStep hooks with fire class Hook attr_reader :id, :tag_expressions, :location, :name diff --git a/lib/cucumber/glue/multiple_world.rb b/lib/cucumber/glue/multiple_world.rb new file mode 100644 index 0000000000..2a09da6ad5 --- /dev/null +++ b/lib/cucumber/glue/multiple_world.rb @@ -0,0 +1,26 @@ +# frozen_string_literal: true + +module Cucumber + module Glue + # Raised if there are 2 or more World blocks. + class MultipleWorld < StandardError + def initialize(first_proc, second_proc) + super(error_message(first_proc, second_proc)) + end + + def error_message(first_proc, second_proc) + <<~MESSAGE + You can only pass a proc to #World once, but it's happening + in 2 places: + #{Glue.backtrace_line(first_proc, 'World')} + #{Glue.backtrace_line(second_proc, 'World')} + Use Ruby modules instead to extend your worlds. See the Cucumber::Glue::Dsl#World RDoc + or http://wiki.github.com/cucumber/cucumber/a-whole-new-world. + + + MESSAGE + end + end + + end +end diff --git a/lib/cucumber/glue/nil_world.rb b/lib/cucumber/glue/nil_world.rb new file mode 100644 index 0000000000..2512e482ce --- /dev/null +++ b/lib/cucumber/glue/nil_world.rb @@ -0,0 +1,12 @@ +# frozen_string_literal: true + +module Cucumber + module Glue + # Raised if a World block returns Nil. + class NilWorld < StandardError + def initialize + super('World procs should never return nil') + end + end + end +end diff --git a/lib/cucumber/glue/registry_and_more.rb b/lib/cucumber/glue/registry_and_more.rb index 3920262f12..d1654995c4 100644 --- a/lib/cucumber/glue/registry_and_more.rb +++ b/lib/cucumber/glue/registry_and_more.rb @@ -15,6 +15,9 @@ require 'cucumber/step_match' require 'cucumber/events/step_definition_registered' +require_relative 'multiple_world' +require_relative 'nil_world' + module Cucumber module Glue def self.backtrace_line(proc, name) @@ -22,28 +25,6 @@ def self.backtrace_line(proc, name) "#{location}:in `#{name}'" end - # Raised if a World block returns Nil. - class NilWorld < StandardError - def initialize - super('World procs should never return nil') - end - end - - # Raised if there are 2 or more World blocks. - class MultipleWorld < StandardError - def initialize(first_proc, second_proc) - # TODO: [LH] - Just use a heredoc here to fix this up - message = String.new - message << "You can only pass a proc to #World once, but it's happening\n" - message << "in 2 places:\n\n" - message << Glue.backtrace_line(first_proc, 'World') << "\n" - message << Glue.backtrace_line(second_proc, 'World') << "\n\n" - message << "Use Ruby modules instead to extend your worlds. See the Cucumber::Glue::Dsl#World RDoc\n" - message << "or http://wiki.github.com/cucumber/cucumber/a-whole-new-world.\n\n" - super(message) - end - end - # TODO: This class has too many responsibilities, split off class RegistryAndMore attr_reader :current_world, :step_definitions diff --git a/lib/cucumber/rspec/doubles.rb b/lib/cucumber/rspec/doubles.rb index b7d872f9eb..9ce7d01b43 100644 --- a/lib/cucumber/rspec/doubles.rb +++ b/lib/cucumber/rspec/doubles.rb @@ -5,15 +5,9 @@ World(RSpec::Mocks::ExampleMethods) Before do - if RSpec::Mocks::Version::STRING >= '2.9.9' - RSpec::Mocks.setup - else - RSpec::Mocks.setup(self) - end + RSpec::Mocks.setup(self) end After do - RSpec::Mocks.verify -ensure RSpec::Mocks.teardown end diff --git a/spec/cucumber/glue/registry_and_more_spec.rb b/spec/cucumber/glue/registry_and_more_spec.rb index a7fbb3fc71..f26bbf9fe0 100644 --- a/spec/cucumber/glue/registry_and_more_spec.rb +++ b/spec/cucumber/glue/registry_and_more_spec.rb @@ -1,6 +1,5 @@ # frozen_string_literal: true -require 'spec_helper' require 'cucumber/glue/registry_and_more' require 'support/fake_objects' diff --git a/spec/cucumber/rake/forked_cucumber_runner_spec.rb b/spec/cucumber/rake/forked_cucumber_runner_spec.rb index 417efc8526..31b6a2f09c 100644 --- a/spec/cucumber/rake/forked_cucumber_runner_spec.rb +++ b/spec/cucumber/rake/forked_cucumber_runner_spec.rb @@ -11,10 +11,10 @@ let(:feature_files) { ['./some.feature'] } context 'when running with bundler' do - let(:bundler) { true } - subject { described_class.new(libs, binary, cucumber_opts, bundler, feature_files) } + let(:bundler) { true } + it 'does use bundler if bundler is set to true' do expect(subject.use_bundler).to be true end @@ -32,10 +32,10 @@ end context 'when running without bundler' do - let(:bundler) { false } - subject { described_class.new(libs, binary, cucumber_opts, bundler, feature_files) } + let(:bundler) { false } + it 'does not use bundler if bundler is set to false' do expect(subject.use_bundler).to be false end From dc7e8b067589683410e94295904b7fea2d04b082 Mon Sep 17 00:00:00 2001 From: Luke Hill <20105237+luke-hill@users.noreply.github.com> Date: Fri, 15 May 2026 20:55:51 +0100 Subject: [PATCH 2/3] Refactor/remove ruby21 patch (#1873) * Remove ruby 2.1 patch * Moved top level errors out of runtime to bespoke errors file * Re-order errors according to names * Re-order requires and pivot to more optimal require relatives * Tidy up of rubocop autogen file --- .rubocop_todo.yml | 47 +++++++++++++++-------------- lib/cucumber/errors.rb | 66 +++++++++++++++++++++++++++-------------- lib/cucumber/runtime.rb | 59 +++++++++++------------------------- 3 files changed, 84 insertions(+), 88 deletions(-) diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index 218257cc13..5b370fae61 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -1,16 +1,15 @@ # This configuration was generated by # `rubocop --auto-gen-config` -# on 2026-03-17 23:57:03 UTC using RuboCop version 1.85.1. +# on 2026-05-15 19:52:05 UTC using RuboCop version 1.85.1. # The point is for the user to remove these configuration records # one by one as the offenses are removed from the code base. # Note that changes in the inspected code, or installation of new # versions of RuboCop, may require this file to be generated again. -# TODO - [LH] -> Jan '25 (Updated deps and v10 prep) - 369 files inspected, 704 offenses detected, 112 offenses autocorrectable -# TODO - [LH] -> Mar '25 (v10 prep) - 370 files inspected, 721 offenses detected, 116 offenses autocorrectable # TODO - [LH] -> Dec '25 - 375 files inspected, 713 offenses detected, 109 offenses autocorrectable # TODO - [LH] -> Dec '25 (query prep) - 378 files inspected, 729 offenses detected, 109 offenses autocorrectable # TODO - [LH] -> Mar '26 (v11 prep) - 389 files inspected, 747 offenses detected, 108 offenses autocorrectable +# TODO - [LH] -> May '26 (v11.0.0) - 391 files inspected, 740 offenses detected, 105 offenses autocorrectable # Offense count: 1 # This cop supports safe autocorrection (--autocorrect). @@ -19,6 +18,20 @@ Layout/EmptyLineBetweenDefs: Exclude: - 'lib/cucumber/glue/snippet.rb' +# Offense count: 1 +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: EnforcedStyle. +# SupportedStyles: empty_lines, empty_lines_except_namespace, empty_lines_special, no_empty_lines +Layout/EmptyLinesAroundModuleBody: + Exclude: + - 'lib/cucumber/glue/multiple_world.rb' + +# Offense count: 1 +# This cop supports safe autocorrection (--autocorrect). +Layout/HeredocIndentation: + Exclude: + - 'lib/cucumber/glue/multiple_world.rb' + # Offense count: 2 Lint/IneffectiveAccessModifier: Exclude: @@ -39,12 +52,6 @@ Lint/UselessAccessModifier: Exclude: - 'lib/cucumber/formatter/curl_option_parser.rb' -# Offense count: 1 -# This cop supports safe autocorrection (--autocorrect). -Lint/UselessAssignment: - Exclude: - - 'compatibility/cck_spec.rb' - # Offense count: 2 # This cop supports unsafe autocorrection (--autocorrect-all). Lint/UselessMethodDefinition: @@ -65,7 +72,7 @@ Metrics/BlockLength: # Offense count: 14 # Configuration parameters: CountComments, CountAsOne. Metrics/ClassLength: - Max: 516 + Max: 500 # Offense count: 8 # Configuration parameters: AllowedMethods, AllowedPatterns. @@ -77,7 +84,7 @@ Metrics/CyclomaticComplexity: Metrics/MethodLength: Max: 65 -# Offense count: 12 +# Offense count: 11 # Configuration parameters: CountComments, CountAsOne. Metrics/ModuleLength: Max: 804 @@ -150,7 +157,7 @@ RSpec/EmptyExampleGroup: - 'spec/cucumber/filters/activate_steps_spec.rb' - 'spec/cucumber/running_test_case_spec.rb' -# Offense count: 70 +# Offense count: 69 # Configuration parameters: CountAsOne. RSpec/ExampleLength: Max: 58 @@ -187,7 +194,7 @@ RSpec/ExpectOutput: Exclude: - 'spec/cucumber/formatter/interceptor_spec.rb' -# Offense count: 58 +# Offense count: 54 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyle. # SupportedStyles: implicit, each, example @@ -211,12 +218,6 @@ RSpec/InstanceVariable: - 'spec/cucumber/formatter/query/hook_by_test_step_spec.rb' - 'spec/support/shared_context/http_server.rb' -# Offense count: 1 -# This cop supports safe autocorrection (--autocorrect). -RSpec/LeadingSubject: - Exclude: - - 'spec/cucumber/cli/main_spec.rb' - # Offense count: 2 # This cop supports safe autocorrection (--autocorrect). RSpec/MatchArray: @@ -238,7 +239,7 @@ RSpec/MissingExampleGroupArgument: - 'spec/cucumber/formatter/fail_fast_spec.rb' - 'spec/cucumber/formatter/rerun_spec.rb' -# Offense count: 58 +# Offense count: 57 RSpec/MultipleExpectations: Max: 3 @@ -261,7 +262,7 @@ RSpec/NamedSubject: - 'spec/cucumber/cli/main_spec.rb' - 'spec/cucumber/configuration_spec.rb' - 'spec/cucumber/formatter/url_reporter_spec.rb' - - 'spec/cucumber/rake/forked_spec.rb' + - 'spec/cucumber/rake/forked_cucumber_runner_spec.rb' - 'spec/cucumber/rake/task_spec.rb' - 'spec/cucumber/runtime/meta_message_builder_spec.rb' - 'spec/cucumber/runtime/support_code_spec.rb' @@ -287,7 +288,7 @@ RSpec/OverwritingSetup: Exclude: - 'spec/cucumber/runtime/support_code_spec.rb' -# Offense count: 10 +# Offense count: 11 # This cop supports unsafe autocorrection (--autocorrect-all). RSpec/ReceiveMessages: Exclude: @@ -329,7 +330,7 @@ RSpec/SubjectDeclaration: - 'spec/cucumber/runtime/after_hooks_spec.rb' - 'spec/cucumber/runtime/before_hooks_spec.rb' -# Offense count: 53 +# Offense count: 52 # Configuration parameters: IgnoreNameless, IgnoreSymbolicNames. RSpec/VerifiedDoubles: Exclude: diff --git a/lib/cucumber/errors.rb b/lib/cucumber/errors.rb index 9805539e84..97e3baa633 100644 --- a/lib/cucumber/errors.rb +++ b/lib/cucumber/errors.rb @@ -3,6 +3,49 @@ require 'cucumber/core/test/result' module Cucumber + # Raised when a step matches 2 or more StepDefinitions + class Ambiguous < StandardError + def initialize(step_name, step_definitions, used_guess) + # TODO: [LH] - Just use a heredoc here to fix this up + message = String.new + message << "Ambiguous match of \"#{step_name}\":\n\n" + message << step_definitions.map(&:backtrace_line).join("\n") + message << "\n\n" + message << "You can run again with --guess to make Cucumber be more smart about it\n" unless used_guess + super(message) + end + end + + class FeatureFolderNotFoundException < RuntimeError + def initialize(path) + @path = path + super + end + + def message + "No such file or directory - #{@path}" + end + end + + class FileNotFoundException < RuntimeError + attr_reader :path + + def initialize(original_exception, path) + @path = path + super(original_exception) + end + end + + # Raised when a StepDefinition's block invokes World#pending + class Pending < Core::Test::Result::Pending + end + + class TagExcess < StandardError + def initialize(messages) + super(messages.join("\n")) + end + end + # Raised when there is no matching StepDefinition for a step. class Undefined < Core::Test::Result::Undefined def self.from(result, step_name) @@ -27,27 +70,4 @@ def initialize(step_name) super %(Undefined dynamic step: "#{step_name}") end end - - # Raised when a StepDefinition's block invokes World#pending - class Pending < Core::Test::Result::Pending - end - - # Raised when a step matches 2 or more StepDefinitions - class Ambiguous < StandardError - def initialize(step_name, step_definitions, used_guess) - # TODO: [LH] - Just use a heredoc here to fix this up - message = String.new - message << "Ambiguous match of \"#{step_name}\":\n\n" - message << step_definitions.map(&:backtrace_line).join("\n") - message << "\n\n" - message << "You can run again with --guess to make Cucumber be more smart about it\n" unless used_guess - super(message) - end - end - - class TagExcess < StandardError - def initialize(messages) - super(messages.join("\n")) - end - end end diff --git a/lib/cucumber/runtime.rb b/lib/cucumber/runtime.rb index 3315fd171d..9a43289450 100644 --- a/lib/cucumber/runtime.rb +++ b/lib/cucumber/runtime.rb @@ -1,52 +1,27 @@ # frozen_string_literal: true require 'fileutils' -require 'cucumber/configuration' -require 'cucumber/load_path' -require 'cucumber/formatter/duration' -require 'cucumber/file_specs' -require 'cucumber/filters' -require 'cucumber/formatter/fanout' -require 'cucumber/gherkin/i18n' -require 'cucumber/glue/registry_wrapper' -require 'cucumber/step_match_search' -require 'cucumber/messages' -require 'cucumber/runtime/meta_message_builder' require 'sys/uname' -module Cucumber - module FixRuby21Bug9285 - def message - String(super).gsub('@ rb_sysopen ', '') - end - end - - class FileException < RuntimeError - attr_reader :path - - def initialize(original_exception, path) - @path = path - super(original_exception) - end - end - - class FileNotFoundException < FileException - end - - class FeatureFolderNotFoundException < RuntimeError - def initialize(path) - @path = path - super - end +require 'cucumber/core' +require 'cucumber/messages' - def message - "No such file or directory - #{@path}" - end - end +require_relative 'configuration' +require_relative 'errors' +require_relative 'file_specs' +require_relative 'filters' +require_relative 'load_path' +require_relative 'step_match_search' + +require_relative 'formatter/duration' +require_relative 'formatter/fanout' +require_relative 'gherkin/i18n' +require_relative 'glue/registry_wrapper' +require_relative 'runtime/meta_message_builder' +require_relative 'runtime/support_code' +require_relative 'runtime/user_interface' - require 'cucumber/core' - require 'cucumber/runtime/user_interface' - require 'cucumber/runtime/support_code' +module Cucumber class Runtime attr_reader :results, :support_code, :configuration From 37c6e500bbc94a6f19ebc3c62cfcbb98a6cf4e05 Mon Sep 17 00:00:00 2001 From: Luke Hill Date: Fri, 15 May 2026 20:59:41 +0100 Subject: [PATCH 3/3] Add changelog --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 66d0416aed..1b09725d8c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,12 @@ Please visit [cucumber/CONTRIBUTING.md](https://github.com/cucumber/cucumber/blo ### Changed - Heavy refactor to the internals for message building (Used in formatters - should be no noticeable change) ([#1853](https://github.com/cucumber/cucumber-ruby/pull/1853) [luke-hill](https://github.com/luke-hill)) +- Refactor to internal error logic (No user facing changes) + +### Removed +- Removed a bunch of RSpec support logic that was no longer used in the codebase (This includes some legacy pending +logic and some old rspec helper files) +- Removed handling of a Ruby 2.1 system error (Minimum Ruby is now 3.2) ## [11.0.0] - 2026-04-14 ### Added