cucumber
Version:
The official JavaScript implementation of Cucumber.
369 lines (302 loc) • 13.5 kB
JavaScript
require('../../support/spec_helper');
describe("Cucumber.SupportCode.Library", function() {
var Cucumber = requireLib('cucumber');
var library, rawSupportCode, hooker;
var stepDefinitionCollection;
var worldConstructor;
beforeEach(function() {
rawSupportCode = createSpy("Raw support code");
stepDefinitionCollection = [
createSpyWithStubs("First step definition", {matchesStepName:false}),
createSpyWithStubs("Second step definition", {matchesStepName:false}),
createSpyWithStubs("Third step definition", {matchesStepName:false})
];
hooker = createSpyWithStubs("hooker");
worldConstructor = createSpy("world constructor");
spyOnStub(stepDefinitionCollection, 'syncForEach').andCallFake(function(cb) { stepDefinitionCollection.forEach(cb); });
spyOn(Cucumber.Type, 'Collection').andReturn(stepDefinitionCollection);
spyOn(Cucumber.SupportCode.Library, 'Hooker').andReturn(hooker);
spyOn(Cucumber.SupportCode, 'WorldConstructor').andReturn(worldConstructor);
library = Cucumber.SupportCode.Library(rawSupportCode);
});
describe("constructor", function() {
it("creates a collection of step definitions", function() {
expect(Cucumber.Type.Collection).toHaveBeenCalled();
});
it("instantiates a hooker", function() {
expect(Cucumber.SupportCode.Library.Hooker).toHaveBeenCalled();
});
it("executes the raw support code", 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();
});
describe("code support helper", function() {
var supportCodeHelper;
beforeEach(function() {
supportCodeHelper = rawSupportCode.mostRecentCall.object;
});
it("exposes a method to define Around hooks", function() {
expect(supportCodeHelper.Around).toBeAFunction();
expect(supportCodeHelper.Around).toBe(library.defineAroundHook);
});
it("exposes a method to define Before hooks", function() {
expect(supportCodeHelper.Before).toBeAFunction();
expect(supportCodeHelper.Before).toBe(library.defineBeforeHook);
});
it("exposes a method to define After hooks", function() {
expect(supportCodeHelper.After).toBeAFunction();
expect(supportCodeHelper.After).toBe(library.defineAfterHook);
});
it("exposes a method to define Given steps", function() {
expect(supportCodeHelper.Given).toBeAFunction();
expect(supportCodeHelper.Given).toBe(library.defineStep);
});
it("exposes a method to define When steps", function() {
expect(supportCodeHelper.When).toBeAFunction();
expect(supportCodeHelper.When).toBe(library.defineStep);
});
it("exposes a method to define Then steps", function() {
expect(supportCodeHelper.Then).toBeAFunction();
expect(supportCodeHelper.Then).toBe(library.defineStep);
});
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);
});
});
});
describe("lookupStepDefinitionByName()", function() {
var stepName;
beforeEach(function() {
stepName = createSpy("Step name");
});
it("asks each step definition in the library if they match the step name", function() {
library.lookupStepDefinitionByName(stepName);
stepDefinitionCollection.forEach(function(stepDefinition) {
expect(stepDefinition.matchesStepName).toHaveBeenCalledWith(stepName);
});
});
it("returns the step definition that matches the name", function() {
var matchingStepDefinition = stepDefinitionCollection[1];
matchingStepDefinition.matchesStepName.andReturn(true);
expect(library.lookupStepDefinitionByName(stepName)).toBe(matchingStepDefinition);
});
});
describe("isStepDefinitionNameDefined()", function() {
var name;
beforeEach(function() {
name = createSpy("step name");
spyOn(library, 'lookupStepDefinitionByName');
});
it("looks up the step definition by the name", function() {
library.isStepDefinitionNameDefined(name);
expect(library.lookupStepDefinitionByName).toHaveBeenCalledWith(name);
});
describe("when a step definition is found", function() {
var stepDefinition;
beforeEach(function() {
stepDefinition = createSpy("step definition");
library.lookupStepDefinitionByName.andReturn(stepDefinition);
});
it("returns true", function() {
expect(library.isStepDefinitionNameDefined(name)).toBeTruthy();
});
});
describe("when no step definition is found", function() {
beforeEach(function() {
library.lookupStepDefinitionByName.andReturn(undefined);
});
it("returns false", function() {
expect(library.isStepDefinitionNameDefined(name)).toBeFalsy();
});
});
});
describe("hookUpFunction()", function() {
var userFunction, scenario, world, hookedUpFunction;
beforeEach(function() {
userFunction = createSpy("user function");
hookedUpFunction = createSpy("hooked up function");
scenario = createSpy("scenario");
world = createSpy("world instance");
spyOnStub(hooker, 'hookUpFunction').andReturn(hookedUpFunction);
});
it("hooks up the function with the world instance", function() {
library.hookUpFunction(userFunction, scenario, world);
expect(hooker.hookUpFunction).toHaveBeenCalledWith(userFunction, scenario, world);
});
it("returns the hooked up function", function() {
expect(library.hookUpFunction(userFunction, scenario, world)).toBe(hookedUpFunction);
});
});
describe("defineAroundHook()", function() {
var code;
beforeEach(function() {
code = createSpy("hook code");
spyOnStub(hooker, 'addAroundHookCode');
});
it("instructs the hooker to use the code as an around hook", function() {
library.defineAroundHook(code);
expect(hooker.addAroundHookCode).toHaveBeenCalledWith(code, {tags: []});
});
it("instructs the hooker to use the code as an around hook with a tag group", function() {
var tagGroup = createSpy("tag group");
library.defineAroundHook(tagGroup, code);
expect(hooker.addAroundHookCode).toHaveBeenCalledWith(code, {tags: [tagGroup]});
});
it("instructs the hooker to use the code as an around hook with tag groups", function() {
var tagGroup1 = createSpy("tag group 1");
var tagGroup2 = createSpy("tag group 2");
library.defineAroundHook(tagGroup1, tagGroup2, code);
expect(hooker.addAroundHookCode).toHaveBeenCalledWith(code, {tags: [tagGroup1, tagGroup2]});
});
});
describe("defineBeforeHook()", function() {
var code;
beforeEach(function() {
code = createSpy("hook code");
spyOnStub(hooker, 'addBeforeHookCode');
});
it("instructs the hooker to use the code as an before hook", function() {
library.defineBeforeHook(code);
expect(hooker.addBeforeHookCode).toHaveBeenCalledWith(code, {tags: []});
});
it("instructs the hooker to use the code as an before hook with a tag group", function() {
var tagGroup = createSpy("tag group");
library.defineBeforeHook(tagGroup, code);
expect(hooker.addBeforeHookCode).toHaveBeenCalledWith(code, {tags: [tagGroup]});
});
it("instructs the hooker to use the code as an before hook with tag groups", function() {
var tagGroup1 = createSpy("tag group 1");
var tagGroup2 = createSpy("tag group 2");
library.defineBeforeHook(tagGroup1, tagGroup2, code);
expect(hooker.addBeforeHookCode).toHaveBeenCalledWith(code, {tags: [tagGroup1, tagGroup2]});
});
});
describe("defineAfterHook()", function() {
var code;
beforeEach(function() {
code = createSpy("hook code");
spyOnStub(hooker, 'addAfterHookCode');
});
it("instructs the hooker to use the code as an after hook", function() {
library.defineAfterHook(code);
expect(hooker.addAfterHookCode).toHaveBeenCalledWith(code, {tags: []});
});
it("instructs the hooker to use the code as an after hook with a tag group", function() {
var tagGroup = createSpy("tag group");
library.defineAfterHook(tagGroup, code);
expect(hooker.addAfterHookCode).toHaveBeenCalledWith(code, {tags: [tagGroup]});
});
it("instructs the hooker to use the code as an after hook with tag groups", function() {
var tagGroup1 = createSpy("tag group 1");
var tagGroup2 = createSpy("tag group 2");
library.defineAfterHook(tagGroup1, tagGroup2, code);
expect(hooker.addAfterHookCode).toHaveBeenCalledWith(code, {tags: [tagGroup1, tagGroup2]});
});
});
describe("defineStep()", function() {
var name, code, stepDefinition;
beforeEach(function() {
name = createSpy("step definition name");
code = createSpy("step definition code");
stepDefinition = createSpy("step definition");
spyOn(Cucumber.SupportCode, 'StepDefinition').andReturn(stepDefinition);
spyOnStub(stepDefinitionCollection, 'add');
});
it("creates a step definition with the name and code", function() {
library.defineStep(name, code);
expect(Cucumber.SupportCode.StepDefinition).toHaveBeenCalledWith(name, code);
});
it("adds the step definition to the step collection", function() {
library.defineStep(name, code);
expect(stepDefinitionCollection.add).toHaveBeenCalledWith(stepDefinition);
});
});
describe("instantiateNewWorld()", function() {
var worldConstructorThis, callback;
beforeEach(function() {
worldConstructorThis = null;
worldConstructor.andCallFake(function(callback) {
worldConstructorThis = this;
if (callback)
callback(this);
});
callback = createSpy("callback");
});
it("creates a new instance of the World and give it a callback", function() {
library.instantiateNewWorld(callback);
expect(worldConstructor).toHaveBeenCalled();
expect(worldConstructor).toHaveBeenCalledWithAFunctionAsNthParameter(1);
expect(worldConstructorThis.constructor).toBe(worldConstructor);
});
describe("world constructor callback", function() {
var worldConstructorCompletionCallback, world;
beforeEach(function() {
library.instantiateNewWorld(callback);
worldConstructorCompletionCallback = worldConstructor.mostRecentCall.args[0];
spyOn(process, 'nextTick');
})
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);
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("throws an exception", function() {
var expectedError = new Error("World constructor called back without World instance.");
expect(function() { worldConstructorCompletionCallback(null); }).toThrow(expectedError);
});
});
});
describe("when the default World constructor is replaced by a custom one", function() {
it("instantiates a custom World", function() {
var worldInstance;
var worldReady = false;
var customWorldConstructor = function(callback) {
callback(this);
};
rawSupportCode = function() { this.World = customWorldConstructor; };
library = Cucumber.SupportCode.Library(rawSupportCode);
runs(function() {
library.instantiateNewWorld(function(world) {
worldInstance = world;
worldReady = true;
});
});
waitsFor(function() {
return worldReady;
}, "world instance constructor", 300);
runs(function() {
expect(worldInstance.constructor).toBe(customWorldConstructor);
});
});
});
});
});