diff --git a/features/step_definitions/cucumber_js_mappings.rb b/features/step_definitions/cucumber_js_mappings.rb index 4dc8daf06..acc69c0c5 100644 --- a/features/step_definitions/cucumber_js_mappings.rb +++ b/features/step_definitions/cucumber_js_mappings.rb @@ -99,7 +99,11 @@ def write_world_variable_with_numeric_value(value) end def write_custom_world_constructor - append_support_code "this.World = function CustomWorld(callback) { callback(this); };" + append_support_code "this.World = function CustomWorld(callback) { callback(this); };\n" + end + + def write_world_constructor_not_calling_back_with_instance + append_support_code "this.World = function CustomWorld(callback) { callback(); };\n" end def write_world_function @@ -132,11 +136,11 @@ def provide_cycle_logging_facilities @cycle_logging_facilities_ready = true append_support_code <<-EOF - this.World.prototype.logCycleEvent = function logCycleEvent(name) { - fd = fs.openSync('#{CYCLE_LOG_FILE}', 'a'); - fs.writeSync(fd, " -> " + name, null); - fs.closeSync(fd); - }; +this.World.prototype.logCycleEvent = function logCycleEvent(name) { + fd = fs.openSync('#{CYCLE_LOG_FILE}', 'a'); + fs.writeSync(fd, " -> " + name, null); + fs.closeSync(fd); +}; EOF end diff --git a/features/step_definitions/cucumber_steps.js b/features/step_definitions/cucumber_steps.js index ee3be1efc..1c7c0c3bb 100644 --- a/features/step_definitions/cucumber_steps.js +++ b/features/step_definitions/cucumber_steps.js @@ -72,6 +72,11 @@ var cucumberSteps = function() { callback(); }); + Given(/^a custom World constructor calling back without an instance$/, function(callback) { + this.stepDefinitions += "this.World = function CustomWorld(callback) { callback(); };\n"; + callback(); + }); + When(/^Cucumber executes the scenario$/, function(callback) { this.runFeature(callback); }); @@ -167,5 +172,10 @@ var cucumberSteps = function() { this.assertCycleSequence('step', hookType); callback(); }); + + Then(/^an error about the missing World instance is raised$/, function(callback) { + this.assertFailureMessage("World constructor called back without World instance"); + callback(); + }); }; module.exports = cucumberSteps; diff --git a/features/step_definitions/cucumber_steps.rb b/features/step_definitions/cucumber_steps.rb index 7ac6e2f81..14c27500c 100644 --- a/features/step_definitions/cucumber_steps.rb +++ b/features/step_definitions/cucumber_steps.rb @@ -5,6 +5,10 @@ write_coffee_script_definition_file end +Given /^a custom World constructor calling back without an instance$/ do + write_world_constructor_not_calling_back_with_instance +end + When /^Cucumber executes a scenario using that mapping$/ do write_feature <<-EOF Feature: @@ -27,3 +31,8 @@ assert_partial_output "Usage: cucumber.js ", all_output assert_success true end + +Then /^an error about the missing World instance is raised$/ do + assert_partial_output("World constructor called back without World instance", all_output) + assert_success false +end diff --git a/features/step_definitions/cucumber_world.js b/features/step_definitions/cucumber_world.js index d2086136d..eea3aad70 100644 --- a/features/step_definitions/cucumber_world.js +++ b/features/step_definitions/cucumber_world.js @@ -27,12 +27,18 @@ proto.runFeatureWithSupportCodeSource = function runFeatureWithSupportCodeSource var cucumber = Cucumber(this.featureSource, supportCode); var formatter = Cucumber.Listener.ProgressFormatter({logToConsole: false}); cucumber.attachListener(formatter); - cucumber.start(function(succeeded) { - world.runSucceeded = succeeded; - world.runOutput = formatter.getLogs(); + try { + cucumber.start(function(succeeded) { + world.runSucceeded = succeeded; + world.runOutput = formatter.getLogs(); + Cucumber.Debug.notice(world.runOutput, 'cucumber output', 5); + callback(); + }); + } catch(e) { + world.runOutput += e.toString(); Cucumber.Debug.notice(world.runOutput, 'cucumber output', 5); callback(); - }); + } } proto.runAScenario = function runAScenario(callback) { diff --git a/features/world_constructor_callback.feature b/features/world_constructor_callback.feature new file mode 100644 index 000000000..374f83cd2 --- /dev/null +++ b/features/world_constructor_callback.feature @@ -0,0 +1,8 @@ +Feature: World constructor callback + A callback is passed to World constructors. It is expected to be called + with the fresh World instance passed as its only parameter. + + Scenario: error on missing World instance + Given a custom World constructor calling back without an instance + When Cucumber executes a scenario + Then an error about the missing World instance is raised diff --git a/lib/cucumber/support_code/library.js b/lib/cucumber/support_code/library.js index 3ae9a8604..b0f6d7dec 100644 --- a/lib/cucumber/support_code/library.js +++ b/lib/cucumber/support_code/library.js @@ -1,4 +1,5 @@ var Library = function(supportCodeDefinition) { + var MISSING_WORLD_INSTANCE_ERROR = "World constructor called back without World instance."; var Cucumber = require('../../cucumber'); var beforeHooks = Cucumber.Type.Collection(); @@ -52,6 +53,9 @@ var Library = function(supportCodeDefinition) { instantiateNewWorld: function instantiateNewWorld(callback) { new worldConstructor(function(world) { + if (!world) { + throw new Error(MISSING_WORLD_INSTANCE_ERROR); + } process.nextTick(function() { // release the constructor callback(world); }); diff --git a/spec/cucumber/support_code/library_spec.js b/spec/cucumber/support_code/library_spec.js index 3b4fd3b85..e9fa92a41 100644 --- a/spec/cucumber/support_code/library_spec.js +++ b/spec/cucumber/support_code/library_spec.js @@ -300,26 +300,43 @@ describe("Cucumber.SupportCode.Library", function() { beforeEach(function() { library.instantiateNewWorld(callback); worldConstructorCompletionCallback = worldConstructor.mostRecentCall.args[0]; - world = createSpy("world instance"); spyOn(process, 'nextTick'); - }); - - it("registers a function for the next tick (to get out of the constructor call)", function() { - worldConstructorCompletionCallback(world); - expect(process.nextTick).toHaveBeenCalledWithAFunctionAsNthParameter(1); - }); - - describe("next tick registered function", function() { - var nextTickFunction; + }) + describe("when the constructor called back with a world instance", function() { beforeEach(function() { + world = createSpy("world instance"); + }); + + it("registers a function for the next tick (to get out of the constructor call)", function() { worldConstructorCompletionCallback(world); - nextTickFunction = process.nextTick.mostRecentCall.args[0]; + expect(process.nextTick).toHaveBeenCalledWithAFunctionAsNthParameter(1); + }); + + describe("next tick registered function", function() { + var nextTickFunction; + + beforeEach(function() { + worldConstructorCompletionCallback(world); + nextTickFunction = process.nextTick.mostRecentCall.args[0]; + }); + + it("calls back with the world instance", function() { + nextTickFunction(); + expect(callback).toHaveBeenCalledWith(world); + }); + }); + }); + + describe("when the constructor called back without a world instance", function() { + it("does not register a function for the next tick", function() { + try { worldConstructorCompletionCallback(null); } catch (e) {}; + expect(process.nextTick).not.toHaveBeenCalled(); }); - it("calls back with the world instance", function() { - nextTickFunction(); - expect(callback).toHaveBeenCalledWith(world); + it("throws an exception", function() { + var expectedError = new Error("World constructor called back without World instance."); + expect(function() { worldConstructorCompletionCallback(null); }).toThrow(expectedError); }); }); }); @@ -349,4 +366,4 @@ describe("Cucumber.SupportCode.Library", function() { }); }); }); -}); \ No newline at end of file +});