Skip to content

Commit

Permalink
Add "World" (close #26)
Browse files Browse the repository at this point in the history
  • Loading branch information
jbpros committed Oct 12, 2011
1 parent 6ae8b2d commit e096ece
Show file tree
Hide file tree
Showing 14 changed files with 172 additions and 35 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ node_modules
.rvmrc
doc/
tmp/
npm-debug.log
2 changes: 1 addition & 1 deletion features/cucumber-features
Submodule cucumber-features updated from 809d3a to f89734
42 changes: 42 additions & 0 deletions features/step_definitions/cucumber_js_mappings.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ 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"

attr_accessor :support_code

Expand Down Expand Up @@ -41,6 +43,24 @@ def write_failing_mapping_with_message(step_name, message)
append_step_definition(step_name, "throw(new Error('#{message}'));")
end

def write_mapping_incrementing_world_variable_by_value(step_name, increment_value)
append_step_definition(step_name, "this.variable += #{increment_value}; callback();")
end

def write_mapping_logging_world_variable_value(step_name, time = "1")
step_def = <<-EOF
fs.writeFileSync("#{WORLD_VARIABLE_LOG_FILE}.#{time}", "" + this.variable); callback();
EOF
append_step_definition(step_name, step_def)
end

def write_mapping_calling_world_function(step_name)
step_def = <<-EOF
this.someFunction(); callback();
EOF
append_step_definition(step_name, step_def)
end

def write_calculator_code
rpn_calculator_code = get_file_contents('../support/rpn_calculator.js')
create_dir 'features/support'
Expand All @@ -57,6 +77,20 @@ def write_mappings_for_calculator
EOF
end

def write_world_variable_with_numeric_value(value)
append_support_code <<-EOF
this.World.prototype.variable = #{value};
EOF
end

def write_world_function
append_support_code <<-EOF
this.World.prototype.someFunction = function() {
fs.writeFileSync("#{WORLD_FUNCTION_LOG_FILE}", "");
};
EOF
end

def assert_passing_scenario
assert_partial_output("1 scenario (1 passed)", all_output)
assert_success true
Expand Down Expand Up @@ -86,6 +120,14 @@ def assert_scenario_not_reported_as_failing(scenario_name)
assert_no_partial_output("# Scenario: #{scenario_name}", all_output)
end

def assert_world_variable_held_value_at_time(value, time)
check_exact_file_content "#{WORLD_VARIABLE_LOG_FILE}.#{time}", value
end

def assert_world_function_called
check_file_presence [WORLD_FUNCTION_LOG_FILE], true
end

def failed_output
"failed"
end
Expand Down
3 changes: 2 additions & 1 deletion lib/cucumber/ast/step.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@ var Step = function(keyword, name, line) {

execute: function execute(visitor, callback) {
var stepDefinition = visitor.lookupStepDefinitionByName(name);
stepDefinition.invoke(name, docString, callback);
var world = visitor.getWorld();
stepDefinition.invoke(name, world, docString, callback);
}
};
return self;
Expand Down
12 changes: 12 additions & 0 deletions lib/cucumber/runtime/ast_tree_walker.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
var AstTreeWalker = function(features, supportCodeLibrary, listeners) {
var listeners;
var world;
var allFeaturesSucceded = true;
var skippingSteps = false;

Expand Down Expand Up @@ -38,6 +39,9 @@ var AstTreeWalker = function(features, supportCodeLibrary, listeners) {

visitScenario: function visitScenario(scenario, callback) {
self.witnessNewScenario();
var world = supportCodeLibrary.instantiateNewWorld();
self.setWorld(world);

var payload = { scenario: scenario };
var event = AstTreeWalker.Event(AstTreeWalker.SCENARIO_EVENT_NAME, payload);
self.broadcastEventAroundUserFunction(
Expand Down Expand Up @@ -105,6 +109,14 @@ var AstTreeWalker = function(features, supportCodeLibrary, listeners) {
return supportCodeLibrary.lookupStepDefinitionByName(stepName);
},

setWorld: function setWorld(newWorld) {
world = newWorld;
},

getWorld: function getWorld() {
return world;
},

isStepUndefined: function isStepUndefined(step) {
var stepName = step.getName();
return !supportCodeLibrary.isStepDefinitionNameDefined(stepName);
Expand Down
9 changes: 5 additions & 4 deletions lib/cucumber/support_code.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
var SupportCode = {};
SupportCode.Library = require('./support_code/library');
SupportCode.StepDefinition = require('./support_code/step_definition');
module.exports = SupportCode;
var SupportCode = {};
SupportCode.Library = require('./support_code/library');
SupportCode.StepDefinition = require('./support_code/step_definition');
SupportCode.WorldConstructor = require('./support_code/world_constructor');
module.exports = SupportCode;
8 changes: 7 additions & 1 deletion lib/cucumber/support_code/library.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ var Library = function(supportCodeDefinition) {
var Cucumber = require('../../cucumber');

var stepDefinitions = Cucumber.Type.Collection();
var worldConstructor = Cucumber.SupportCode.WorldConstructor();

var self = {
lookupStepDefinitionByName: function lookupStepDefinitionByName(name) {
Expand All @@ -23,14 +24,19 @@ var Library = function(supportCodeDefinition) {
defineStep: function defineStep(name, code) {
var stepDefinition = Cucumber.SupportCode.StepDefinition(name, code);
stepDefinitions.add(stepDefinition);
},

instantiateNewWorld: function instantiateNewWorld() {
return new worldConstructor();
}
};

var supportCodeHelper = {
Given : self.defineStep,
When : self.defineStep,
Then : self.defineStep,
defineStep : self.defineStep
defineStep : self.defineStep,
World : worldConstructor
};
supportCodeDefinition.call(supportCodeHelper);

Expand Down
4 changes: 2 additions & 2 deletions lib/cucumber/support_code/step_definition.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ var StepDefinition = function(regexp, code) {
return regexp.test(stepName);
},

invoke: function invoke(stepName, docString, callback) {
invoke: function invoke(stepName, world, docString, callback) {
var codeCallback = function() {
var successfulStepResult = Cucumber.Runtime.SuccessfulStepResult();
callback(successfulStepResult);
Expand All @@ -17,7 +17,7 @@ var StepDefinition = function(regexp, code) {

var parameters = self.buildInvocationParameters(stepName, docString, codeCallback);
try {
code.apply(undefined, parameters);
code.apply(world, parameters);
} catch (exception) {
if (exception)
Cucumber.Debug.warn(exception.stack || exception, 'exception inside feature', 3);
Expand Down
4 changes: 4 additions & 0 deletions lib/cucumber/support_code/world_constructor.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
var WorldConstructor = function() {
return function() {};
};
module.exports = WorldConstructor;
19 changes: 13 additions & 6 deletions spec/cucumber/ast/step_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -82,27 +82,34 @@ describe("Cucumber.Ast.Step", function() {
});

describe("execute()", function() {
var stepDefinition;
var stepDefinition, world;
var visitor, callback;

beforeEach(function() {
stepDefinition = createSpy("Step definition");
visitor = createSpy("Visitor");
callback = createSpy("Callback received by execute()");
stepDefinition = createSpy("step definition");
world = createSpy("world");
visitor = createSpy("visitor");
callback = createSpy("callback received by execute()");
spyOnStub(stepDefinition, 'invoke');
spyOnStub(visitor, 'lookupStepDefinitionByName').andReturn(stepDefinition);
spyOnStub(visitor, 'getWorld').andReturn(world);
});

it("looks up the step definition based on the step string", function() {
step.execute(visitor, callback);
expect(visitor.lookupStepDefinitionByName).toHaveBeenCalledWith(name);
});

it("invokes the step definition with the step name, DocString and the callback", function() {
it("gets the current World instance", function() {
step.execute(visitor, callback);
expect(visitor.getWorld).toHaveBeenCalled();
});

it("invokes the step definition with the step name, world, DocString and the callback", function() {
var docString = createSpy("DocString");
step.attachDocString(docString);
step.execute(visitor, callback);
expect(stepDefinition.invoke).toHaveBeenCalledWith(name, docString, callback);
expect(stepDefinition.invoke).toHaveBeenCalledWith(name, world, docString, callback);
});
});
});
27 changes: 27 additions & 0 deletions spec/cucumber/runtime/ast_tree_walker_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -162,22 +162,36 @@ describe("Cucumber.Runtime.AstTreeWalker", function() {

describe("visitScenario()", function() {
var scenario, callback, event, payload;
var world;

beforeEach(function() {
scenario = createSpyWithStubs("Scenario AST element", {acceptVisitor: null});
callback = createSpy("Callback");
event = createSpy("Event");
payload = {scenario: scenario};
world = createSpy("world instance");
spyOn(Cucumber.Runtime.AstTreeWalker, 'Event').andReturn(event);
spyOnStub(supportCodeLibrary, 'instantiateNewWorld').andReturn(world);
spyOn(treeWalker, 'broadcastEventAroundUserFunction');
spyOn(treeWalker, 'witnessNewScenario');
spyOn(treeWalker, 'setWorld');
});

it("witnesses a new scenario", function() {
treeWalker.visitScenario(scenario, callback);
expect(treeWalker.witnessNewScenario).toHaveBeenCalled();
});

it("instantiates a new World instance", function() {
treeWalker.visitScenario(scenario, callback);
expect(supportCodeLibrary.instantiateNewWorld).toHaveBeenCalled();
});

it("sets the new World instance", function() {
treeWalker.visitScenario(scenario, callback);
expect(treeWalker.setWorld).toHaveBeenCalledWith(world);
});

it("creates a new event about the scenario", function() {
treeWalker.visitScenario(scenario, callback);
expect(Cucumber.Runtime.AstTreeWalker.Event).toHaveBeenCalledWith(Cucumber.Runtime.AstTreeWalker.SCENARIO_EVENT_NAME, payload);
Expand Down Expand Up @@ -547,6 +561,19 @@ describe("Cucumber.Runtime.AstTreeWalker", function() {
});
});

describe("getWorld() [setWorld()]", function() {
var world;

beforeEach(function() {
world = createSpy("world instance");
});

it("returns the World instance set with setWorld()", function() {
treeWalker.setWorld(world);
expect(treeWalker.getWorld()).toBe(world);
});
});

describe("lookupStepDefinitionByName()", function() {
var stepName, stepDefinition;

Expand Down
33 changes: 29 additions & 4 deletions spec/cucumber/support_code/library_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ describe("Cucumber.SupportCode.Library", function() {
var Cucumber = require('cucumber');
var library, rawSupportCode;
var stepDefinitionCollection;
var worldConstructor;
var spiesDuringSupportCodeDefinitionExecution = {};
var worldConstructorCalled;

beforeEach(function() {
rawSupportCode = createSpy("Raw support code");
Expand All @@ -13,8 +15,11 @@ describe("Cucumber.SupportCode.Library", function() {
createSpyWithStubs("Second step definition", {matchesStepName:false}),
createSpyWithStubs("Third step definition", {matchesStepName:false})
];
worldConstructorCalled = false;
worldConstructor = function() { worldConstructorCalled = true; };
spyOnStub(stepDefinitionCollection, 'syncForEach').andCallFake(function(cb) { stepDefinitionCollection.forEach(cb); });
spyOn(Cucumber.Type, 'Collection').andReturn(stepDefinitionCollection);
spyOn(Cucumber.SupportCode, 'WorldConstructor').andReturn(worldConstructor);
library = Cucumber.SupportCode.Library(rawSupportCode);
});

Expand All @@ -27,6 +32,10 @@ describe("Cucumber.SupportCode.Library", function() {
expect(rawSupportCode).toHaveBeenCalled();
});

it("creates a new World constructor", function() {
expect(Cucumber.SupportCode.WorldConstructor).toHaveBeenCalled();
});

it("executes the raw support code with a support code helper as 'this'", function() {
expect(rawSupportCode.mostRecentCall.object).toBeDefined();
});
Expand All @@ -38,25 +47,29 @@ describe("Cucumber.SupportCode.Library", function() {
supportCodeHelper = rawSupportCode.mostRecentCall.object;
});

it("supplies a method to define Given steps", function() {
it("exposes a method to define Given steps", function() {
expect(supportCodeHelper.Given).toBeAFunction();
expect(supportCodeHelper.Given).toBe(library.defineStep);
});

it("supplies a method to define When steps", function() {
it("exposes a method to define When steps", function() {
expect(supportCodeHelper.When).toBeAFunction();
expect(supportCodeHelper.When).toBe(library.defineStep);
});

it("supplies a method to define Then steps", function() {
it("exposes a method to define Then steps", function() {
expect(supportCodeHelper.Then).toBeAFunction();
expect(supportCodeHelper.Then).toBe(library.defineStep);
});

it("supplies a method to define any step", function() {
it("exposes a method to define any step", function() {
expect(supportCodeHelper.defineStep).toBeAFunction();
expect(supportCodeHelper.defineStep).toBe(library.defineStep);
});

it("exposes the World constructor", function() {
expect(supportCodeHelper.World).toBe(worldConstructor);
});
});
});

Expand Down Expand Up @@ -140,5 +153,17 @@ describe("Cucumber.SupportCode.Library", function() {
expect(stepDefinitionCollection.add).toHaveBeenCalledWith(stepDefinition);
});
});

describe("instantiateNewWorld()", function() {
it("creates a new instance of the World", function() {
library.instantiateNewWorld();
expect(worldConstructorCalled).toBeTruthy();
});

it("returns the instance of the World", function() {
var worldInstance = library.instantiateNewWorld();
expect(worldInstance.constructor).toBe(worldConstructor);
});
});
});

Loading

0 comments on commit e096ece

Please sign in to comment.