From b13123572be8b5fee659afbe010fdf5d555eea42 Mon Sep 17 00:00:00 2001 From: Luke Hill Date: Fri, 22 May 2026 08:50:47 +0100 Subject: [PATCH 01/12] Handle media type json a bit friendlier --- lib/cucumber/formatter/message_builder.rb | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/cucumber/formatter/message_builder.rb b/lib/cucumber/formatter/message_builder.rb index 86a0e9204..a5ef0fdd9 100644 --- a/lib/cucumber/formatter/message_builder.rb +++ b/lib/cucumber/formatter/message_builder.rb @@ -1,8 +1,9 @@ # frozen_string_literal: true require 'base64' -require 'cucumber/formatter/backtrace_filter' +require 'json' +require 'cucumber/formatter/backtrace_filter' require 'cucumber/query' module Cucumber @@ -67,6 +68,9 @@ def attach(src, media_type, filename) if media_type&.start_with?('text/') attachment_data[:content_encoding] = Cucumber::Messages::AttachmentContentEncoding::IDENTITY attachment_data[:body] = src + elsif media_type.end_with('json') + attachment_data[:content_type] = Cucumber::Messages::AttachmentContentEncoding::IDENTITY + attachment_data[:body] = src.is_a?(Hash) ? src.to_json : src else body = src.respond_to?(:read) ? src.read : src attachment_data[:content_encoding] = Cucumber::Messages::AttachmentContentEncoding::BASE64 From ae22a8ab159c96d23ea851404884e3de9f17df4f Mon Sep 17 00:00:00 2001 From: Luke Hill <20105237+luke-hill@users.noreply.github.com> Date: Fri, 22 May 2026 09:14:04 +0100 Subject: [PATCH 02/12] Update message_builder.rb Fix boolean ref --- lib/cucumber/formatter/message_builder.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/cucumber/formatter/message_builder.rb b/lib/cucumber/formatter/message_builder.rb index a5ef0fdd9..4fa690813 100644 --- a/lib/cucumber/formatter/message_builder.rb +++ b/lib/cucumber/formatter/message_builder.rb @@ -68,7 +68,7 @@ def attach(src, media_type, filename) if media_type&.start_with?('text/') attachment_data[:content_encoding] = Cucumber::Messages::AttachmentContentEncoding::IDENTITY attachment_data[:body] = src - elsif media_type.end_with('json') + elsif media_type.end_with?('json') attachment_data[:content_type] = Cucumber::Messages::AttachmentContentEncoding::IDENTITY attachment_data[:body] = src.is_a?(Hash) ? src.to_json : src else From 9ea31c3b2fd9588dc265f45b73e440bcdd7ea34b Mon Sep 17 00:00:00 2001 From: Luke Hill Date: Fri, 22 May 2026 09:57:51 +0100 Subject: [PATCH 03/12] Fix invalid key --- lib/cucumber/formatter/message_builder.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/cucumber/formatter/message_builder.rb b/lib/cucumber/formatter/message_builder.rb index 4fa690813..d92c26765 100644 --- a/lib/cucumber/formatter/message_builder.rb +++ b/lib/cucumber/formatter/message_builder.rb @@ -69,7 +69,7 @@ def attach(src, media_type, filename) attachment_data[:content_encoding] = Cucumber::Messages::AttachmentContentEncoding::IDENTITY attachment_data[:body] = src elsif media_type.end_with?('json') - attachment_data[:content_type] = Cucumber::Messages::AttachmentContentEncoding::IDENTITY + attachment_data[:content_encoding] = Cucumber::Messages::AttachmentContentEncoding::IDENTITY attachment_data[:body] = src.is_a?(Hash) ? src.to_json : src else body = src.respond_to?(:read) ? src.read : src From b468936a4889bb80a6cba8ef2965686a96bb9678 Mon Sep 17 00:00:00 2001 From: Luke Hill Date: Fri, 22 May 2026 16:07:31 +0100 Subject: [PATCH 04/12] Invert the logic and instead only stream readable entities otherwise forcibly co-erce into stringified format --- lib/cucumber/formatter/message_builder.rb | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/lib/cucumber/formatter/message_builder.rb b/lib/cucumber/formatter/message_builder.rb index d92c26765..badd67125 100644 --- a/lib/cucumber/formatter/message_builder.rb +++ b/lib/cucumber/formatter/message_builder.rb @@ -65,16 +65,12 @@ def attach(src, media_type, filename) timestamp: time_to_timestamp(Time.now) } - if media_type&.start_with?('text/') - attachment_data[:content_encoding] = Cucumber::Messages::AttachmentContentEncoding::IDENTITY - attachment_data[:body] = src - elsif media_type.end_with?('json') + if src.respond_to(:read) + attachment_data[:content_encoding] = Cucumber::Messages::AttachmentContentEncoding::BASE64 + attachment_data[:body] = Base64.strict_encode64(src.read) + else attachment_data[:content_encoding] = Cucumber::Messages::AttachmentContentEncoding::IDENTITY attachment_data[:body] = src.is_a?(Hash) ? src.to_json : src - else - body = src.respond_to?(:read) ? src.read : src - attachment_data[:content_encoding] = Cucumber::Messages::AttachmentContentEncoding::BASE64 - attachment_data[:body] = Base64.strict_encode64(body) end message = Cucumber::Messages::Envelope.new(attachment: Cucumber::Messages::Attachment.new(**attachment_data)) From d5986d1bf12b9e60dd334950ca20175bacc7f401 Mon Sep 17 00:00:00 2001 From: Luke Hill Date: Fri, 22 May 2026 16:09:30 +0100 Subject: [PATCH 05/12] Update changelog --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 66d0416ae..134afbde6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,11 @@ 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)) +- Simplify attachment handling in the `MessageBuilder` and `#attach` method + +([#1853]( +### Fixed +- When someone `#attach`s a hashified output (Instead of JSON), call `#to_json` before attaching as a stringified JSON response ## [11.0.0] - 2026-04-14 ### Added From 21bbcae0e650132b709543d4bb7cb051aa538cd2 Mon Sep 17 00:00:00 2001 From: Luke Hill Date: Fri, 22 May 2026 16:23:22 +0100 Subject: [PATCH 06/12] Fix method signature --- lib/cucumber/formatter/message_builder.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/cucumber/formatter/message_builder.rb b/lib/cucumber/formatter/message_builder.rb index badd67125..a3a775ab9 100644 --- a/lib/cucumber/formatter/message_builder.rb +++ b/lib/cucumber/formatter/message_builder.rb @@ -65,7 +65,7 @@ def attach(src, media_type, filename) timestamp: time_to_timestamp(Time.now) } - if src.respond_to(:read) + if src.respond_to?(:read) attachment_data[:content_encoding] = Cucumber::Messages::AttachmentContentEncoding::BASE64 attachment_data[:body] = Base64.strict_encode64(src.read) else From 139f9da0c4859c757b70686ddc581c09ac058f08 Mon Sep 17 00:00:00 2001 From: Luke Hill Date: Fri, 22 May 2026 22:16:51 +0100 Subject: [PATCH 07/12] Fix attach and streamed file logic for formatters plus message output --- features/docs/fixtures/cucumber.jpeg | Bin 0 -> 1444 bytes .../writing_support_code/attachments.feature | 17 +++++++---------- .../lib/step_definitions/cucumber_steps.rb | 2 +- .../lib/step_definitions/filesystem_steps.rb | 4 ++++ features/lib/step_definitions/message_steps.rb | 6 ++++++ features/lib/support/filesystem.rb | 5 +++++ lib/cucumber/formatter/console.rb | 2 +- lib/cucumber/formatter/json.rb | 2 +- lib/cucumber/formatter/message_builder.rb | 6 +++--- lib/cucumber/formatter/pretty.rb | 2 +- lib/cucumber/glue/proto_world.rb | 12 +++++++----- lib/cucumber/runtime/user_interface.rb | 4 ++-- 12 files changed, 38 insertions(+), 24 deletions(-) create mode 100644 features/docs/fixtures/cucumber.jpeg diff --git a/features/docs/fixtures/cucumber.jpeg b/features/docs/fixtures/cucumber.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..e833d6c77d33e620ae49d01c6341ad7e5543bbd1 GIT binary patch literal 1444 zcmex=ma3|jiIItm zOAI4iKMQ#V{6EAX$ibk;pvlar#K0uT$SlbC{|JK&&=V}oKwrQC2Ma43I|Cy#)Bjrx z93W3JFj%5R(9qV{Ntcq}Ky~tP0uLAPSj53Js=3YaT3(saOkCY9;`5A|n$EGYdN> z$V3JPCPrqUBN+tQg%k~il>!AEoq|My6AOzQCy9zF8yTB~CKo{+&uGtZX0w)!P@n75 z+pl=GZ!o)2TKe4kXH-t@v#4EXEL!JE?zOu6r)a^it+7!xlS1t8{b#8CcjV%O8K0Lw zw!5=@{{4E%lYXmb|9XFR)y|pcq!QHyPd&4_T>Dx#CekoQenQBu?LV{I?)a=Z|5`qv zFCb50m1$zkt9Lo_J0{jC=okGqK3#m~$Ha_v^0sbzj}ElmKM-8K@73}%+E1_Sj1$h= za&Sv<=wdZVW0znRzq!_L_bo^lOYMz6H&?{%)XoFh(vNfajh)BRw*tXt#N5h?h}`dZpTv+M2|iZeX+YndxfsS7m0HMKkjMk z3R-;5(6ii>*OK{q>6sTyS60rQly}eE#LW&5BG%-(C1!D;fUT^{(mfa_yh< z{#u>M^5=OJ`#1Z*!uSb$%D$efQTh0vq2T`C;?gU-q$)Q@ZJHi;`qC^B2V>vgrM{Op zz2Y^CHRFrdI~bc)vyq`s=08J-S2f$|3$=&W)J@FMUY+(-GI%Z`A@L?@)(B$FN=Q(t=#l1^;xf6 ztKqtSx0-4T?$s+Kr!hS|dgkT)$(;KdOXF_sl{kL&qTE`s1rg3$Iy;OF^4Vh9&`X%c- z?@Ir1`muNQ?HGfD#&eeSnRs0PY8_Wf0x zYr%isu|McfyL=m%q*VpMX&9LEAPE|n37MD~SOi&xgcSot42=?%9Sb43h7p*8_w1>- z6Mg&7uRHgQ{q+{+?($T>Bl_*Z+yh@{H3b$5c{NME=w}I8|8lFOYSF`(6w%|A@+BXl zmusIkS2)Mu_D0Bf{>CWJM_W~opFJsmFzB$zE1{4Rf8~=jG8xtK!v7@pU6p$h?!dst zAi;cKJ=ASXzzoOCisUvHRt7dfAw$K$LSa!QBS)~?m_cs48@=pNOiq~YFVP2{3ZGA2 z+`D6=>s#ATRX)mE@f(tl=gZwXkQQv**LRUIg?&x;o5PWIH+oN(d8?&{>zqn{JdOYJ zpDR~h9MUx8cipWf_R8znWtJ&uK2N+Xmx{{Kw?qP#Uw literal 0 HcmV?d00001 diff --git a/features/docs/writing_support_code/attachments.feature b/features/docs/writing_support_code/attachments.feature index d868abc19..88d71cbfd 100644 --- a/features/docs/writing_support_code/attachments.feature +++ b/features/docs/writing_support_code/attachments.feature @@ -20,18 +20,15 @@ Feature: Attachments Scenario: Given I attach a screenshot without a media type """ - And a file named "features/screenshot.png" with: - """ - foo - """ + And an image named "cucumber.jpeg" And a file named "features/step_definitions/attaching_screenshot_steps.rb" with: """ Given('I attach a screenshot with a media type') do - attach('features/screenshot.png', 'image/png') + attach('features/cucumber.jpeg', 'image/jpeg') end Given('I attach a screenshot without a media type') do - attach('features/screenshot.png') + attach('features/cucumber.jpeg') end """ @@ -39,15 +36,15 @@ Feature: Attachments When I run `cucumber --format message features/attaching_screenshot_with_mediatype.feature` Then output should be valid NDJSON And the output should contain NDJSON with key "attachment" - And the output should contain NDJSON "attachment" message with key "body" and value "Zm9v" - And the output should contain NDJSON "attachment" message with key "mediaType" and value "image/png" + And the output should contain NDJSON "attachment" message with key "body" and an encoded value + And the output should contain NDJSON "attachment" message with key "mediaType" and value "image/jpeg" Scenario: Media type is inferred from the given file When I run `cucumber --format message features/attaching_screenshot_without_mediatype.feature` Then output should be valid NDJSON And the output should contain NDJSON with key "attachment" - And the output should contain NDJSON "attachment" message with key "body" and value "Zm9v" - And the output should contain NDJSON "attachment" message with key "mediaType" and value "image/png" + And the output should contain NDJSON "attachment" message with key "body" and an encoded value + And the output should contain NDJSON "attachment" message with key "mediaType" and value "image/jpeg" Scenario: With json formatter, files can be attached given their path When I run `cucumber --format json features/attaching_screenshot_with_mediatype.feature` diff --git a/features/lib/step_definitions/cucumber_steps.rb b/features/lib/step_definitions/cucumber_steps.rb index dc7c830d3..8574e2f1d 100644 --- a/features/lib/step_definitions/cucumber_steps.rb +++ b/features/lib/step_definitions/cucumber_steps.rb @@ -15,7 +15,7 @@ ' @io = config.out_stream', ' end', '', - ' def attach(src, media_type, _filename)', + ' def attach(src, media_type, _filename, streamed_file = false)', ' @io.puts(src)', ' end', 'end' diff --git a/features/lib/step_definitions/filesystem_steps.rb b/features/lib/step_definitions/filesystem_steps.rb index ce719214d..a85cb8b82 100644 --- a/features/lib/step_definitions/filesystem_steps.rb +++ b/features/lib/step_definitions/filesystem_steps.rb @@ -8,6 +8,10 @@ write_file(path, content) end +Given('an image named {string}') do |name| + copy_image_named(name) +end + Given('an empty file named {string}') do |path| write_file(path, '') end diff --git a/features/lib/step_definitions/message_steps.rb b/features/lib/step_definitions/message_steps.rb index 4996bf88b..40dec1927 100644 --- a/features/lib/step_definitions/message_steps.rb +++ b/features/lib/step_definitions/message_steps.rb @@ -18,6 +18,12 @@ expect(command_line.stdout).to match(/"#{key}": ?"#{value}"/) end +Then('the output should contain NDJSON {string} message with key {string} and an encoded value') do |message_name, key| + message_contents = command_line.stdout(format: :messages).detect { |msg| msg.keys == [message_name] }[message_name] + + expect(message_contents[key].length).to be > 50 +end + Then('the output should contain NDJSON {string} message with key {string} and value {string}') do |message_name, key, value| message_contents = command_line.stdout(format: :messages).detect { |msg| msg.keys == [message_name] }[message_name] diff --git a/features/lib/support/filesystem.rb b/features/lib/support/filesystem.rb index fa99b5a83..d12d13d80 100644 --- a/features/lib/support/filesystem.rb +++ b/features/lib/support/filesystem.rb @@ -4,3 +4,8 @@ def write_file(path, content) FileUtils.mkdir_p(File.dirname(path)) File.open(path, 'w') { |file| file.write(content) } end + +def copy_image_named(name) + fixture_dir = File.expand_path('../../features/docs/fixtures') + FileUtils.cp("#{fixture_dir}/#{name}", "#{Dir.pwd}/features/#{name}") +end diff --git a/lib/cucumber/formatter/console.rb b/lib/cucumber/formatter/console.rb index dfe7688ae..3704537d5 100644 --- a/lib/cucumber/formatter/console.rb +++ b/lib/cucumber/formatter/console.rb @@ -169,7 +169,7 @@ def do_print_passing_wip(passed_messages) end end - def attach(src, media_type, filename) + def attach(src, media_type, filename, streamed_file = false) return unless media_type == 'text/x.cucumber.log+plain' return unless @io diff --git a/lib/cucumber/formatter/json.rb b/lib/cucumber/formatter/json.rb index 8fc8471a4..401d762cb 100644 --- a/lib/cucumber/formatter/json.rb +++ b/lib/cucumber/formatter/json.rb @@ -85,7 +85,7 @@ def on_test_run_finished(_event) @io.write(JSON.pretty_generate(@feature_hashes)) end - def attach(src, mime_type, _filename) + def attach(src, mime_type, _filename, _streamed_file) if mime_type == 'text/x.cucumber.log+plain' test_step_output << src return diff --git a/lib/cucumber/formatter/message_builder.rb b/lib/cucumber/formatter/message_builder.rb index a3a775ab9..92f5335a8 100644 --- a/lib/cucumber/formatter/message_builder.rb +++ b/lib/cucumber/formatter/message_builder.rb @@ -56,7 +56,7 @@ def initialize(config) config.on_event :undefined_parameter_type, &method(:on_undefined_parameter_type) end - def attach(src, media_type, filename) + def attach(src, media_type, filename, streamed_file) attachment_data = { test_step_id: @current_test_step_id, test_case_started_id: @current_test_case_started_id, @@ -65,9 +65,9 @@ def attach(src, media_type, filename) timestamp: time_to_timestamp(Time.now) } - if src.respond_to?(:read) + if streamed_file attachment_data[:content_encoding] = Cucumber::Messages::AttachmentContentEncoding::BASE64 - attachment_data[:body] = Base64.strict_encode64(src.read) + attachment_data[:body] = Base64.strict_encode64(src) else attachment_data[:content_encoding] = Cucumber::Messages::AttachmentContentEncoding::IDENTITY attachment_data[:body] = src.is_a?(Hash) ? src.to_json : src diff --git a/lib/cucumber/formatter/pretty.rb b/lib/cucumber/formatter/pretty.rb index 97ba8d796..7f4549703 100644 --- a/lib/cucumber/formatter/pretty.rb +++ b/lib/cucumber/formatter/pretty.rb @@ -140,7 +140,7 @@ def on_test_run_finished(_event) print_summary end - def attach(src, media_type, filename) + def attach(src, media_type, filename, _streamed_file) return unless media_type == 'text/x.cucumber.log+plain' if filename diff --git a/lib/cucumber/glue/proto_world.rb b/lib/cucumber/glue/proto_world.rb index 83452aa8d..0549a9735 100644 --- a/lib/cucumber/glue/proto_world.rb +++ b/lib/cucumber/glue/proto_world.rb @@ -89,14 +89,16 @@ def log(*messages) # @param filename [string] the name of the file you wish to specify. # This is only needed in situations where you want to rename a PDF download e.t.c. - In most situations # you should not need to pass a filename - def attach(file, media_type = nil, filename = nil) + def attach(file, media_type = nil, filename = nil, streamed_file = nil) if File.file?(file) media_type = MiniMime.lookup_by_filename(file)&.content_type if media_type.nil? file = File.read(file, mode: 'rb') + streamed_file = true end - super + # We pass in the concept of a streamed_file to ensure that the envelope encoding is correct + super(file, media_type, filename, streamed_file) rescue StandardError - super + super(file, media_type, filename, streamed_file) end # Mark the matched step as pending. @@ -153,8 +155,8 @@ def add_modules!(world_modules, namespaced_world_modules) runtime.ask(question, timeout_seconds) end - define_method(:attach) do |file, media_type, filename| - runtime.attach(file, media_type, filename) + define_method(:attach) do |file, media_type, filename, streamed_file = false| + runtime.attach(file, media_type, filename, streamed_file) end # Prints the list of modules that are included in the World diff --git a/lib/cucumber/runtime/user_interface.rb b/lib/cucumber/runtime/user_interface.rb index 433ef8273..1621d87f8 100644 --- a/lib/cucumber/runtime/user_interface.rb +++ b/lib/cucumber/runtime/user_interface.rb @@ -41,8 +41,8 @@ def ask(question, timeout_seconds) # be a path to a file, or if it's an image it may also be a Base64 encoded image. # The embedded data may or may not be ignored, depending on what kind of formatter(s) are active. # - def attach(src, media_type, filename) - @visitor.attach(src, media_type, filename) + def attach(src, media_type, filename, streamed_file) + @visitor.attach(src, media_type, filename, streamed_file) end private From af3f0fb1034d1d746d38741ba09ab8b7da370eb9 Mon Sep 17 00:00:00 2001 From: Luke Hill Date: Fri, 22 May 2026 22:19:03 +0100 Subject: [PATCH 08/12] Fix step def specs --- spec/cucumber/glue/step_definition_spec.rb | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/spec/cucumber/glue/step_definition_spec.rb b/spec/cucumber/glue/step_definition_spec.rb index c377d7816..89514268c 100644 --- a/spec/cucumber/glue/step_definition_spec.rb +++ b/spec/cucumber/glue/step_definition_spec.rb @@ -63,7 +63,7 @@ def step_match(text) it 'calls a method on the world when specified with a symbol' do expect(registry.current_world).to receive(:with_symbol) - dsl.Given(/With symbol/, :with_symbol) + dsl.Given('With symbol', :with_symbol) run_step 'With symbol' end @@ -71,7 +71,7 @@ def step_match(text) it 'calls a method on a specified object' do allow(registry.current_world).to receive(:target) { target } - dsl.Given(/With symbol on block/, :with_symbol, on: -> { target }) + dsl.Given('With symbol on block', :with_symbol, on: -> { target }) expect(target).to receive(:with_symbol) @@ -81,7 +81,7 @@ def step_match(text) it 'calls a method on a specified world attribute' do allow(registry.current_world).to receive(:target) { target } - dsl.Given(/With symbol on symbol/, :with_symbol, on: :target) + dsl.Given('With symbol on symbol', :with_symbol, on: :target) expect(target).to receive(:with_symbol) @@ -102,17 +102,15 @@ def step_match(text) end it 'raises UndefinedDynamicStep when an undefined step is parsed dynamically' do - dsl.Given(/Outside/) do - steps %( - Given Inside - ) + dsl.Given('Outside') do + steps %(Given Inside) end expect { run_step 'Outside' }.to raise_error(Cucumber::UndefinedDynamicStep) end it 'raises UndefinedDynamicStep when an undefined step with doc string is parsed dynamically' do - dsl.Given(/Outside/) do + dsl.Given('Outside') do steps %( Given Inside """ @@ -125,7 +123,7 @@ def step_match(text) end it 'raises UndefinedDynamicStep when an undefined step with data table is parsed dynamically' do - dsl.Given(/Outside/) do + dsl.Given('Outside') do steps %( Given Inside | a | @@ -193,14 +191,14 @@ def step_match(text) describe '#log' do it 'calls "attach" with the correct media type' do - expect(user_interface).to receive(:attach).with('wasup', 'text/x.cucumber.log+plain', nil) + expect(user_interface).to receive(:attach).with('wasup', 'text/x.cucumber.log+plain', nil, nil) dsl.Given('Loud') { log 'wasup' } run_step 'Loud' end it 'calls `to_s` if the message is not a String' do - expect(user_interface).to receive(:attach).with('["Not", 1, "string"]', 'text/x.cucumber.log+plain', nil) + expect(user_interface).to receive(:attach).with('["Not", 1, "string"]', 'text/x.cucumber.log+plain', nil, nil) dsl.Given('Loud') { log ['Not', 1, 'string'] } run_step 'Loud' From 797e043e028bab4041419e2ebf11e99b57452035 Mon Sep 17 00:00:00 2001 From: Luke Hill Date: Fri, 22 May 2026 22:22:28 +0100 Subject: [PATCH 09/12] Fix console formatting --- lib/cucumber/formatter/console.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/cucumber/formatter/console.rb b/lib/cucumber/formatter/console.rb index 3704537d5..194698f56 100644 --- a/lib/cucumber/formatter/console.rb +++ b/lib/cucumber/formatter/console.rb @@ -169,7 +169,7 @@ def do_print_passing_wip(passed_messages) end end - def attach(src, media_type, filename, streamed_file = false) + def attach(src, media_type, filename, _streamed_file) return unless media_type == 'text/x.cucumber.log+plain' return unless @io From baa670d3cd05922d06c2cd4fb902e2385d2110e6 Mon Sep 17 00:00:00 2001 From: Luke Hill Date: Fri, 22 May 2026 22:26:21 +0100 Subject: [PATCH 10/12] Fix json formatter test --- features/docs/writing_support_code/attachments.feature | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/features/docs/writing_support_code/attachments.feature b/features/docs/writing_support_code/attachments.feature index 88d71cbfd..afe39eae8 100644 --- a/features/docs/writing_support_code/attachments.feature +++ b/features/docs/writing_support_code/attachments.feature @@ -49,5 +49,5 @@ Feature: Attachments Scenario: With json formatter, files can be attached given their path When I run `cucumber --format json features/attaching_screenshot_with_mediatype.feature` Then the output should contain "embeddings\":" - And the output should contain "\"mime_type\": \"image/png\"," - And the output should contain "\"data\": \"Zm9v\"" + And the output should contain "\"mime_type\": \"image/jpeg\"," + And the output should contain "\"data\":" From 68f0cb0a2ba6f1cf92f33268308bc88c64e9b244 Mon Sep 17 00:00:00 2001 From: Luke Hill Date: Fri, 22 May 2026 22:30:20 +0100 Subject: [PATCH 11/12] Final tidy for defaults --- features/lib/step_definitions/cucumber_steps.rb | 2 +- lib/cucumber/glue/proto_world.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/features/lib/step_definitions/cucumber_steps.rb b/features/lib/step_definitions/cucumber_steps.rb index 8574e2f1d..9b33e62a3 100644 --- a/features/lib/step_definitions/cucumber_steps.rb +++ b/features/lib/step_definitions/cucumber_steps.rb @@ -15,7 +15,7 @@ ' @io = config.out_stream', ' end', '', - ' def attach(src, media_type, _filename, streamed_file = false)', + ' def attach(src, media_type, _filename, _streamed_file)', ' @io.puts(src)', ' end', 'end' diff --git a/lib/cucumber/glue/proto_world.rb b/lib/cucumber/glue/proto_world.rb index 0549a9735..a070ce50e 100644 --- a/lib/cucumber/glue/proto_world.rb +++ b/lib/cucumber/glue/proto_world.rb @@ -95,7 +95,7 @@ def attach(file, media_type = nil, filename = nil, streamed_file = nil) file = File.read(file, mode: 'rb') streamed_file = true end - # We pass in the concept of a streamed_file to ensure that the envelope encoding is correct + # We pass in the concept of whether the file is streamed to ensure that the envelope encoding is correct super(file, media_type, filename, streamed_file) rescue StandardError super(file, media_type, filename, streamed_file) From 94dfff81e3c09f30d665924552ba6946cc2d2669 Mon Sep 17 00:00:00 2001 From: Luke Hill Date: Tue, 26 May 2026 12:42:41 +0100 Subject: [PATCH 12/12] Ensure original first port-of-call cannot pass in streamed_file but all subsequent ones do --- lib/cucumber/glue/proto_world.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/cucumber/glue/proto_world.rb b/lib/cucumber/glue/proto_world.rb index a070ce50e..a797d34b8 100644 --- a/lib/cucumber/glue/proto_world.rb +++ b/lib/cucumber/glue/proto_world.rb @@ -89,7 +89,7 @@ def log(*messages) # @param filename [string] the name of the file you wish to specify. # This is only needed in situations where you want to rename a PDF download e.t.c. - In most situations # you should not need to pass a filename - def attach(file, media_type = nil, filename = nil, streamed_file = nil) + def attach(file, media_type = nil, filename = nil) if File.file?(file) media_type = MiniMime.lookup_by_filename(file)&.content_type if media_type.nil? file = File.read(file, mode: 'rb') @@ -155,7 +155,7 @@ def add_modules!(world_modules, namespaced_world_modules) runtime.ask(question, timeout_seconds) end - define_method(:attach) do |file, media_type, filename, streamed_file = false| + define_method(:attach) do |file, media_type, filename, streamed_file| runtime.attach(file, media_type, filename, streamed_file) end