From 3f75f7879ca580666ad2857a09175c82358312cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Rasmusson?= Date: Mon, 11 May 2026 08:28:40 +0200 Subject: [PATCH 1/2] Pass all messages through the event bus * Make the MessageBuilder a mandatory formatter and let it pass the created message to the event bus. Users of the message stream then only have to register for on_envelope on the event bus to get the full message stream. --- lib/cucumber/formatter/message.rb | 11 +++--- lib/cucumber/formatter/message_builder.rb | 41 +++++++++-------------- lib/cucumber/formatter/rerun.rb | 14 +++++--- lib/cucumber/runtime.rb | 6 +++- spec/cucumber/formatter/rerun_spec.rb | 6 ++++ spec/cucumber/formatter/spec_helper.rb | 2 +- 6 files changed, 44 insertions(+), 36 deletions(-) diff --git a/lib/cucumber/formatter/message.rb b/lib/cucumber/formatter/message.rb index 38f3b4f71..a4abcb805 100644 --- a/lib/cucumber/formatter/message.rb +++ b/lib/cucumber/formatter/message.rb @@ -1,20 +1,23 @@ # frozen_string_literal: true require 'cucumber/formatter/io' -require 'cucumber/formatter/message_builder' +require 'cucumber/query' module Cucumber module Formatter # The formatter used for --format message - class Message < MessageBuilder + class Message include Io def initialize(config) @io = ensure_io(config.out_stream, config.error_stream) - super(config) + @repository = Cucumber::Repository.new + @query = Cucumber::Query.new(@repository) + config.on_event :envelope, &method(:output_envelope) end - def output_envelope(envelope) + def output_envelope(event) + envelope = event.envelope @repository.update(envelope) @io.write(envelope.to_json) @io.write("\n") diff --git a/lib/cucumber/formatter/message_builder.rb b/lib/cucumber/formatter/message_builder.rb index 3eaa5b7a6..38d86a0a7 100644 --- a/lib/cucumber/formatter/message_builder.rb +++ b/lib/cucumber/formatter/message_builder.rb @@ -7,8 +7,6 @@ require 'cucumber/formatter/query/step_definitions_by_test_step' require 'cucumber/formatter/query/test_case_started_by_test_case' -require 'cucumber/query' - module Cucumber module Formatter class MessageBuilder @@ -24,7 +22,6 @@ def initialize(config) @test_case_started_by_test_case = Query::TestCaseStartedByTestCase.new(config) @repository = Cucumber::Repository.new - @query = Cucumber::Query.new(@repository) @test_run_started_id = config.id_generator.new_id @test_case_by_step_id = {} @@ -32,8 +29,6 @@ def initialize(config) # Ensure all handlers for events occur after all ivars are instantiated - config.on_event :envelope, &method(:on_envelope) - config.on_event :gherkin_source_parsed, &method(:on_gherkin_source_parsed) config.on_event :gherkin_source_read, &method(:on_gherkin_source_read) @@ -79,15 +74,11 @@ def attach(src, media_type, filename) end message = Cucumber::Messages::Envelope.new(attachment: Cucumber::Messages::Attachment.new(**attachment_data)) - output_envelope(message) + @config.event_bus.envelope(message) end private - def on_envelope(event) - output_envelope(event.envelope) - end - def on_gherkin_source_parsed(_event) # TODO: Handle GherkinSourceParsed end @@ -101,7 +92,7 @@ def on_gherkin_source_read(event) ) ) - output_envelope(message) + @config.event_bus.envelope(message) end def on_hook_test_step_created(_event) @@ -118,16 +109,13 @@ def on_test_case_ready(event) ) ) - # TODO: This may be a redundant update. But for now we're leaving this in whilst we're in the transitory phase - @repository.update(message) - # TODO: Switch this over to using the Repo Query object -> `test_step_by_id` # TODO: The finder in query is `find_test_step_by` (Using +TestStepStarted+ message) event.test_case.test_steps.each do |step| @test_case_by_step_id[step.id] = event.test_case end - output_envelope(message) + @config.event_bus.envelope(message) end def test_step_to_message(step) @@ -188,7 +176,7 @@ def on_test_run_started(*) ) ) - output_envelope(message) + @config.event_bus.envelope(message) end def on_test_case_started(event) @@ -203,7 +191,8 @@ def on_test_case_started(event) ) ) - output_envelope(message) + @config.event_bus.envelope(message) + @repository.update(message) end def on_test_step_created(event) @@ -223,7 +212,7 @@ def on_test_step_created(event) # } # ) # - # output_envelope(message) + # @config.event_bus.envelope(message) end def on_test_step_started(event) @@ -238,7 +227,7 @@ def on_test_step_started(event) ) ) - output_envelope(message) + @config.event_bus.envelope(message) end def on_test_step_finished(event) @@ -266,7 +255,7 @@ def on_test_step_finished(event) ) ) - output_envelope(message) + @config.event_bus.envelope(message) end def create_error_message(message_element) @@ -302,7 +291,7 @@ def on_test_case_finished(event) ) ) - output_envelope(message) + @config.event_bus.envelope(message) end def on_test_run_finished(event) @@ -314,7 +303,7 @@ def on_test_run_finished(event) ) ) - output_envelope(message) + @config.event_bus.envelope(message) end def on_step_activated(event) @@ -322,7 +311,7 @@ def on_step_activated(event) end def on_step_definition_registered(event) - output_envelope(event.step_definition.to_envelope) + @config.event_bus.envelope(event.step_definition.to_envelope) end def on_test_run_hook_started(event) @@ -337,7 +326,7 @@ def on_test_run_hook_started(event) ) ) - output_envelope(message) + @config.event_bus.envelope(message) end def on_test_run_hook_finished(event) @@ -361,7 +350,7 @@ def on_test_run_hook_finished(event) ) ) - output_envelope(message) + @config.event_bus.envelope(message) end def on_undefined_parameter_type(event) @@ -372,7 +361,7 @@ def on_undefined_parameter_type(event) ) ) - output_envelope(message) + @config.event_bus.envelope(message) end def test_case_started_id(test_case) diff --git a/lib/cucumber/formatter/rerun.rb b/lib/cucumber/formatter/rerun.rb index bbf7b3081..ae6436628 100644 --- a/lib/cucumber/formatter/rerun.rb +++ b/lib/cucumber/formatter/rerun.rb @@ -1,17 +1,23 @@ # frozen_string_literal: true require 'cucumber/formatter/io' -require 'cucumber/formatter/message_builder' +require 'cucumber/query' module Cucumber module Formatter - class Rerun < MessageBuilder + class Rerun + include Io + def initialize(config) + @config = config @io = ensure_io(config.out_stream, config.error_stream) - super(config) + @repository = Cucumber::Repository.new + @query = Cucumber::Query.new(@repository) + config.on_event :envelope, &method(:output_envelope) end - def output_envelope(envelope) + def output_envelope(event) + envelope = event.envelope @repository.update(envelope) finish_report if envelope.test_run_finished end diff --git a/lib/cucumber/runtime.rb b/lib/cucumber/runtime.rb index 3315fd171..9efd5ca43 100644 --- a/lib/cucumber/runtime.rb +++ b/lib/cucumber/runtime.rb @@ -191,12 +191,16 @@ def set_encoding def report return @report if @report - reports = [summary_report] + formatters + reports = [message_builder, summary_report] + formatters reports << fail_fast_report if @configuration.fail_fast? reports << publish_banner_printer unless @configuration.publish_quiet? @report ||= Formatter::Fanout.new(reports) end + def message_builder + @message_builder ||= Formatter::MessageBuilder.new(@configuration) + end + def summary_report @summary_report ||= Core::Report::Summary.new(@configuration.event_bus) end diff --git a/spec/cucumber/formatter/rerun_spec.rb b/spec/cucumber/formatter/rerun_spec.rb index ae4385701..6c8cefc60 100644 --- a/spec/cucumber/formatter/rerun_spec.rb +++ b/spec/cucumber/formatter/rerun_spec.rb @@ -36,6 +36,7 @@ module Formatter end described_class.new(config) + MessageBuilder.new(config) execute [gherkin], [StandardStepActions.new, Filters::BroadcastTestRunStartedEvent.new(config), Filters::BroadcastTestCaseReadyEvent.new(config)], config.event_bus config.event_bus.test_run_finished @@ -70,6 +71,7 @@ module Formatter end described_class.new(config) + MessageBuilder.new(config) execute [foo, bar], [StandardStepActions.new, Filters::BroadcastTestRunStartedEvent.new(config), Filters::BroadcastTestCaseReadyEvent.new(config)], config.event_bus config.event_bus.test_run_finished @@ -88,6 +90,7 @@ module Formatter end described_class.new(config) + MessageBuilder.new(config) execute [gherkin], [StandardStepActions.new, Filters::BroadcastTestRunStartedEvent.new(config), Filters::BroadcastTestCaseReadyEvent.new(config)], config.event_bus config.event_bus.test_run_finished @@ -107,6 +110,7 @@ module Formatter end described_class.new(config) + MessageBuilder.new(config) execute [gherkin], [FakeObjects::FlakyStepActions.new, Filters::BroadcastTestRunStartedEvent.new(config), Filters::BroadcastTestCaseReadyEvent.new(config)], config.event_bus config.event_bus.test_run_finished @@ -128,6 +132,7 @@ module Formatter end described_class.new(config) + MessageBuilder.new(config) execute [gherkin], [FakeObjects::FlakyStepActions.new, Filters::BroadcastTestRunStartedEvent.new(config), Filters::BroadcastTestCaseReadyEvent.new(config)], config.event_bus config.event_bus.test_run_finished @@ -144,6 +149,7 @@ module Formatter end described_class.new(config) + MessageBuilder.new(config) execute [gherkin, gherkin], [StandardStepActions.new, Filters::BroadcastTestRunStartedEvent.new(config), Filters::BroadcastTestCaseReadyEvent.new(config)], config.event_bus config.event_bus.test_run_finished diff --git a/spec/cucumber/formatter/spec_helper.rb b/spec/cucumber/formatter/spec_helper.rb index 85b7ad34b..ba9d14ee1 100644 --- a/spec/cucumber/formatter/spec_helper.rb +++ b/spec/cucumber/formatter/spec_helper.rb @@ -21,7 +21,7 @@ module SpecHelper def run_defined_feature define_steps - actual_runtime.visitor = Fanout.new([@formatter]) + actual_runtime.visitor = Fanout.new([actual_runtime.send(:message_builder), @formatter]) receiver = Test::Runner.new(event_bus) event_bus.gherkin_source_read(gherkin_doc.uri, gherkin_doc.body) From 08f5bc5c6886e0f8478f1343b14bb351d6e7e57b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Rasmusson?= Date: Fri, 15 May 2026 17:14:13 +0200 Subject: [PATCH 2/2] Send also StepDefinition messages from the event source * When a StepDefinitionRegistered event is fired, send also a StepDefinition message. * Remove the handling of StepDefinitionRegistered event in the MessageBuilder. --- lib/cucumber/formatter/message_builder.rb | 5 ----- lib/cucumber/glue/registry_and_more.rb | 1 + 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/lib/cucumber/formatter/message_builder.rb b/lib/cucumber/formatter/message_builder.rb index 38d86a0a7..9cfbdc8af 100644 --- a/lib/cucumber/formatter/message_builder.rb +++ b/lib/cucumber/formatter/message_builder.rb @@ -35,7 +35,6 @@ def initialize(config) config.on_event :hook_test_step_created, &method(:on_hook_test_step_created) config.on_event :step_activated, &method(:on_step_activated) - config.on_event :step_definition_registered, &method(:on_step_definition_registered) # TODO: Handle TestCaseCreated config.on_event :test_case_ready, &method(:on_test_case_ready) @@ -310,10 +309,6 @@ def on_step_activated(event) # TODO: Handle StepActivated end - def on_step_definition_registered(event) - @config.event_bus.envelope(event.step_definition.to_envelope) - end - def on_test_run_hook_started(event) @current_test_run_hook_started_id = @config.id_generator.new_id diff --git a/lib/cucumber/glue/registry_and_more.rb b/lib/cucumber/glue/registry_and_more.rb index f042c027a..09ef22962 100644 --- a/lib/cucumber/glue/registry_and_more.rb +++ b/lib/cucumber/glue/registry_and_more.rb @@ -99,6 +99,7 @@ def register_rb_step_definition(string_or_regexp, proc_or_sym, options) step_definition = StepDefinition.new(@configuration.id_generator.new_id, self, string_or_regexp, proc_or_sym, options) @step_definitions << step_definition @configuration.notify :step_definition_registered, step_definition + @configuration.notify :envelope, step_definition.to_envelope step_definition rescue Cucumber::CucumberExpressions::UndefinedParameterTypeError => e # TODO: add a way to extract the parameter type directly from the error.