From 4be20a5211bd213dd1e110fc126a9e027e635f6e Mon Sep 17 00:00:00 2001 From: Paul Shannon Date: Thu, 1 Aug 2013 13:21:13 -0700 Subject: [PATCH] Add listener and event handler registration (close #130) This introduces registerListener and registerEventHandler to the support code helper. Those two methods let clients register custom listeners for AST events. Relates to #91. --- lib/cucumber/listener.js | 16 +- lib/cucumber/listener/events.js | 10 + lib/cucumber/runtime/ast_tree_walker.js | 18 +- lib/cucumber/support_code/library.js | 54 +- package.json | 3 +- spec/cucumber/listener/events_spec.js | 20 + spec/cucumber/listener_spec.js | 39 +- spec/cucumber/runtime/ast_tree_walker_spec.js | 30 +- spec/cucumber/support_code/library_spec.js | 510 +++++++++++------- 9 files changed, 469 insertions(+), 231 deletions(-) create mode 100644 lib/cucumber/listener/events.js create mode 100644 spec/cucumber/listener/events_spec.js diff --git a/lib/cucumber/listener.js b/lib/cucumber/listener.js index bf04e36c8..309f38f56 100644 --- a/lib/cucumber/listener.js +++ b/lib/cucumber/listener.js @@ -15,16 +15,21 @@ var Listener = function () { }, buildHandlerNameForEvent: function buildHandlerNameForEvent(event) { - var handlerName = - Listener.EVENT_HANDLER_NAME_PREFIX + - event.getName() + - Listener.EVENT_HANDLER_NAME_SUFFIX; - return handlerName; + return self.buildHandlerName(event.getName()); }, getHandlerForEvent: function getHandlerForEvent(event) { var eventHandlerName = self.buildHandlerNameForEvent(event); return self[eventHandlerName]; + }, + + buildHandlerName: function buildHandler(shortName) { + return Listener.EVENT_HANDLER_NAME_PREFIX + shortName + Listener.EVENT_HANDLER_NAME_SUFFIX; + }, + + setHandlerForEvent: function setHandlerForEvent(shortname, handler) { + var eventName = self.buildHandlerName(shortname); + self[eventName] = handler; } }; return self; @@ -33,6 +38,7 @@ var Listener = function () { Listener.EVENT_HANDLER_NAME_PREFIX = 'handle'; Listener.EVENT_HANDLER_NAME_SUFFIX = 'Event'; +Listener.Events = require('./listener/events'); Listener.Formatter = require('./listener/formatter'); Listener.PrettyFormatter = require('./listener/pretty_formatter'); Listener.ProgressFormatter = require('./listener/progress_formatter'); diff --git a/lib/cucumber/listener/events.js b/lib/cucumber/listener/events.js new file mode 100644 index 000000000..4fd5d984c --- /dev/null +++ b/lib/cucumber/listener/events.js @@ -0,0 +1,10 @@ +exports['BeforeFeatures'] = 'BeforeFeatures'; +exports['BeforeFeature'] = 'BeforeFeature'; +exports['Background'] = 'Background'; +exports['BeforeScenario'] = 'BeforeScenario'; +exports['BeforeStep'] = 'BeforeStep'; +exports['StepResult'] = 'StepResult'; +exports['AfterStep'] = 'AfterStep'; +exports['AfterScenario'] = 'AfterScenario'; +exports['AfterFeature'] = 'AfterFeature'; +exports['AfterFeatures'] = 'AfterFeatures'; \ No newline at end of file diff --git a/lib/cucumber/runtime/ast_tree_walker.js b/lib/cucumber/runtime/ast_tree_walker.js index 4da776fd7..d3fd865b5 100644 --- a/lib/cucumber/runtime/ast_tree_walker.js +++ b/lib/cucumber/runtime/ast_tree_walker.js @@ -1,7 +1,6 @@ var AstTreeWalker = function(features, supportCodeLibrary, listeners) { var Cucumber = require('../../cucumber'); - var listeners; var world; var allFeaturesSucceded = true; var skippingSteps = false; @@ -107,10 +106,19 @@ var AstTreeWalker = function(features, supportCodeLibrary, listeners) { }, broadcastEvent: function broadcastEvent(event, callback) { - listeners.forEach( - function(listener, callback) { listener.hear(event, callback); }, - callback - ); + broadcastToListners(listeners, onRuntimeListenersComplete); + + function onRuntimeListenersComplete() { + var listeners = supportCodeLibrary.getListeners(); + broadcastToListners(listeners, callback); + } + + function broadcastToListners(listeners, callback) { + listeners.forEach( + function(listener, callback) { listener.hear(event, callback); }, + callback + ); + } }, lookupStepDefinitionByName: function lookupStepDefinitionByName(stepName) { diff --git a/lib/cucumber/support_code/library.js b/lib/cucumber/support_code/library.js index 501e627b5..825b88bfb 100644 --- a/lib/cucumber/support_code/library.js +++ b/lib/cucumber/support_code/library.js @@ -1,6 +1,7 @@ var Library = function(supportCodeDefinition) { var Cucumber = require('../../cucumber'); + var listeners = Cucumber.Type.Collection(); var stepDefinitions = Cucumber.Type.Collection(); var hooker = Cucumber.SupportCode.Library.Hooker(); var worldConstructor = Cucumber.SupportCode.WorldConstructor(); @@ -50,6 +51,20 @@ var Library = function(supportCodeDefinition) { stepDefinitions.add(stepDefinition); }, + registerListener: function registerListener(listener) { + listeners.add(listener); + }, + + registerHandler: function registerHandler(eventName, handler) { + var listener = Cucumber.Listener(); + listener.setHandlerForEvent(eventName, handler); + self.registerListener(listener); + }, + + getListeners: function getListeners() { + return listeners; + }, + instantiateNewWorld: function instantiateNewWorld(callback) { var world = new worldConstructor(function(explicitWorld) { process.nextTick(function() { // release the constructor @@ -60,19 +75,42 @@ var Library = function(supportCodeDefinition) { }; var supportCodeHelper = { - Around : self.defineAroundHook, - Before : self.defineBeforeHook, - After : self.defineAfterHook, - Given : self.defineStep, - When : self.defineStep, - Then : self.defineStep, - defineStep : self.defineStep, - World : worldConstructor + Around : self.defineAroundHook, + Before : self.defineBeforeHook, + After : self.defineAfterHook, + Given : self.defineStep, + When : self.defineStep, + Then : self.defineStep, + defineStep : self.defineStep, + registerListener : self.registerListener, + registerHandler : self.registerHandler, + World : worldConstructor }; + + appendEventHandlers(supportCodeHelper, self); supportCodeDefinition.call(supportCodeHelper); worldConstructor = supportCodeHelper.World; return self; }; + +function appendEventHandlers(supportCodeHelper, library) { + var Cucumber = require('../../cucumber'); + var events = Cucumber.Listener.Events; + var eventName; + + for (eventName in events) { + if (events.hasOwnProperty(eventName)) { + supportCodeHelper[eventName] = createEventListenerMethod(library, eventName); + } + } +} + +function createEventListenerMethod(library, eventName) { + return function(handler) { + library.registerHandler(eventName, handler); + }; +} + Library.Hooker = require('./library/hooker'); module.exports = Library; diff --git a/package.json b/package.json index 250d9fe00..31ced9176 100644 --- a/package.json +++ b/package.json @@ -30,7 +30,8 @@ "Kim, Jang-hwan ", "Michael Zedeler ", "Tom V ", - "David Godfrey " + "David Godfrey ", + "Paul Shannon (http://devpaul.com)" ], "repository": { "type": "git", diff --git a/spec/cucumber/listener/events_spec.js b/spec/cucumber/listener/events_spec.js new file mode 100644 index 000000000..3498d0a4d --- /dev/null +++ b/spec/cucumber/listener/events_spec.js @@ -0,0 +1,20 @@ +require('../../support/spec_helper'); + +describe('Cucumber.Listener.Events', function () { + var Cucumber = requireLib('cucumber'); + var events = Cucumber.Listener.Events; + + describe('construction', function () { + it("contains a list of event names", function () { + for(var name in events) { + if(events.hasOwnProperty(name)) { + expect(events[name]).toEqual(name); + } + } + }); + + it("is defined", function() { + expect(events).toBeDefined(); + }); + }); +}); \ No newline at end of file diff --git a/spec/cucumber/listener_spec.js b/spec/cucumber/listener_spec.js index 94578a41d..6db14ff26 100644 --- a/spec/cucumber/listener_spec.js +++ b/spec/cucumber/listener_spec.js @@ -2,6 +2,7 @@ require('../support/spec_helper'); describe("Cucumber.Listener", function() { var Cucumber = requireLib('cucumber'); + var listener; beforeEach(function() { listener = Cucumber.Listener(); @@ -96,11 +97,13 @@ describe("Cucumber.Listener", function() { }); describe("buildHandlerNameForEvent", function () { - var event, eventName; + var event, eventName, buildHandlerName; beforeEach(function () { eventName = "SomeEventName"; event = createSpyWithStubs("Event", {getName: eventName}); + buildHandlerName = spyOn(listener, "buildHandlerName"); + }); it("gets the name of the event", function () { @@ -108,8 +111,9 @@ describe("Cucumber.Listener", function() { expect(event.getName).toHaveBeenCalled(); }); - it("returns the name of the event with prefix 'handle' and suffix 'Event'", function () { - expect(listener.buildHandlerNameForEvent(event)).toBe("handle" + eventName + "Event"); + it("calls buildHandlerName", function() { + listener.buildHandlerNameForEvent(event); + expect(buildHandlerName).toHaveBeenCalled(); }); }); @@ -145,4 +149,33 @@ describe("Cucumber.Listener", function() { }); }); }); + + describe("buildHandlerName", function() { + it("returns the name of the event with prefix 'handle' and suffix 'Event'", function () { + var eventName = "shortName"; + var expected = "handle" + eventName + "Event"; + + expect(listener.buildHandlerName(eventName)).toBe(expected); + }); + }); + + describe("setHandlerForEvent", function() { + var shortName = "anEventName"; + var handler = function(){}; + var buildHandlerName; + + beforeEach(function() { + buildHandlerName = spyOn(listener, "buildHandlerName").andCallThrough(); + listener.setHandlerForEvent(shortName, handler); + }); + + it("attaches the function as a property to itself", function() { + var expectedKey = Cucumber.Listener.EVENT_HANDLER_NAME_PREFIX + shortName + Cucumber.Listener.EVENT_HANDLER_NAME_SUFFIX; + expect(listener[expectedKey]).toBe(handler); + }); + + it("calls buildHandlerName", function() { + expect(buildHandlerName).toHaveBeenCalled(); + }); + }); }); diff --git a/spec/cucumber/runtime/ast_tree_walker_spec.js b/spec/cucumber/runtime/ast_tree_walker_spec.js index 88f903d4e..84027d432 100644 --- a/spec/cucumber/runtime/ast_tree_walker_spec.js +++ b/spec/cucumber/runtime/ast_tree_walker_spec.js @@ -2,13 +2,16 @@ require('../../support/spec_helper'); describe("Cucumber.Runtime.AstTreeWalker", function() { var Cucumber = requireLib('cucumber'); - var treeWalker, features, supportCodeLibrary, listeners; + var treeWalker, features, supportCodeLibrary, listeners, supportListeners; beforeEach(function() { features = createSpyWithStubs("Features AST element", {acceptVisitor: null}); supportCodeLibrary = createSpy("Support code library"); listeners = [createSpy("First listener"), createSpy("Second listener")]; + supportListeners = [createSpy("First support listener"), createSpy("Second support listener")]; spyOnStub(listeners, 'syncForEach').andCallFake(function(cb) { listeners.forEach(cb); }); + spyOnStub(supportListeners, 'syncForEach').andCallFake(function(cb) { supportListeners.forEach(cb); }); + spyOnStub(supportCodeLibrary, 'getListeners').andCallFake(function() { return supportListeners; }); treeWalker = Cucumber.Runtime.AstTreeWalker(features, supportCodeLibrary, listeners); }); @@ -447,7 +450,7 @@ describe("Cucumber.Runtime.AstTreeWalker", function() { beforeEach(function() { wrapper = treeWalker.wrapAfterEventBroadcast(event, callback); - spyOn(treeWalker, 'broadcastAfterEvent');; + spyOn(treeWalker, 'broadcastAfterEvent'); }); it("broadcasts an after event with the received callback as callback", function() { @@ -501,21 +504,34 @@ describe("Cucumber.Runtime.AstTreeWalker", function() { describe("broadcastEvent()", function() { - var event, eventName, callback; + var event, callback; beforeEach(function() { event = createSpy("Event"); callback = createSpy("Callback"); - spyOn(listeners, 'forEach'); + spyOnListeners(listeners); + spyOnListeners(supportListeners); }); + function spyOnListeners(listeners) { + spyOn(listeners, 'forEach').andCallFake(function() { + var callback = listeners.forEach.mostRecentCall.args[1]; + callback(); + }); + } + it("iterates over the listeners", function() { treeWalker.broadcastEvent(event, callback); - expect(listeners.forEach).toHaveBeenCalled(); - expect(listeners.forEach).toHaveBeenCalledWithAFunctionAsNthParameter(1); - expect(listeners.forEach).toHaveBeenCalledWithValueAsNthParameter(callback, 2); + assertListenerCollectionCalled(listeners.forEach); + assertListenerCollectionCalled(supportListeners.forEach); + expect(supportListeners.forEach).toHaveBeenCalledWithValueAsNthParameter(callback, 2); }); + function assertListenerCollectionCalled(forEachSpy) { + expect(forEachSpy).toHaveBeenCalled(); + expect(forEachSpy).toHaveBeenCalledWithAFunctionAsNthParameter(1); + } + describe("for each listener", function() { var userFunction, listener, forEachCallback; diff --git a/spec/cucumber/support_code/library_spec.js b/spec/cucumber/support_code/library_spec.js index 1f93c5385..6a79854f1 100644 --- a/spec/cucumber/support_code/library_spec.js +++ b/spec/cucumber/support_code/library_spec.js @@ -3,26 +3,22 @@ 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() { + beforeEach(function() { + spyOn(Cucumber.Type, 'Collection'); + library = Cucumber.SupportCode.Library(rawSupportCode); + }); + it("creates a collection of step definitions", function() { expect(Cucumber.Type.Collection).toHaveBeenCalled(); }); @@ -88,281 +84,391 @@ describe("Cucumber.SupportCode.Library", function() { it("exposes the World constructor", function() { expect(supportCodeHelper.World).toBe(worldConstructor); }); - }); - }); - - describe("lookupStepDefinitionByName()", function() { - var stepName; - beforeEach(function() { - stepName = createSpy("Step name"); - }); + it("exposes a method to register a listener", function() { + expect(supportCodeHelper.registerListener).toBeAFunction(); + }); - 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("exposes a method to register a handler", function() { + expect(supportCodeHelper.registerHandler).toBeAFunction(); }); - }); - it("returns the step definition that matches the name", function() { - var matchingStepDefinition = stepDefinitionCollection[1]; - matchingStepDefinition.matchesStepName.andReturn(true); - expect(library.lookupStepDefinitionByName(stepName)).toBe(matchingStepDefinition); + // parameterized test + for(eventName in Cucumber.Listener.Events) { + if(!Cucumber.Listener.Events.hasOwnProperty(eventName)) + continue; + + describe(eventName + ' event register handler method', function() { + beforeEach(function() { + spyOn(library, 'registerHandler'); + }); + + it("is defined as a function", function() { + expect(supportCodeHelper[eventName]).toBeAFunction(); + }); + + it("calls registerHandler with the eventName", function() { + var handler = createSpy('handler'); + supportCodeHelper[eventName](handler); + expect(library.registerHandler).toHaveBeenCalled(); + expect(library.registerHandler).toHaveBeenCalledWithValueAsNthParameter(eventName, 1); + expect(library.registerHandler).toHaveBeenCalledWithValueAsNthParameter(handler, 2); + }); + }); + } }); }); - describe("isStepDefinitionNameDefined()", function() { - var name; + describe('Step Definitions', function() { + var stepDefinitionCollection; beforeEach(function() { - name = createSpy("step name"); - spyOn(library, 'lookupStepDefinitionByName'); + stepDefinitionCollection = [ + createSpyWithStubs("First step definition", {matchesStepName:false}), + createSpyWithStubs("Second step definition", {matchesStepName:false}), + createSpyWithStubs("Third step definition", {matchesStepName:false}) + ]; + spyOnStub(stepDefinitionCollection, 'syncForEach').andCallFake(function(cb) { stepDefinitionCollection.forEach(cb); }); + spyOn(Cucumber.Type, 'Collection').andReturn(stepDefinitionCollection); + library = Cucumber.SupportCode.Library(rawSupportCode); }); - 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; + describe("lookupStepDefinitionByName()", function() { + var stepName; beforeEach(function() { - stepDefinition = createSpy("step definition"); - library.lookupStepDefinitionByName.andReturn(stepDefinition); + 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 true", function() { - expect(library.isStepDefinitionNameDefined(name)).toBeTruthy(); + 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("when no step definition is found", function() { + describe("isStepDefinitionNameDefined()", function() { + var name; + beforeEach(function() { - library.lookupStepDefinitionByName.andReturn(undefined); + name = createSpy("step name"); + spyOn(library, 'lookupStepDefinitionByName'); }); - it("returns false", function() { - expect(library.isStepDefinitionNameDefined(name)).toBeFalsy(); + it("looks up the step definition by the name", function() { + library.isStepDefinitionNameDefined(name); + expect(library.lookupStepDefinitionByName).toHaveBeenCalledWith(name); }); - }); - }); - describe("hookUpFunction()", function() { - var userFunction, scenario, world, hookedUpFunction; + describe("when a step definition is found", function() { + var stepDefinition; - beforeEach(function() { - userFunction = createSpy("user function"); - hookedUpFunction = createSpy("hooked up function"); - scenario = createSpy("scenario"); - world = createSpy("world instance"); - spyOnStub(hooker, 'hookUpFunction').andReturn(hookedUpFunction); - }); + beforeEach(function() { + stepDefinition = createSpy("step definition"); + library.lookupStepDefinitionByName.andReturn(stepDefinition); + }); + + it("returns true", function() { + expect(library.isStepDefinitionNameDefined(name)).toBeTruthy(); + }); + }); - it("hooks up the function with the world instance", function() { - library.hookUpFunction(userFunction, scenario, world); - expect(hooker.hookUpFunction).toHaveBeenCalledWith(userFunction, scenario, world); + describe("when no step definition is found", function() { + beforeEach(function() { + library.lookupStepDefinitionByName.andReturn(undefined); + }); + + it("returns false", function() { + expect(library.isStepDefinitionNameDefined(name)).toBeFalsy(); + }); + }); }); - it("returns the hooked up function", function() { - expect(library.hookUpFunction(userFunction, scenario, world)).toBe(hookedUpFunction); + 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("defineAroundHook()", function() { - var code; + describe('Listener Support', function() { + var listeners; beforeEach(function() { - code = createSpy("hook code"); - spyOnStub(hooker, 'addAroundHookCode'); + listeners = [ + createSpyWithStubs("First listener", {setHandlerForEvent: null}), + createSpyWithStubs("Second listener", {setHandlerForEvent: null}), + createSpyWithStubs("Third listener", {setHandlerForEvent: null}) + ]; + spyOn(Cucumber.Type, 'Collection').andReturn(listeners); + spyOnStub(listeners, 'add'); + library = Cucumber.SupportCode.Library(rawSupportCode); + }); + + describe('getListeners()', function() { + it("returns a listener collection", function() { + var listeners = library.getListeners(); + expect(listeners).toBeDefined(); + }); }); - it("instructs the hooker to use the code as an around hook", function() { - library.defineAroundHook(code); - expect(hooker.addAroundHookCode).toHaveBeenCalledWith(code, {tags: []}); + describe("registerListener()", function() { + it("adds the listener to the listener collection", function() { + var listener = createSpy('sample listener'); + library.registerListener(listener); + expect(listeners.add).toHaveBeenCalledWith(listener); + }) }); - 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]}); - }); + describe('registerHandler()', function() { + var eventName, handler, listener; + + beforeEach(function() { + eventName = 'eventName'; + handler = createSpy('sampleHandler'); + listener = createSpyWithStubs("listener", {setHandlerForEvent: null}); + spyOn(Cucumber, 'Listener').andReturn(listener); + library.registerHandler(eventName, handler); + }); + + it('creates a listener to the listener collection', function() { + expect(listener.setHandlerForEvent).toHaveBeenCalledWithValueAsNthParameter(eventName, 1); + expect(listener.setHandlerForEvent).toHaveBeenCalledWithValueAsNthParameter(handler, 2); + }); - 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]}); + it("adds the listener to the listener collection", function() { + expect(listeners.add).toHaveBeenCalled(); + }); }); }); - describe("defineBeforeHook()", function() { - var code; - + describe('Hook Methods', function() { beforeEach(function() { - code = createSpy("hook code"); - spyOnStub(hooker, 'addBeforeHookCode'); + library = Cucumber.SupportCode.Library(rawSupportCode); }); - it("instructs the hooker to use the code as an before hook", function() { - library.defineBeforeHook(code); - expect(hooker.addBeforeHookCode).toHaveBeenCalledWith(code, {tags: []}); - }); + describe("hookUpFunction()", function() { + var userFunction, scenario, world, hookedUpFunction; - 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]}); - }); + 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("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]}); + it("returns the hooked up function", function() { + expect(library.hookUpFunction(userFunction, scenario, world)).toBe(hookedUpFunction); + }); }); - }); - describe("defineAfterHook()", function() { - var code; + describe("defineAroundHook()", function() { + var code; - beforeEach(function() { - code = createSpy("hook code"); - spyOnStub(hooker, 'addAfterHookCode'); - }); + beforeEach(function() { + code = createSpy("hook code"); + spyOnStub(hooker, 'addAroundHookCode'); + }); - 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 around hook", function() { + library.defineAroundHook(code); + expect(hooker.addAroundHookCode).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 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 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]}); + 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("defineStep()", function() { - var name, code, stepDefinition; + describe("defineBeforeHook()", function() { + var code; - 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'); - }); + beforeEach(function() { + code = createSpy("hook code"); + spyOnStub(hooker, 'addBeforeHookCode'); + }); - it("creates a step definition with the name and code", function() { - library.defineStep(name, code); - expect(Cucumber.SupportCode.StepDefinition).toHaveBeenCalledWith(name, code); - }); + it("instructs the hooker to use the code as an before hook", function() { + library.defineBeforeHook(code); + expect(hooker.addBeforeHookCode).toHaveBeenCalledWith(code, {tags: []}); + }); - it("adds the step definition to the step collection", function() { - library.defineStep(name, code); - expect(stepDefinitionCollection.add).toHaveBeenCalledWith(stepDefinition); + 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("instantiateNewWorld()", function() { - var worldInstance, callback; + describe("defineAfterHook()", function() { + var code; - beforeEach(function() { - worldInstance = null; - worldConstructor.andCallFake(function(callback) { - worldInstance = this; - if (callback) - callback(); + 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]}); }); - 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(worldInstance.constructor).toBe(worldConstructor); + describe('World construction', function() { + beforeEach(function() { + library = Cucumber.SupportCode.Library(rawSupportCode); }); - describe("world constructor callback", function() { - var worldConstructorCompletionCallback; + describe("instantiateNewWorld()", function() { + var worldInstance, callback; beforeEach(function() { - library.instantiateNewWorld(callback); - worldConstructorCompletionCallback = worldConstructor.mostRecentCall.args[0]; - spyOn(process, 'nextTick'); - }) + worldInstance = null; + worldConstructor.andCallFake(function(callback) { + worldInstance = this; + if (callback) + callback(); + }); + callback = createSpy("callback"); + }); - it("registers a function for the next tick (to get out of the constructor call)", function() { - worldConstructorCompletionCallback(); - expect(process.nextTick).toHaveBeenCalledWithAFunctionAsNthParameter(1); + 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(worldInstance.constructor).toBe(worldConstructor); }); - describe("next tick registered function", function() { - var nextTickFunction; + describe("world constructor callback", function() { + var worldConstructorCompletionCallback; - describe("when the world constructor called back without any argument", function() { - beforeEach(function() { - worldConstructorCompletionCallback(); - nextTickFunction = process.nextTick.mostRecentCall.args[0]; - }); + beforeEach(function() { + library.instantiateNewWorld(callback); + worldConstructorCompletionCallback = worldConstructor.mostRecentCall.args[0]; + spyOn(process, 'nextTick'); + }); - it("calls back with the world instance", function() { - nextTickFunction(); - expect(callback).toHaveBeenCalledWith(worldInstance); - }); + it("registers a function for the next tick (to get out of the constructor call)", function() { + worldConstructorCompletionCallback(); + expect(process.nextTick).toHaveBeenCalledWithAFunctionAsNthParameter(1); }); - describe("when the world constructor called back with an explicit world object", function() { - var explicitWorld; + describe("next tick registered function", function() { + var nextTickFunction; - beforeEach(function() { - explicitWorld = createSpy("explicit world object"); - worldConstructorCompletionCallback(explicitWorld); - 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(explicitWorld); + 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); + }); }); - }); + }); }); }); - }); - 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(); - }; - rawSupportCode = function() { this.World = customWorldConstructor; }; - library = Cucumber.SupportCode.Library(rawSupportCode); - - runs(function() { - library.instantiateNewWorld(function(world) { - worldInstance = world; - worldReady = true; + 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(); + }; + 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); }); - }); - waitsFor(function() { - return worldReady; - }, "world instance constructor", 300); - runs(function() { - expect(worldInstance.constructor).toBe(customWorldConstructor); }); }); }); -}); +}); \ No newline at end of file