diff --git a/features/step_definitions/cucumber_js_mappings.rb b/features/step_definitions/cucumber_js_mappings.rb index 010a79d09..195073ac0 100644 --- a/features/step_definitions/cucumber_js_mappings.rb +++ b/features/step_definitions/cucumber_js_mappings.rb @@ -1,12 +1,13 @@ module CucumberJsMappings - STEP_DEFINITIONS_FILE = "features/step_definitions/cucumber_steps.js" - COFFEE_SCRIPT_DEFINITIONS_FILE = "features/step_definitions/cucumber_steps.coffee" - FEATURE_FILE = "features/a_feature.feature" - WORLD_VARIABLE_LOG_FILE = "world_variable.log" - WORLD_FUNCTION_LOG_FILE = "world_function.log" - DATA_TABLE_LOG_FILE = "data_table.log" - CYCLE_LOG_FILE = "cycle.log" - CYCLE_SEQUENCE_SEPARATOR = " -> " + STEP_DEFINITIONS_FILE = "features/step_definitions/cucumber_steps.js" + COFFEE_SCRIPT_DEFINITIONS_FILE = "features/step_definitions/cucumber_steps.coffee" + FEATURE_FILE = "features/a_feature.feature" + WORLD_VARIABLE_LOG_FILE = "world_variable.log" + WORLD_FUNCTION_LOG_FILE = "world_function.log" + EXPLICIT_WORLD_OBJECT_FUNCTION_LOG_FILE = "world_function.log"; + DATA_TABLE_LOG_FILE = "data_table.log" + CYCLE_LOG_FILE = "cycle.log" + CYCLE_SEQUENCE_SEPARATOR = " -> " attr_accessor :support_code @@ -124,6 +125,14 @@ def write_custom_world_constructor append_support_code "this.World = function CustomWorld(callback) { callback(); };\n" end + def write_custom_world_constructor_calling_back_with_explicit_object + append_support_code "this.World = function CustomWorldConstructor(callback) { + callback({ + someFunction: function() { fs.writeFileSync(\"#{EXPLICIT_WORLD_OBJECT_FUNCTION_LOG_FILE}\", \"\")} + }); +};\n" + end + def write_world_function append_support_code <<-EOF this.World.prototype.someFunction = function() { @@ -231,6 +240,10 @@ def assert_world_function_called check_file_presence [WORLD_FUNCTION_LOG_FILE], true end + def assert_explicit_world_object_function_called + check_file_presence [EXPLICIT_WORLD_OBJECT_FUNCTION_LOG_FILE], true + end + def assert_cycle_sequence *args expected_string = args.join CYCLE_SEQUENCE_SEPARATOR check_file_content(CucumberJsMappings::CYCLE_LOG_FILE, expected_string, true) diff --git a/features/step_definitions/cucumber_steps.js b/features/step_definitions/cucumber_steps.js index 695da170f..b576e8020 100644 --- a/features/step_definitions/cucumber_steps.js +++ b/features/step_definitions/cucumber_steps.js @@ -126,6 +126,13 @@ setTimeout(callback.pending, 10);\ callback(); }); + Given(/a custom World constructor calling back with an explicit object$/, function(callback) { + this.stepDefinitions += "this.World = function CustomWorldConstructor(callback) {\n\ + callback({someFunction: function () {world.explicitWorldFunctionCalled = true; }});\n\ +};\n"; + callback(); + }); + Given(/^a scenario without any tags$/, function(callback) { this.addPassingScenarioWithoutTags(); callback(); @@ -159,6 +166,11 @@ setTimeout(callback.pending, 10);\ this.runAScenario(callback); }); + When(/^Cucumber executes a scenario that calls a function on the explicit World object$/, function(callback) { + // express the regexp above with the code you wish you had + this.runAScenarioCallingWorldFunction(callback); + }); + When(/^Cucumber executes a scenario tagged with "([^"]*)"$/, function(tag, callback) { this.addPassingScenarioWithTags(tag); this.runFeature({}, callback); @@ -276,6 +288,12 @@ callback();\ callback(); }); + this.Then(/^the explicit World object function should have been called$/, function(callback) { + this.assertTrue(this.explicitWorldFunctionCalled); + callback(); + }); + + Then(/^the (before|after) hook is fired (?:before|after) the scenario$/, function(hookType, callback) { if (hookType == 'before') this.assertCycleSequence(hookType, 'step 1'); diff --git a/features/step_definitions/cucumber_steps.rb b/features/step_definitions/cucumber_steps.rb index e7c2ec0ab..a0066ffa9 100644 --- a/features/step_definitions/cucumber_steps.rb +++ b/features/step_definitions/cucumber_steps.rb @@ -13,6 +13,10 @@ write_asynchronously_failing_mapping_with_message(step_name, message) end +Given /^a custom World constructor calling back with an explicit object$/ do + write_custom_world_constructor_calling_back_with_explicit_object +end + Given /^an around hook tagged with "([^"]*)"$/ do |tag| write_passing_hook :type => "around", :tags => [tag], :log_cycle_event_as => "hook" end @@ -26,10 +30,24 @@ run_feature end +When /^Cucumber executes a scenario that calls a function on the explicit World object$/ do + write_mapping_calling_world_function("I call the explicit world object function") + write_feature <<-EOF +Feature: + Scenario: + When I call the explicit world object function +EOF + run_feature +end + Then /^the mapping is run$/ do assert_passed "a mapping" end +Then /^the explicit World object function should have been called$/ do + assert_explicit_world_object_function_called +end + Then /^I see the version of Cucumber$/ do assert_matching_output "\\d+\\.\\d+\\.\\d+\\n", all_output assert_success true diff --git a/features/step_definitions/cucumber_world.js b/features/step_definitions/cucumber_world.js index 48ba81a04..f65555d4f 100644 --- a/features/step_definitions/cucumber_world.js +++ b/features/step_definitions/cucumber_world.js @@ -55,6 +55,16 @@ proto.runAScenario = function runAScenario(callback) { this.runFeature({}, callback); } +proto.runAScenarioCallingWorldFunction = function runAScenarioCallingWorldFunction(callback) { + this.addScenario("", "Given a step"); + this.stepDefinitions += "Given(/^a step$/, function(callback) {\ + world.logCycleEvent('step 1');\ + this.someFunction();\ + callback();\ +});"; + this.runFeature({}, callback); +} + proto.logCycleEvent = function logCycleEvent(event) { this.cycleEvents += " -> " + event; } @@ -188,6 +198,11 @@ proto.assertEqual = function assertRawDataTable(expected, actual) { throw(new Error("Expected:\n\"" + actualJSON + "\"\nto match:\n\"" + expectedJSON + "\"")); } +proto.assertTrue = function assertTrue(value) { + if (!value) + throw(new Error("Expected:\n\"" + value + "\"\n to be true")); +} + proto.assertExecutedNumberedScenarios = function assertExecutedNumberedScenarios() { var self = this; var scenarioIndexes = Array.prototype.slice.apply(arguments); diff --git a/features/world_constructor_callback_with_object.feature b/features/world_constructor_callback_with_object.feature new file mode 100644 index 000000000..226a10d01 --- /dev/null +++ b/features/world_constructor_callback_with_object.feature @@ -0,0 +1,23 @@ +Feature: World constructor callback with object + It is possible for the World constructor function to tell Cucumber + to use another object than itself as the World instance: + + this.World = function WorldConstructor(callback) { + var myCustomWorld = { dance: function() { /* ... */ } }; + callback(myCustomWorld); // tell Cucumber to use myCustomWorld + // as the world object. + }; + + If no parameter is passed to the callback, the WorldConstructor + instance will be used by Cucumber: + + this.World = function WorldConstructor(callback) { + var myCustomWorld = {}; + callback(); // could have been written `callback(this);` + }; + + Scenario: scenario calling function on explicit world instance + Given a custom World constructor calling back with an explicit object + When Cucumber executes a scenario that calls a function on the explicit World object + Then the feature passes + And the explicit World object function should have been called diff --git a/lib/cucumber/support_code/library.js b/lib/cucumber/support_code/library.js index 12e41c910..501e627b5 100644 --- a/lib/cucumber/support_code/library.js +++ b/lib/cucumber/support_code/library.js @@ -51,9 +51,9 @@ var Library = function(supportCodeDefinition) { }, instantiateNewWorld: function instantiateNewWorld(callback) { - var world = new worldConstructor(function() { + var world = new worldConstructor(function(explicitWorld) { process.nextTick(function() { // release the constructor - callback(world); + callback(explicitWorld || world); }); }); } diff --git a/spec/cucumber/support_code/library_spec.js b/spec/cucumber/support_code/library_spec.js index 55b40a966..1f93c5385 100644 --- a/spec/cucumber/support_code/library_spec.js +++ b/spec/cucumber/support_code/library_spec.js @@ -310,15 +310,33 @@ describe("Cucumber.SupportCode.Library", function() { describe("next tick registered function", function() { var nextTickFunction; - beforeEach(function() { - worldConstructorCompletionCallback(); - nextTickFunction = process.nextTick.mostRecentCall.args[0]; + describe("when the world constructor called back without any argument", function() { + beforeEach(function() { + worldConstructorCompletionCallback(); + nextTickFunction = process.nextTick.mostRecentCall.args[0]; + }); + + it("calls back with the world instance", function() { + nextTickFunction(); + expect(callback).toHaveBeenCalledWith(worldInstance); + }); }); - it("calls back with the world instance", function() { - nextTickFunction(); - expect(callback).toHaveBeenCalledWith(worldInstance); + describe("when the world constructor called back with an explicit world object", function() { + var explicitWorld; + + beforeEach(function() { + explicitWorld = createSpy("explicit world object"); + worldConstructorCompletionCallback(explicitWorld); + nextTickFunction = process.nextTick.mostRecentCall.args[0]; + }); + + it("calls back with the world instance", function() { + nextTickFunction(); + expect(callback).toHaveBeenCalledWith(explicitWorld); + }); }); + }); }); });