diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 84fb8b029f..51c25fd788 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -17,7 +17,7 @@ jobs: fail-fast: false matrix: os: [ubuntu-latest, macos-latest, windows-latest] - ruby: ['3.0', '3.1', '3.2', '3.3'] + ruby: ['3.0', '3.1', '3.2', '3.3', '3.4'] include: - os: ubuntu-latest ruby: jruby-9.4 diff --git a/CHANGELOG.md b/CHANGELOG.md index 35263233d2..219c081cd1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,7 @@ that need to rely on procedural loading / reloading of files should use method i ### Fixed - Fixed an issue where a change to one example in compatibility testing wasn't fully adhered to ([luke-hill](https://github.com/luke-hill)) +- Fixed Ruby 3.4+ issue where error backtraces weren't being formatted. ([#1771](https://github.com/cucumber/cucumber-ruby/pull/1771) [orien](https://github.com/orien)) ### Removed - `StepDefinitionLight` associated methods. The class itself is present but deprecated diff --git a/features/docs/extending_cucumber/custom_formatter.feature b/features/docs/extending_cucumber/custom_formatter.feature index a5728db932..187996c9da 100644 --- a/features/docs/extending_cucumber/custom_formatter.feature +++ b/features/docs/extending_cucumber/custom_formatter.feature @@ -45,7 +45,7 @@ Feature: Custom Formatter def initialize(config, options) @io = config.out_stream config.on_event :test_run_finished do |event| - @io.print options.inspect + @io.print options.to_json end end end @@ -54,5 +54,5 @@ Feature: Custom Formatter When I run `cucumber features/f.feature --format MyCustom::Formatter,foo=bar,one=two --publish-quiet` Then it should pass with exactly: """ - {"foo"=>"bar", "one"=>"two"} + {"foo":"bar","one":"two"} """ diff --git a/lib/cucumber/formatter/backtrace_filter.rb b/lib/cucumber/formatter/backtrace_filter.rb index 82c0628f73..d35d2b014e 100644 --- a/lib/cucumber/formatter/backtrace_filter.rb +++ b/lib/cucumber/formatter/backtrace_filter.rb @@ -43,8 +43,10 @@ def exception if ::ENV['CUCUMBER_TRUNCATE_OUTPUT'] # Strip off file locations + regexp = RUBY_VERSION >= '3.4' ? /(.*):in '/ : /(.*):in `/ filtered = filtered.map do |line| - line =~ /(.*):in `/ ? Regexp.last_match(1) : line + match = regexp.match(line) + match ? match[1] : line end end diff --git a/lib/cucumber/glue/invoke_in_world.rb b/lib/cucumber/glue/invoke_in_world.rb index 6e957d1b88..534ae9d4d5 100644 --- a/lib/cucumber/glue/invoke_in_world.rb +++ b/lib/cucumber/glue/invoke_in_world.rb @@ -14,7 +14,10 @@ def self.replace_instance_exec_invocation_line!(backtrace, instance_exec_invocat return unless instance_exec_pos replacement_line = instance_exec_pos + INSTANCE_EXEC_OFFSET - backtrace[replacement_line].gsub!(/`.*'/, "`#{pseudo_method}'") if pseudo_method + if pseudo_method + pattern = RUBY_VERSION >= '3.4' ? /'.*'/ : /`.*'/ + backtrace[replacement_line].gsub!(pattern, "`#{pseudo_method}'") + end depth = backtrace.count { |line| line == instance_exec_invocation_line } end_pos = depth > 1 ? instance_exec_pos : -1 @@ -49,7 +52,13 @@ def self.cucumber_compatible_arity?(args, block) def self.cucumber_run_with_backtrace_filtering(pseudo_method) yield rescue Exception => e - instance_exec_invocation_line = "#{__FILE__}:#{__LINE__ - 2}:in `cucumber_run_with_backtrace_filtering'" + yield_line_number = __LINE__ - 2 + instance_exec_invocation_line = + if RUBY_VERSION >= '3.4' + "#{__FILE__}:#{yield_line_number}:in '#{name}.#{__method__}'" + else + "#{__FILE__}:#{yield_line_number}:in `#{__method__}'" + end replace_instance_exec_invocation_line!((e.backtrace || []), instance_exec_invocation_line, pseudo_method) raise e end diff --git a/spec/cucumber/glue/proto_world_spec.rb b/spec/cucumber/glue/proto_world_spec.rb index c9f37b0730..1138bb5376 100644 --- a/spec/cucumber/glue/proto_world_spec.rb +++ b/spec/cucumber/glue/proto_world_spec.rb @@ -68,36 +68,39 @@ module Glue define_steps do When('an object is logged') do - log(a: 1, b: 2, c: 3) + object = Object.new + def object.to_s + '' + end + log(object) end end - it 'attached the styring version on the object' do - expect(@out.string).to include '{:a=>1, :b=>2, :c=>3}' + it 'prints the stringified version of the object as a log message' do + expect(@out.string).to include('') end end describe 'when logging multiple items on one call' do define_feature <<-FEATURE - Feature: Banana party + Feature: Logging multiple entries - Scenario: Monkey eats banana - When monkey eats banana + Scenario: Logging multiple entries + When logging multiple entries FEATURE define_steps do - When('{word} {word} {word}') do |subject, verb, complement| - log "subject: #{subject}", "verb: #{verb}", "complement: #{complement}", subject: subject, verb: verb, complement: complement + When('logging multiple entries') do + log 'entry one', 'entry two', 'entry three' end end - it 'logs each parameter independently' do - expect(@out.string).to include [ - ' subject: monkey', - ' verb: eats', - ' complement: banana', - ' {:subject=>"monkey", :verb=>"eats", :complement=>"banana"}' - ].join("\n") + it 'logs each entry independently' do + expect(@out.string).to include([ + ' entry one', + ' entry two', + ' entry three' + ].join("\n")) end end