diff --git a/lib/cucumber/configuration.rb b/lib/cucumber/configuration.rb index f460ee872..2efd24a18 100644 --- a/lib/cucumber/configuration.rb +++ b/lib/cucumber/configuration.rb @@ -285,6 +285,10 @@ def id_generator @id_generator ||= Cucumber::Messages::Helpers::IdGenerator::UUID.new end + def test_run_started_id + @test_run_started_id ||= id_generator.new_id + end + private def default_features_paths diff --git a/lib/cucumber/formatter/message_builder.rb b/lib/cucumber/formatter/message_builder.rb index ffc48d57e..2afad896b 100644 --- a/lib/cucumber/formatter/message_builder.rb +++ b/lib/cucumber/formatter/message_builder.rb @@ -15,7 +15,7 @@ def initialize(config) @ast_lookup = AstLookup.new(config) @repository = Cucumber::Repository.new - @test_run_started_id = config.id_generator.new_id + @test_run_started_id = config.test_run_started_id # Fake Query objects @test_case_by_step_id = {} @@ -28,7 +28,6 @@ def initialize(config) # Ensure all handlers for events occur after all ivars are instantiated config.on_event :gherkin_source_parsed, &method(:on_gherkin_source_parsed) - config.on_event :gherkin_source_read, &method(:on_gherkin_source_read) config.on_event :hook_test_step_created, &method(:on_hook_test_step_created) @@ -42,14 +41,15 @@ def initialize(config) config.on_event :test_run_started, &method(:on_test_run_started) config.on_event :test_run_finished, &method(:on_test_run_finished) - config.on_event :test_run_hook_started, &method(:on_test_run_hook_started) - config.on_event :test_run_hook_finished, &method(:on_test_run_hook_finished) - config.on_event :test_step_created, &method(:on_test_step_created) config.on_event :test_step_started, &method(:on_test_step_started) config.on_event :test_step_finished, &method(:on_test_step_finished) - config.on_event :undefined_parameter_type, &method(:on_undefined_parameter_type) + config.on_event :envelope, &method(:on_envelope) + end + + def on_envelope(event) + @current_test_run_hook_started_id = event.envelope.test_run_hook_started.id if event.envelope.test_run_hook_started end def attach(src, media_type, filename) @@ -90,18 +90,6 @@ def on_gherkin_source_parsed(_event) # TODO: Handle GherkinSourceParsed end - def on_gherkin_source_read(event) - message = Cucumber::Messages::Envelope.new( - source: Cucumber::Messages::Source.new( - uri: event.path, - data: event.body, - media_type: 'text/x.cucumber.gherkin+plain' - ) - ) - - @config.event_bus.envelope(message) - end - def on_hook_test_step_created(event) @hook_id_by_test_step_id[event.test_step.id] = event.hook.id end @@ -203,45 +191,6 @@ def on_test_run_finished(event) @config.event_bus.envelope(message) end - def on_test_run_hook_started(event) - @current_test_run_hook_started_id = @config.id_generator.new_id - - message = Cucumber::Messages::Envelope.new( - test_run_hook_started: Cucumber::Messages::TestRunHookStarted.new( - id: @current_test_run_hook_started_id, - hook_id: event.hook.id, - test_run_started_id: @test_run_started_id, - timestamp: time_to_timestamp(Time.now) - ) - ) - - @config.event_bus.envelope(message) - end - - def on_test_run_hook_finished(event) - result = event.test_result - result_message = result.to_message - - if result.failed? - result_message = Cucumber::Messages::TestStepResult.new( - status: result_message.status, - duration: result_message.duration, - message: create_error_message(result.exception), - exception: create_exception_object(result, result.exception) - ) - end - - message = Cucumber::Messages::Envelope.new( - test_run_hook_finished: Cucumber::Messages::TestRunHookFinished.new( - test_run_hook_started_id: @current_test_run_hook_started_id, - timestamp: time_to_timestamp(Time.now), - result: result_message - ) - ) - - @config.event_bus.envelope(message) - end - def on_test_step_created(event) @pickle_id_step_by_test_step_id[event.test_step.id] = event.pickle_step.id @step_definition_ids_by_test_step_id[event.test_step.id] = [] diff --git a/lib/cucumber/glue/registry_and_more.rb b/lib/cucumber/glue/registry_and_more.rb index 1ea09bb16..c47665905 100644 --- a/lib/cucumber/glue/registry_and_more.rb +++ b/lib/cucumber/glue/registry_and_more.rb @@ -4,6 +4,8 @@ require 'cucumber/cucumber_expressions/cucumber_expression' require 'cucumber/cucumber_expressions/regular_expression' require 'cucumber/cucumber_expressions/cucumber_expression_generator' +require 'cucumber/messages/helpers/time_conversion' + require 'cucumber/glue/dsl' require 'cucumber/glue/snippet' require 'cucumber/glue/hook' @@ -48,6 +50,8 @@ def initialize(first_proc, second_proc) class RegistryAndMore attr_reader :current_world, :step_definitions + include Cucumber::Messages::Helpers::TimeConversion + all_keywords = ::Gherkin::DIALECTS.keys.map do |dialect_name| dialect = ::Gherkin::Dialect.for(dialect_name) dialect.given_keywords + dialect.when_keywords + dialect.then_keywords + dialect.and_keywords + dialect.but_keywords @@ -98,14 +102,20 @@ def define_parameter_type(parameter_type) 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 + @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. - type_name = e.message.match(/^Undefined parameter type ['|{](.*)['|}].?$/)[1] - - @configuration.notify :undefined_parameter_type, type_name, string_or_regexp + @configuration.notify(:undefined_parameter_type, e.undefined_parameter_type_name, string_or_regexp) + # Move the below code into cucumber-expressions. Once done. Switch the line for + # @configuration.notify(:envelope, e.to_envelope(string_or_regexp)) + to_envelope = Cucumber::Messages::Envelope.new( + undefined_parameter_type: Cucumber::Messages::UndefinedParameterType.new( + name: e.undefined_parameter_type_name, + expression: string_or_regexp + ) + ) + @configuration.notify(:envelope, to_envelope) end def build_rb_world_factory(world_modules, namespaced_world_modules, proc) @@ -195,17 +205,76 @@ def create_expression(string_or_regexp) def invoke_run_hook(hook, pseudo_method) @configuration.notify(:test_run_hook_started, hook) + + current_test_run_hook_started_id = @configuration.id_generator.new_id + started_envelope = test_run_hook_started_envelope(hook, current_test_run_hook_started_id) + @configuration.notify(:envelope, started_envelope) + timer = Core::Test::Timer.new.start begin hook.invoke(pseudo_method, []) @configuration.notify(:test_run_hook_finished, hook, Core::Test::Result::Passed.new(timer.duration)) + finished_envelope = test_run_hook_finished_envelope(Core::Test::Result::Passed.new(timer.duration), current_test_run_hook_started_id) + @configuration.notify(:envelope, finished_envelope) true rescue StandardError => e @configuration.notify(:test_run_hook_finished, hook, Core::Test::Result::Failed.new(timer.duration, e)) + finished_envelope = test_run_hook_finished_envelope(Core::Test::Result::Failed.new(timer.duration, e), current_test_run_hook_started_id) + @configuration.notify(:envelope, finished_envelope) false end end + def test_run_hook_started_envelope(hook, id) + Cucumber::Messages::Envelope.new( + test_run_hook_started: Cucumber::Messages::TestRunHookStarted.new( + id: id, + hook_id: hook.id, + test_run_started_id: @configuration.test_run_started_id, + timestamp: time_to_timestamp(Time.now) + ) + ) + end + + def test_run_hook_finished_envelope(test_result, test_run_hook_started_id) + result = test_result + result_message = result.to_message + + if result.failed? + result_message = Cucumber::Messages::TestStepResult.new( + status: result_message.status, + duration: result_message.duration, + message: create_error_message(result.exception), + exception: create_exception_object(result, result.exception) + ) + end + + Cucumber::Messages::Envelope.new( + test_run_hook_finished: Cucumber::Messages::TestRunHookFinished.new( + test_run_hook_started_id: test_run_hook_started_id, + timestamp: time_to_timestamp(Time.now), + result: result_message + ) + ) + end + + def create_error_message(message_element) + <<~ERROR_MESSAGE + #{message_element.message} (#{message_element.class}) + #{message_element.backtrace} + ERROR_MESSAGE + end + + def create_exception_object(result, message_element) + return unless result.failed? + + Cucumber::Messages::Exception.new( + type: message_element.class, + message: message_element.message, + stack_trace: message_element.backtrace.join("\n") + ) + end + def parameter_type_envelope(parameter_type) # TODO: should this be moved to Cucumber::Expression::ParameterType#to_envelope ?? # Note: that would mean that cucumber-expression would depend on cucumber-messages diff --git a/lib/cucumber/runtime.rb b/lib/cucumber/runtime.rb index 3c760eb6f..70a7a68f8 100644 --- a/lib/cucumber/runtime.rb +++ b/lib/cucumber/runtime.rb @@ -130,6 +130,22 @@ def features @features ||= feature_files.map do |path| source = NormalisedEncodingFile.read(path) @configuration.notify :gherkin_source_read, path, source + + # TODO: When core is v17+ switch the below code out to the following + # Cucumber::Core::Gherkin::Document.new(path, source).tap do |document| + # @configuration.notify(:envelope, document.to_envelope) + # end + to_envelope = + Cucumber::Messages::Envelope.new( + source: Cucumber::Messages::Source.new( + uri: path, + data: source, + media_type: 'text/x.cucumber.gherkin+plain' + ) + ) + + @configuration.notify(:envelope, to_envelope) + Cucumber::Core::Gherkin::Document.new(path, source) end end