Skip to content

Commit

Permalink
Broadcast asynchronous messages with callbacks to tree walker listeners
Browse files Browse the repository at this point in the history
Squashed commit of the following:

commit d84be27d69143579119e245a36dffb4180bf1b59
Author: Julien Biezemans <jb@jbpros.com>
Date:   Tue Jun 14 18:45:56 2011 +0200

    Clean up commented code

commit 35c6787b42b0952d246d7b639dfc6088cb80041e
Author: Julien Biezemans <jb@jbpros.com>
Date:   Tue Jun 14 18:44:45 2011 +0200

    Add TreeWalker.broadcastAfterEvent() and Event.replicateAsPostEvent()

commit bdf5f070e7cffe0bcc40120d96e9069cb602b2c6
Author: Julien Biezemans <jb@jbpros.com>
Date:   Tue Jun 14 18:35:20 2011 +0200

    Add Event.getName() and Event.getPayloadItem()

commit 7bc8fd6aab5946370a7ce01c3627b81de79a0193
Author: Julien Biezemans <jb@jbpros.com>
Date:   Tue Jun 14 17:59:18 2011 +0200

    Broadcast Event objects in TreeWalker.broadcastEvent() instead of a variable number of arbitrary parameters

commit 5da22b354e48b7dbef1f7996e46a250d190ade87
Author: Julien Biezemans <jb@jbpros.com>
Date:   Tue Jun 14 17:21:25 2011 +0200

    Add TreeWalker.broadcastBeforeEvent() and Event.replicateAsPreEvent()

commit 3d98b6601d0ef978022536abf632eb5f08ac38a4
Author: Julien Biezemans <jb@jbpros.com>
Date:   Tue Jun 14 14:46:58 2011 +0200

    Use Event objects instead of implicit parameters

commit 8f77702
Author: Julien Biezemans <jb@jbpros.com>
Date:   Wed Jun 8 12:31:22 2011 +0200

    Rename Message to Event

commit a164205
Author: Julien Biezemans <jb@jbpros.com>
Date:   Wed Jun 8 12:00:44 2011 +0200

    Clean unfinished spec

commit 9dce11e
Author: Julien Biezemans <jb@jbpros.com>
Date:   Tue Jun 7 18:45:23 2011 +0200

    Refactor message, event and payload wording to consistent use

commit ae2b3f4
Author: Julien Biezemans <jb@jbpros.com>
Date:   Tue Jun 7 17:34:36 2011 +0200

    Add wrapUserFunctionAndAfterMessageBroadcast() and wrapAfterMessageBroadcast()

commit 55fe390
Author: Julien Biezemans <jb@jbpros.com>
Date:   Tue Jun 7 15:29:11 2011 +0200

    Add extractCallbackFromArguments()

commit 95f5a07
Author: Julien Biezemans <jb@jbpros.com>
Date:   Tue Jun 7 15:21:40 2011 +0200

    Add extractNonMessagePayloadArgumentsFromArguments()

commit d49ce12
Author: Julien Biezemans <jb@jbpros.com>
Date:   Tue Jun 7 15:05:44 2011 +0200

    Add extractUserFunctionFromArguments()

commit a6d3287
Author: Julien Biezemans <jb@jbpros.com>
Date:   Tue Jun 7 14:36:08 2011 +0200

    Add Cucumber.Util.Arguments

commit fc367ea
Author: Julien Biezemans <jb@jbpros.com>
Date:   Tue Jun 7 14:13:49 2011 +0200

    Add extractMessagePayloadFromArguments()

commit 3e69e98
Author: Julien Biezemans <jb@jbpros.com>
Date:   Tue Jun 7 11:42:37 2011 +0200

    Spec broadcastUserFunction()

commit b669e01
Author: Julien Biezemans <jb@jbpros.com>
Date:   Mon Jun 6 16:59:59 2011 +0200

    Refactor visitXXX() methods
  • Loading branch information
jbpros committed Jun 14, 2011
1 parent 4abc92b commit fc83930
Show file tree
Hide file tree
Showing 5 changed files with 488 additions and 210 deletions.
188 changes: 124 additions & 64 deletions lib/cucumber.js
Original file line number Diff line number Diff line change
Expand Up @@ -237,63 +237,81 @@ Cucumber.Ast.TreeWalker = function(features, supportCodeLibrary, listeners) {
},

