Skip to content

Commit

Permalink
Allow World constructor to set explicit World object (close #50)
Browse files Browse the repository at this point in the history
  • Loading branch information
jbpros committed Mar 21, 2012
1 parent aad94c4 commit 14e4ef5
Show file tree
Hide file tree
Showing 7 changed files with 121 additions and 16 deletions.
29 changes: 21 additions & 8 deletions features/step_definitions/cucumber_js_mappings.rb
Original file line number Diff line number Diff line change
@@ -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

Expand Down Expand Up @@ -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() {
Expand Down Expand Up @@ -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)
Expand Down
18 changes: 18 additions & 0 deletions features/step_definitions/cucumber_steps.js
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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');
Expand Down
18 changes: 18 additions & 0 deletions features/step_definitions/cucumber_steps.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down
15 changes: 15 additions & 0 deletions features/step_definitions/cucumber_world.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down Expand Up @@ -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);
Expand Down
23 changes: 23 additions & 0 deletions features/world_constructor_callback_with_object.feature
Original file line number Diff line number Diff line change
@@ -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
4 changes: 2 additions & 2 deletions lib/cucumber/support_code/library.js
Original file line number Diff line number Diff line change
Expand Up @@ -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);
});
});
}
Expand Down
30 changes: 24 additions & 6 deletions spec/cucumber/support_code/library_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -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);
});
});

});
});
});
Expand Down

0 comments on commit 14e4ef5

Please sign in to comment.