Skip to content

Commit

Permalink
Handle missing instance in World constructor callback (close #40)
Browse files Browse the repository at this point in the history
  • Loading branch information
jbpros committed Jan 20, 2012
1 parent d579383 commit 6c1ba44
Show file tree
Hide file tree
Showing 7 changed files with 83 additions and 25 deletions.
16 changes: 10 additions & 6 deletions features/step_definitions/cucumber_js_mappings.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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

Expand Down
10 changes: 10 additions & 0 deletions features/step_definitions/cucumber_steps.js
Original file line number Diff line number Diff line change
Expand Up @@ -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);
});
Expand Down Expand Up @@ -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;
9 changes: 9 additions & 0 deletions features/step_definitions/cucumber_steps.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -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
14 changes: 10 additions & 4 deletions features/step_definitions/cucumber_world.js
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
8 changes: 8 additions & 0 deletions features/world_constructor_callback.feature
Original file line number Diff line number Diff line change
@@ -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
4 changes: 4 additions & 0 deletions lib/cucumber/support_code/library.js
Original file line number Diff line number Diff line change
@@ -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();
Expand Down Expand Up @@ -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);
});
Expand Down
47 changes: 32 additions & 15 deletions spec/cucumber/support_code/library_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -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);
});
});
});
Expand Down Expand Up @@ -349,4 +366,4 @@ describe("Cucumber.SupportCode.Library", function() {
});
});
});
});
});

0 comments on commit 6c1ba44

Please sign in to comment.