visitFeatures: function visitFeatures(features, callback) {
self.broadcastMessagesBeforeAndAfterFunction(Cucumber.Ast.TreeWalker.FEATURES_MESSAGE, function() {
features.acceptVisitor(self, callback);
});
var event = Cucumber.Ast.TreeWalker.Event(Cucumber.Ast.TreeWalker.FEATURES_EVENT_NAME);
self.broadcastEventAroundUserFunction(
event,
function(callback) { features.acceptVisitor(self, callback); },
callback
);
},

visitFeature: function visitFeature(feature, callback) {
self.broadcastMessagesBeforeAndAfterFunction(Cucumber.Ast.TreeWalker.FEATURE_MESSAGE, feature, function() {
feature.acceptVisitor(self, callback);
});
var payload = { feature: feature };
var event = Cucumber.Ast.TreeWalker.Event(Cucumber.Ast.TreeWalker.FEATURE_EVENT_NAME, payload);
self.broadcastEventAroundUserFunction(
event,
function(callback) { feature.acceptVisitor(self, callback); },
callback
);
},

visitScenario: function visitScenario(scenario, callback) {
self.broadcastMessagesBeforeAndAfterFunction(Cucumber.Ast.TreeWalker.SCENARIO_MESSAGE, scenario, function() {
scenario.acceptVisitor(self, callback);
});
var payload = { scenario: scenario };
var event = Cucumber.Ast.TreeWalker.Event(Cucumber.Ast.TreeWalker.SCENARIO_EVENT_NAME, payload);
self.broadcastEventAroundUserFunction(
event,
function(callback) { scenario.acceptVisitor(self, callback); },
callback
);
},

visitStep: function visitStep(step, callback) {
self.broadcastMessagesBeforeAndAfterFunction(Cucumber.Ast.TreeWalker.STEP_MESSAGE, step, function() {
step.acceptVisitor(self, callback);
});
var payload = { step: step };
var event = Cucumber.Ast.TreeWalker.Event(Cucumber.Ast.TreeWalker.STEP_EVENT_NAME, payload);
self.broadcastEventAroundUserFunction(
event,
function(callback) { step.acceptVisitor(self, callback); },
callback
);
},

visitStepResult: function visitStepResult(stepResult, callback) {
self.broadcastMessage(Cucumber.Ast.TreeWalker.STEP_RESULT_MESSAGE, stepResult);
callback();
var payload = { stepResult: stepResult };
var event = Cucumber.Ast.TreeWalker.Event(Cucumber.Ast.TreeWalker.STEP_RESULT_EVENT_NAME, payload);
self.broadcastEvent(event, callback);
},

broadcastMessagesBeforeAndAfterFunction: function broadcastMessagesBeforeAndAfterFunction() {
var message = arguments[0];
var parameters = [];
if (arguments.length > 2) {
for(var i = 1; i < arguments.length - 1; i++) {
parameters.push(arguments[i]);
};
};
var userFunction = arguments[arguments.length - 1];
var beforeMessage = Cucumber.Ast.TreeWalker.BEFORE_MESSAGE_PREFIX + message;
var afterMessage = Cucumber.Ast.TreeWalker.AFTER_MESSAGE_PREFIX + message;
var beforeParameters = [beforeMessage].concat(parameters);
var afterParameters = [afterMessage].concat(parameters);

self.broadcastMessage.apply(this, beforeParameters);
userFunction();
self.broadcastMessage.apply(this, [afterMessage].concat(parameters));
},

broadcastMessage: function broadcastMessage() {
var message = arguments[0];
var parameters = [];
for(var i = 1; i < arguments.length; i++) {
parameters.push(arguments[i]);
broadcastEventAroundUserFunction: function broadcastEventAroundUserFunction(event, userFunction, callback) {
var userFunctionWrapper = self.wrapUserFunctionAndAfterEventBroadcast(userFunction, event, callback);
self.broadcastBeforeEvent(event, userFunctionWrapper);
},

wrapUserFunctionAndAfterEventBroadcast: function wrapUserFunctionAndAfterEventBroadcast(userFunction, event, callback) {
var callAfterEventBroadcast = self.wrapAfterEventBroadcast(event, callback);
return function callUserFunctionAndBroadcastAfterEvent() {
userFunction(callAfterEventBroadcast);
};
listeners.syncForEach(function(listener) {
var hearMethodName = Cucumber.Ast.TreeWalker.HEAR_METHOD_PREFIX + message;
listener[hearMethodName].apply(this, parameters);
});
},

wrapAfterEventBroadcast: function wrapAfterEventBroadcast(event, callback) {
return function() { self.broadcastAfterEvent(event, callback); };
},

broadcastBeforeEvent: function broadcastBeforeEvent(event, callback) {
var preEvent = event.replicateAsPreEvent();
self.broadcastEvent(preEvent, callback);
},

broadcastAfterEvent: function broadcastAfterEvent(event, callback) {
var postEvent = event.replicateAsPostEvent();
self.broadcastEvent(postEvent, callback);
},

broadcastEvent: function broadcastEvent(event, callback) {
listeners.forEach(
function(listener, callback) { listener.hear(event, callback); },
callback
);
},

lookupStepDefinitionByName: function lookupStepDefinitionByName(stepName) {
Expand All @@ -303,14 +321,39 @@ Cucumber.Ast.TreeWalker = function(features, supportCodeLibrary, listeners) {
return self;
};

Cucumber.Ast.TreeWalker.FEATURES_MESSAGE = 'Features';
Cucumber.Ast.TreeWalker.FEATURE_MESSAGE = 'Feature';
Cucumber.Ast.TreeWalker.SCENARIO_MESSAGE = 'Scenario';
Cucumber.Ast.TreeWalker.STEP_MESSAGE = 'Step';
Cucumber.Ast.TreeWalker.STEP_RESULT_MESSAGE = 'StepResult';
Cucumber.Ast.TreeWalker.BEFORE_MESSAGE_PREFIX = 'Before';
Cucumber.Ast.TreeWalker.AFTER_MESSAGE_PREFIX = 'After';
Cucumber.Ast.TreeWalker.HEAR_METHOD_PREFIX = 'hear';
Cucumber.Ast.TreeWalker.FEATURES_EVENT_NAME = 'Features';
Cucumber.Ast.TreeWalker.FEATURE_EVENT_NAME = 'Feature';
Cucumber.Ast.TreeWalker.SCENARIO_EVENT_NAME = 'Scenario';
Cucumber.Ast.TreeWalker.STEP_EVENT_NAME = 'Step';
Cucumber.Ast.TreeWalker.STEP_RESULT_EVENT_NAME = 'StepResult';
Cucumber.Ast.TreeWalker.BEFORE_EVENT_NAME_PREFIX = 'Before';
Cucumber.Ast.TreeWalker.AFTER_EVENT_NAME_PREFIX = 'After';
Cucumber.Ast.TreeWalker.NON_EVENT_LEADING_PARAMETERS_COUNT = 0;
Cucumber.Ast.TreeWalker.NON_EVENT_TRAILING_PARAMETERS_COUNT = 2;

Cucumber.Ast.TreeWalker.Event = function(name, payload) {
var self = {
getName: function getName() {
return name;
},

getPayloadItem: function getPayloadItem(itemName) {
return payload[itemName];
},

replicateAsPreEvent: function replicateAsPreEvent() {
var newName = Cucumber.Ast.TreeWalker.BEFORE_EVENT_NAME_PREFIX + name;
return Cucumber.Ast.TreeWalker.Event(newName, payload);
},

replicateAsPostEvent: function replicateAsPostEvent() {
var newName = Cucumber.Ast.TreeWalker.AFTER_EVENT_NAME_PREFIX + name;
return Cucumber.Ast.TreeWalker.Event(newName, payload);
}

};
return self;
};

Cucumber.SupportCode = {};

Expand Down Expand Up @@ -436,6 +479,11 @@ Cucumber.Types.Collection = function() {
return self;
};

Cucumber.Util = {};
Cucumber.Util.Arguments = function(argumentsObject) {
return Array.prototype.slice.call(argumentsObject);
};

/*
Cucumber.Debug utilities are meant to experiment and...
debug. No tests cover that namespace.
Expand All @@ -451,43 +499,55 @@ Cucumber.Debug.SimpleAstListener = function(options) {
var options = {};

var self = {
hearBeforeFeatures: function hearBeforeFeatures() { },

hearAfterFeatures: function hearAfterFeatures() { },
hear: function hear(event, callback) {
switch(event.getName()) {
case 'BeforeFeature':
self.hearBeforeFeature(event.getPayloadItem('feature'), callback);
break;
case 'BeforeScenario':
self.hearBeforeScenario(event.getPayloadItem('scenario'), callback);
break;
case 'BeforeStep':
self.hearBeforeStep(event.getPayloadItem('step'), callback);
break;
case 'StepResult':
self.hearStepResult(event.getPayloadItem('stepResult'), callback);
break;
default:
callback();
}
},

hearBeforeFeature: function hearBeforeFeature(feature) {
hearBeforeFeature: function hearBeforeFeature(feature, callback) {
log("Feature: " + feature.getName());
var description = feature.getDescription();
if (description != "")
log(description, 1);
callback();
},

hearAfterFeature: function hearAfterFeature() { },

hearBeforeScenario: function hearBeforeScenario(scenario) {
hearBeforeScenario: function hearBeforeScenario(scenario, callback) {
beforeEachScenarioCallbacks.forEach(function(func) {
func();
});
log("");
log(scenario.getKeyword() + ": " + scenario.getName(), 1);
callback();
},

hearAfterScenario: function hearAfterScenario() { },

hearBeforeStep: function hearBeforeStep(step) {
hearBeforeStep: function hearBeforeStep(step, callback) {
currentStep = step;
callback();
},

hearStepResult: function hearStepResult(stepResult) {
hearStepResult: function hearStepResult(stepResult, callback) {
log(currentStep.getKeyword() + currentStep.getName(), 2);
if (currentStep.hasDocString()) {
log('"""', 3);
log(currentStep.getDocString().getString(), 3);
log('"""', 3);
};
},

hearAfterStep: function hearAfterStep() {
callback();
},

getLogs: function getLogs() {
Expand Down
4 changes: 2 additions & 2 deletions spec/cucumber/ast/features_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ describe("Cucumber.Ast.Features", function() {
var Cucumber = require('cucumber');
var featureCollection, lastFeature;
var features;

beforeEach(function() {
lastFeature = createSpy("Last feature");
featureCollection = createSpy("Feature collection");
Expand All @@ -14,7 +14,7 @@ describe("Cucumber.Ast.Features", function() {
spyOn(Cucumber.Types, 'Collection').andReturn(featureCollection);
features = Cucumber.Ast.Features();
});

describe("constructor", function() {
it("creates a new collection to store features", function() {
expect(Cucumber.Types.Collection).toHaveBeenCalledWith();
Expand Down
77 changes: 77 additions & 0 deletions spec/cucumber/ast/tree_walker/event_spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
require('../../../support/spec_helper');

describe("Cucumber.Ast.TreeWalker.Event", function() {
var Cucumber = require('cucumber');
var event, name, payload;

beforeEach(function() {
name = "SomeEvent";
payloadItems = [
createSpy("First payload item"),
createSpy("Second payload item"),
createSpy("Third payload item")
];
payload = {
firstItem: payloadItems[0],
secondItem: payloadItems[1],
thirdItem: payloadItems[2]
};
event = Cucumber.Ast.TreeWalker.Event(name, payload);
});

describe("getName()", function() {
it("returns the name of the event", function() {
expect(event.getName()).toBe(name);
});
});

describe("replicateAsPreEvent()", function() {
var preEvent;

beforeEach(function() {
preEvent = createSpy("Pre-event (before)");
spyOn(Cucumber.Ast.TreeWalker, 'Event').andReturn(preEvent);
});

it("creates a new event with the before prefix prepended to the event name and the same payload", function() {
var newName = Cucumber.Ast.TreeWalker.BEFORE_EVENT_NAME_PREFIX + name;
event.replicateAsPreEvent();
expect(Cucumber.Ast.TreeWalker.Event).toHaveBeenCalledWith(newName, payload);
});

it("returns the new event", function() {
expect(event.replicateAsPreEvent()).toBe(preEvent);
});
});

describe("replicateAsPostEvent()", function() {
var postEvent;

beforeEach(function() {
postEvent = createSpy("Post-event (after)");
spyOn(Cucumber.Ast.TreeWalker, 'Event').andReturn(postEvent);
});

it("creates a new event with the after prefix prepended to the event name and the same payload", function() {
var newName = Cucumber.Ast.TreeWalker.AFTER_EVENT_NAME_PREFIX + name;
event.replicateAsPostEvent();
expect(Cucumber.Ast.TreeWalker.Event).toHaveBeenCalledWith(newName, payload);
});

it("returns the new event", function() {
expect(event.replicateAsPostEvent()).toBe(postEvent);
});
});

describe("getPayloadItem()", function() {
it("returns the requested item from the payload", function() {
expect(event.getPayloadItem('firstItem')).toBe(payloadItems[0]);
expect(event.getPayloadItem('secondItem')).toBe(payloadItems[1]);
expect(event.getPayloadItem('thirdItem')).toBe(payloadItems[2]);
});

it("returns undefined when the item does not exist in the payload", function() {
expect(event.getPayloadItem('unknownItem')).toBeUndefined();
});
});
});
Loading

0 comments on commit fc83930

Please sign in to comment.