Skip to content
Browse files

Add support for tagged hooks (close #32)

  • Loading branch information...
1 parent f383a0b commit bf57ff17fe7237a52211515dda9289c6209e8724 @jbpros jbpros committed Feb 27, 2012
2 features/cucumber-tck
@@ -1 +1 @@
-Subproject commit 8559dee1a68227d85d48684ce92c937bad14f9ac
+Subproject commit 0f42d85e17c1bbfa6da76960fc40985db69e49bd
View
9 features/hooks.feature
@@ -0,0 +1,9 @@
+Feature: Environment Hooks
+
+ The following scenario is a regression test for special "around" hooks which
+ deserve a bit more of attention.
+
+ Scenario: Tagged around hook with untagged scenario
+ Given an around hook tagged with "@foo"
+ When Cucumber executes a scenario with no tags
+ Then the hook is not fired
View
42 features/step_definitions/cucumber_js_mappings.rb
@@ -6,6 +6,7 @@ module CucumberJsMappings
WORLD_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
@@ -135,23 +136,34 @@ def write_world_function
EOF
end
- def write_passing_hook hook_type
+ def write_passing_hook options = {}
+ log_string = options[:log_cycle_event_as]
+ if options[:type]
+ hook_type = options[:type]
+ log_string ||= hook_type
+ else
+ hook_type = "before"
+ log_string ||= "hook"
+ end
+ tags = options[:tags] || []
provide_cycle_logging_facilities
define_hook = hook_type.capitalize
+ params = tags.any? ? "'#{tags.join("', '")}', " : ""
+
if hook_type == "around"
append_support_code <<-EOF
-this.#{define_hook}(function(runScenario) {
- this.logCycleEvent('#{hook_type}-pre');
+this.#{define_hook}(#{params}function(runScenario) {
+ this.logCycleEvent('#{log_string}-pre');
runScenario(function(callback) {
- this.logCycleEvent('#{hook_type}-post');
+ this.logCycleEvent('#{log_string}-post');
callback();
});
});
EOF
else
append_support_code <<-EOF
-this.#{define_hook}(function(callback) {
- this.logCycleEvent('#{hook_type}');
+this.#{define_hook}(#{params}function(callback) {
+ this.logCycleEvent('#{log_string}');
callback();
});
EOF
@@ -178,7 +190,7 @@ def provide_cycle_logging_facilities
append_support_code <<-EOF
this.World.prototype.logCycleEvent = function logCycleEvent(name) {
fd = fs.openSync('#{CYCLE_LOG_FILE}', 'a');
- fs.writeSync(fd, " -> " + name, null);
+ fs.writeSync(fd, "#{CYCLE_SEQUENCE_SEPARATOR}" + name, null);
fs.closeSync(fd);
};
EOF
@@ -224,12 +236,18 @@ def assert_world_function_called
end
def assert_cycle_sequence *args
- expected_string = args.join " -> "
+ expected_string = args.join CYCLE_SEQUENCE_SEPARATOR
check_file_content(CucumberJsMappings::CYCLE_LOG_FILE, expected_string, true)
end
+ def assert_cycle_sequence_excluding *args
+ args.each do |unexpected_string|
+ check_file_content(CucumberJsMappings::CYCLE_LOG_FILE, unexpected_string, false)
+ end
+ end
+
def assert_complete_cycle_sequence *args
- expected_string = args.join " -> "
+ expected_string = "#{CYCLE_SEQUENCE_SEPARATOR}#{args.join(CYCLE_SEQUENCE_SEPARATOR)}"
check_exact_file_content(CucumberJsMappings::CYCLE_LOG_FILE, expected_string)
end
@@ -259,10 +277,10 @@ def assert_suggested_step_definition_snippet(stepdef_keyword, stepdef_pattern, p
end
def assert_executed_scenarios *scenario_offsets
- sequence = scenario_offsets.inject('') do |sequence, scenario_offset|
- "#{sequence} -> #{nth_step_name(scenario_offset)}"
+ sequence = scenario_offsets.inject([]) do |sequence, scenario_offset|
+ sequence << nth_step_name(scenario_offset)
end
- assert_complete_cycle_sequence sequence
+ assert_complete_cycle_sequence *sequence
end
def failed_output
View
46 features/step_definitions/cucumber_steps.js
@@ -28,7 +28,7 @@ var cucumberSteps = function() {
});
Given(/^a passing around hook$/, function(callback) {
- this.stepDefinitions += "this.Around(function(runScenario) {\
+ this.stepDefinitions += "Around(function(runScenario) {\
world.logCycleEvent('around-pre');\
runScenario(function(callback) {\
world.logCycleEvent('around-post');\
@@ -38,6 +38,33 @@ var cucumberSteps = function() {
callback();
});
+ Given(/^an untagged hook$/, function(callback) {
+ this.stepDefinitions += "Before(function(callback) {\
+ world.logCycleEvent('hook');\
+ callback();\
+});\n";
+ callback();
+ });
+
+ Given(/^a hook tagged with "([^"]*)"$/, function(tag, callback) {
+ this.stepDefinitions += "Before('" + tag +"', function(callback) {\
+ world.logCycleEvent('hook');\
+ callback();\
+});\n";
+ callback();
+ });
+
+ Given(/^an around hook tagged with "([^"]*)"$/, function(tag, callback) {
+ this.stepDefinitions += "Around('" + tag + "', function(runScenario) {\
+ world.logCycleEvent('hook-pre');\
+ runScenario(function(callback) {\
+ world.logCycleEvent('hook-post');\
+ callback();\
+ });\
+});\n";
+ callback();
+ });
+
Given(/^the step "([^"]*)" has a failing mapping$/, function(stepName, callback) {
this.stepDefinitions += "Given(/^" + stepName + "$/, function(callback) {\
world.touchStep(\"" + stepName + "\");\
@@ -133,10 +160,15 @@ setTimeout(callback.pending, 10);\
this.runFeature({}, callback);
});
- When(/^Cucumber executes a scenario$/, function(callback) {
+ When(/^Cucumber executes a scenario(?: with no tags)?$/, function(callback) {
this.runAScenario(callback);
});
+ When(/^Cucumber executes a scenario tagged with "([^"]*)"$/, function(tag, callback) {
+ this.addPassingScenarioWithTags(tag);
+ this.runFeature({}, callback);
+ });
+
When(/^Cucumber runs the feature$/, function(callback) {
this.runFeature({}, callback);
});
@@ -267,6 +299,16 @@ callback();\
callback();
});
+ Then(/^the hook is fired$/, function(callback) {
+ this.assertCycleSequence('hook');
+ callback();
+ });
+
+ Then(/^the hook is not fired$/, function(callback) {
+ this.assertCycleSequenceExcluding('hook');
+ callback();
+ });
+
Then(/^an error about the missing World instance is raised$/, function(callback) {
this.assertFailureMessage("World constructor called back without World instance");
callback();
View
4 features/step_definitions/cucumber_steps.rb
@@ -17,6 +17,10 @@
write_asynchronously_failing_mapping_with_message(step_name, message)
end
+Given /^an around hook tagged with "([^"]*)"$/ do |tag|
+ write_passing_hook :type => "around", :tags => [tag], :log_cycle_event_as => "hook"
+end
+
When /^Cucumber executes a scenario using that mapping$/ do
write_feature <<-EOF
Feature:
View
11 features/step_definitions/cucumber_world.js
@@ -15,7 +15,7 @@ var proto = World.prototype;
proto.runFeature = function runFeature(options, callback) {
var supportCode;
var supportCodeSource = "supportCode = function() {\n var Given = When = Then = this.defineStep;\n" +
- " var Before = this.Before, After = this.After;\n" +
+ " var Around = this.Around, Before = this.Before, After = this.After;\n" +
this.stepDefinitions + "};\n";
var world = this;
eval(supportCodeSource);
@@ -215,6 +215,15 @@ proto.assertCompleteCycleSequence = function assertCompleteCycleSequence() {
}
+proto.assertCycleSequenceExcluding = function assertCycleSequenceExcluding() {
+ var self = this;
+ var events = Array.prototype.slice.apply(arguments);
+ events.forEach(function(event) {
+ if (self.cycleEvents.indexOf(event) >= 0)
+ throw(new Error("Expected cycle sequence \"" + self.cycleEvents + "\" not to contain \"" + event + "\""));
+ });
+}
+
proto.indentCode = function indentCode(code, levels) {
var indented = '';
var lines = code.split("\n");
View
8 lib/cucumber/cli/argument_parser.js
@@ -1,5 +1,3 @@
-var _ = require('underscore');
-
var ArgumentParser = function(argv) {
var Cucumber = require('../../cucumber');
@@ -55,11 +53,7 @@ var ArgumentParser = function(argv) {
getTagGroups: function getTagGroups() {
var tagOptionValues = self.getOptionOrDefault(ArgumentParser.TAGS_OPTION_NAME, []);
- var tagGroups = _.map(tagOptionValues, function(tagOptionValue) {
- var tagGroupParser = Cucumber.TagGroupParser(tagOptionValue);
- var tagGroup = tagGroupParser.parse();
- return tagGroup;
- });
+ var tagGroups = Cucumber.TagGroupParser.getTagGroupsFromStrings(tagOptionValues);
return tagGroups;
},
View
3 lib/cucumber/runtime/ast_tree_walker.js
@@ -45,8 +45,9 @@ var AstTreeWalker = function(features, supportCodeLibrary, listeners) {
self.witnessNewScenario();
var payload = { scenario: scenario };
var event = AstTreeWalker.Event(AstTreeWalker.SCENARIO_EVENT_NAME, payload);
- var hookedUpScenarioVisit = supportCodeLibrary.hookUpFunctionWithWorld(
+ var hookedUpScenarioVisit = supportCodeLibrary.hookUpFunction(
function(callback) { scenario.acceptVisitor(self, callback); },
+ scenario,
world
);
self.broadcastEventAroundUserFunction(
View
28 lib/cucumber/support_code/hook.js
@@ -1,9 +1,31 @@
-var Hook = function(code) {
+var _ = require('underscore');
+
+var Hook = function(code, options) {
var Cucumber = require('../../cucumber');
+ var tags = options['tags'] || [];
+
var self = {
- invoke: function(world, callback) {
- code.call(world, callback);
+ invokeBesideScenario: function invokeBesideScenario(scenario, world, callback) {
+ if (self.appliesToScenario(scenario))
+ code.call(world, callback);
+ else
+ callback(function(endPostScenarioAroundHook) { endPostScenarioAroundHook(); });
+ },
+
+ appliesToScenario: function appliesToScenario(scenario) {
+ var astFilter = self.getAstFilter();
+ return astFilter.isScenarioEnrolled(scenario);
+ },
+
+ getAstFilter: function getAstFilter() {
+ var tagGroups = Cucumber.TagGroupParser.getTagGroupsFromStrings(tags);
+ var rules = _.map(tagGroups, function(tagGroup) {
+ var rule = Cucumber.Ast.Filter.AnyOfTagsRule(tagGroup);
+ return rule;
+ });
+ var astFilter = Cucumber.Ast.Filter(rules);
+ return astFilter;
}
};
return self;
View
22 lib/cucumber/support_code/library.js
@@ -23,21 +23,27 @@ var Library = function(supportCodeDefinition) {
return (stepDefinition != undefined);
},
- hookUpFunctionWithWorld: function hookUpFunctionWithWorld(userFunction, world) {
- var hookedUpFunction = hooker.hookUpFunctionWithWorld(userFunction, world);
+ hookUpFunction: function hookUpFunction(userFunction, scenario, world) {
+ var hookedUpFunction = hooker.hookUpFunction(userFunction, scenario, world);
return hookedUpFunction;
},
- defineAroundHook: function defineAroundHook(code) {
- hooker.addAroundHookCode(code);
+ defineAroundHook: function defineAroundHook() {
+ var tagGroupStrings = Cucumber.Util.Arguments(arguments);
+ var code = tagGroupStrings.pop();
+ hooker.addAroundHookCode(code, {tags: tagGroupStrings});
},
- defineBeforeHook: function defineBeforeHook(code) {
- hooker.addBeforeHookCode(code);
+ defineBeforeHook: function defineBeforeHook() {
+ var tagGroupStrings = Cucumber.Util.Arguments(arguments);
+ var code = tagGroupStrings.pop();
+ hooker.addBeforeHookCode(code, {tags: tagGroupStrings});
},
- defineAfterHook: function defineAfterHook(code) {
- hooker.addAfterHookCode(code);
+ defineAfterHook: function defineAfterHook() {
+ var tagGroupStrings = Cucumber.Util.Arguments(arguments);
+ var code = tagGroupStrings.pop();
+ hooker.addAfterHookCode(code, {tags: tagGroupStrings});
},
defineStep: function defineStep(name, code) {
View
28 lib/cucumber/support_code/library/hooker.js
@@ -6,43 +6,43 @@ var Hooker = function() {
var afterHooks = Cucumber.Type.Collection();
var self = {
- addAroundHookCode: function addAroundHookCode(code) {
- var aroundHook = Cucumber.SupportCode.Hook(code);
+ addAroundHookCode: function addAroundHookCode(code, options) {
+ var aroundHook = Cucumber.SupportCode.Hook(code, options);
aroundHooks.add(aroundHook);
},
- addBeforeHookCode: function addBeforeHookCode(code) {
- var beforeHook = Cucumber.SupportCode.Hook(code);
+ addBeforeHookCode: function addBeforeHookCode(code, options) {
+ var beforeHook = Cucumber.SupportCode.Hook(code, options);
beforeHooks.add(beforeHook);
},
- addAfterHookCode: function addAfterHookCode(code) {
- var afterHook = Cucumber.SupportCode.Hook(code);
+ addAfterHookCode: function addAfterHookCode(code, options) {
+ var afterHook = Cucumber.SupportCode.Hook(code, options);
afterHooks.unshift(afterHook);
},
- hookUpFunctionWithWorld: function hookUpFunctionWithWorld(userFunction, world) {
+ hookUpFunction: function hookUpFunction(userFunction, scenario, world) {
var hookedUpFunction = function(callback) {
var postScenarioAroundHookCallbacks = Cucumber.Type.Collection();
aroundHooks.forEach(callPreScenarioAroundHook, callBeforeHooks);
function callPreScenarioAroundHook(aroundHook, preScenarioAroundHookCallback) {
- aroundHook.invoke(world, function(postScenarioAroundHookCallback) {
+ aroundHook.invokeBesideScenario(scenario, world, function(postScenarioAroundHookCallback) {
postScenarioAroundHookCallbacks.unshift(postScenarioAroundHookCallback);
preScenarioAroundHookCallback();
});
}
function callBeforeHooks() {
- self.triggerBeforeHooks(world, callUserFunction);
+ self.triggerBeforeHooks(scenario, world, callUserFunction);
}
function callUserFunction() {
userFunction(callAfterHooks);
}
function callAfterHooks() {
- self.triggerAfterHooks(world, callPostScenarioAroundHooks);
+ self.triggerAfterHooks(scenario, world, callPostScenarioAroundHooks);
}
function callPostScenarioAroundHooks() {
@@ -59,15 +59,15 @@ var Hooker = function() {
return hookedUpFunction;
},
- triggerBeforeHooks: function triggerBeforeHooks(world, callback) {
+ triggerBeforeHooks: function triggerBeforeHooks(scenario, world, callback) {
beforeHooks.forEach(function(beforeHook, callback) {
- beforeHook.invoke(world, callback);
+ beforeHook.invokeBesideScenario(scenario, world, callback);
}, callback);
},
- triggerAfterHooks: function triggerAfterHooks(world, callback) {
+ triggerAfterHooks: function triggerAfterHooks(scenario, world, callback) {
afterHooks.forEach(function(afterHook, callback) {
- afterHook.invoke(world, callback);
+ afterHook.invokeBesideScenario(scenario, world, callback);
}, callback);
}
};
View
13 lib/cucumber/tag_group_parser.js
@@ -8,8 +8,19 @@ var TagGroupParser = function(tagGroupString) {
return trimmedTags;
}
};
-
return self;
};
+
+TagGroupParser.getTagGroupsFromStrings = function getTagGroupsFromStrings(tagGroupStrings) {
+ var Cucumber = require('../cucumber');
+
+ var tagGroups = _.map(tagGroupStrings, function(tagOptionValue) {
+ var tagGroupParser = Cucumber.TagGroupParser(tagOptionValue);
+ var tagGroup = tagGroupParser.parse();
+ return tagGroup;
+ });
+ return tagGroups;
+};
+
TagGroupParser.TAG_SEPARATOR = ',';
module.exports = TagGroupParser;
View
35 spec/cucumber/cli/argument_parser_spec.js
@@ -253,46 +253,17 @@ describe("Cucumber.Cli.ArgumentParser", function() {
tagOptionValues = createSpy("tag option values");
tagGroups = createSpy("tag groups");
spyOn(argumentParser, 'getOptionOrDefault').andReturn(tagOptionValues);
- spyOn(_, 'map').andReturn(tagGroups);
+ spyOn(Cucumber.TagGroupParser, 'getTagGroupsFromStrings').andReturn(tagGroups);
});
it("gets the tag option values", function() {
argumentParser.getTagGroups();
expect(argumentParser.getOptionOrDefault).toHaveBeenCalledWith(Cucumber.Cli.ArgumentParser.TAGS_OPTION_NAME, []);
});
- it("maps the tag groups", function() {
+ it("gets the tag groups based on the tag option values", function() {
argumentParser.getTagGroups();
- expect(_.map).toHaveBeenCalled();
- expect(_.map).toHaveBeenCalledWithValueAsNthParameter(tagOptionValues, 1);
- expect(_.map).toHaveBeenCalledWithAFunctionAsNthParameter(2);
- });
-
- describe("tag group mapper function", function() {
- var tagGroupMapperFunc, tagOptionValue, tagGroupParser, tagGroup;
-
- beforeEach(function() {
- tagOptionValue = createSpy("tag option value");
- tagGroup = createSpy("tag group");
- tagGroupParser = createSpyWithStubs("tag group parser", {parse: tagGroup});
- argumentParser.getTagGroups();
- tagGroupMapperFunc = _.map.mostRecentCall.args[1];
- spyOn(Cucumber, 'TagGroupParser').andReturn(tagGroupParser);
- });
-
- it("instantiates a tag group parser", function() {
- tagGroupMapperFunc(tagOptionValue);
- expect(Cucumber.TagGroupParser).toHaveBeenCalledWith(tagOptionValue);
- });
-
- it("asks the parser for the parsed tag group", function() {
- tagGroupMapperFunc(tagOptionValue);
- expect(tagGroupParser.parse).toHaveBeenCalled();
- });
-
- it("returns the tag group", function() {
- expect(tagGroupMapperFunc(tagOptionValue)).toBe(tagGroup);
- });
+ expect(Cucumber.TagGroupParser.getTagGroupsFromStrings).toHaveBeenCalledWith(tagOptionValues);
});
it("returns the tag option values", function() {
View
17 spec/cucumber/runtime/ast_tree_walker_spec.js
@@ -183,14 +183,14 @@ describe("Cucumber.Runtime.AstTreeWalker", function() {
beforeEach(function() {
treeWalker.visitScenario(scenario, callback);
worldInstantiationCompletionCallback = supportCodeLibrary.instantiateNewWorld.mostRecentCall.args[0];
- world = createSpy("world instance");
- event = createSpy("scenario visit event");
+ world = createSpy("world instance");
+ event = createSpy("scenario visit event");
hookedUpScenarioVisit = createSpy("hooked up scenario visit");
- payload = {scenario: scenario};
+ payload = {scenario: scenario};
spyOn(treeWalker, 'setWorld');
spyOn(treeWalker, 'witnessNewScenario');
spyOn(Cucumber.Runtime.AstTreeWalker, 'Event').andReturn(event);
- spyOnStub(supportCodeLibrary, 'hookUpFunctionWithWorld').andReturn(hookedUpScenarioVisit);
+ spyOnStub(supportCodeLibrary, 'hookUpFunction').andReturn(hookedUpScenarioVisit);
spyOn(treeWalker, 'broadcastEventAroundUserFunction');
});
@@ -211,17 +211,18 @@ describe("Cucumber.Runtime.AstTreeWalker", function() {
it("hooks up a function", function() {
worldInstantiationCompletionCallback(world);
- expect(supportCodeLibrary.hookUpFunctionWithWorld).toHaveBeenCalled();
- expect(supportCodeLibrary.hookUpFunctionWithWorld).toHaveBeenCalledWithAFunctionAsNthParameter(1);
- expect(supportCodeLibrary.hookUpFunctionWithWorld).toHaveBeenCalledWithValueAsNthParameter(world, 2);
+ expect(supportCodeLibrary.hookUpFunction).toHaveBeenCalled();
+ expect(supportCodeLibrary.hookUpFunction).toHaveBeenCalledWithAFunctionAsNthParameter(1);
+ expect(supportCodeLibrary.hookUpFunction).toHaveBeenCalledWithValueAsNthParameter(scenario, 2);
+ expect(supportCodeLibrary.hookUpFunction).toHaveBeenCalledWithValueAsNthParameter(world, 3);
});
describe("hooked up function", function() {
var hookedUpFunction, hookedUpFunctionCallback;
beforeEach(function() {
worldInstantiationCompletionCallback(world);
- hookedUpFunction = supportCodeLibrary.hookUpFunctionWithWorld.mostRecentCall.args[0];
+ hookedUpFunction = supportCodeLibrary.hookUpFunction.mostRecentCall.args[0];
hookedUpFunctionCallback = createSpy("hooked up function callback");
spyOnStub(scenario, 'acceptVisitor');
});
View
139 spec/cucumber/support_code/hook_spec.js
@@ -2,25 +2,140 @@ require('../../support/spec_helper');
describe("Cucumber.SupportCode.Hook", function() {
var Cucumber = requireLib('cucumber');
- var hook, code;
+ var hook, code, options, tags;
beforeEach(function() {
- code = createSpy("hook code");
- hook = Cucumber.SupportCode.Hook(code);
+ code = createSpy("hook code");
+ options = {};
+ hook = Cucumber.SupportCode.Hook(code, options);
});
- describe("invoke()", function() {
- var world, callback;
+ describe("invokeBesideScenario()", function() {
+ var scenario, world, callback;
beforeEach(function() {
- world = createSpy("world");
- callback = createSpy("callback");
+ scenario = createSpy("scenario");
+ world = createSpy("world");
+ callback = createSpy("callback");
+ spyOn(hook, 'appliesToScenario');
});
- it("calls the code with the world instance as this", function() {
- hook.invoke(world, callback);
- expect(code).toHaveBeenCalledWith(callback);
- expect(code.mostRecentCall.object).toBe(world);
+ it("checks wether the hook applies to this scenario or not", function() {
+ hook.invokeBesideScenario(scenario, world, callback);
+ expect(hook.appliesToScenario).toHaveBeenCalledWith(scenario);
+ });
+
+ describe("when the hook applies to the scenario ", function() {
+ beforeEach(function() {
+ hook.appliesToScenario.andReturn(true);
+ });
+
+ it("calls the code with the world instance as this", function() {
+ hook.invokeBesideScenario(scenario, world, callback);
+ expect(code).toHaveBeenCalledWith(callback);
+ expect(code.mostRecentCall.object).toBe(world);
+ });
+
+ it("does not call back", function() {
+ hook.invokeBesideScenario(scenario, world, callback);
+ expect(callback).not.toHaveBeenCalled();
+ });
+ });
+
+ describe("when the hook does not apply to the scenario", function() {
+ beforeEach(function() {
+ hook.appliesToScenario.andReturn(false);
+ });
+
+ it("does not call the code", function() {
+ hook.invokeBesideScenario(scenario, world, callback);
+ expect(code).not.toHaveBeenCalled();
+ });
+
+ it("calls back directly with a post-scenario around hook", function() {
+ hook.invokeBesideScenario(scenario, world, callback);
+ expect(callback).toHaveBeenCalled();
+ expect(callback).toHaveBeenCalledWithAFunctionAsNthParameter(1);
+ });
+
+ describe("post-scenario around hook", function() {
+ var postScenarioAroundHook, postScenarioAroundHookCallback;
+
+ beforeEach(function() {
+ hook.invokeBesideScenario(scenario, world, callback);
+ postScenarioAroundHook = callback.mostRecentCall.args[0];
+ postScenarioAroundHookCallback = createSpy("post-scenario around hook callback");
+ });
+
+ it("passes a callback to replace the post-scenario hook (in case of an around hook)", function() {
+ postScenarioAroundHook(postScenarioAroundHookCallback);
+ expect(postScenarioAroundHookCallback).toHaveBeenCalled();
+ });
+ });
+ });
+ });
+
+ describe("appliesToScenario()", function() {
+ var scenario, astFilter, scenarioEnrolled;
+
+ beforeEach(function() {
+ scenarioEnrolled = createSpy("scenario enrolled?");
+ astFilter = createSpyWithStubs("AST filter", {isScenarioEnrolled: scenarioEnrolled});
+ scenario = createSpy("scenario");
+ spyOn(hook, 'getAstFilter').andReturn(astFilter);
+ });
+
+ it("gets the AST filter", function() {
+ hook.appliesToScenario(scenario);
+ expect(hook.getAstFilter).toHaveBeenCalled();
+ });
+
+ it("asks the AST filter wether the scenario is enrolled or not", function() {
+ hook.appliesToScenario(scenario);
+ expect(astFilter.isScenarioEnrolled).toHaveBeenCalledWith(scenario);
+ });
+
+ it("returns the AST filter answer", function() {
+ expect(hook.appliesToScenario(scenario)).toBe(scenarioEnrolled);
+ });
+ });
+
+
+ describe("getAstFilter()", function() {
+ var tags, tagGroups, rules, astFilter;
+
+ beforeEach(function() {
+ tagGroups = [createSpy("tag group 1"), createSpy("tag group 2")];
+ tags = createSpy("tags");
+ options = {tags: tags};
+ hook = Cucumber.SupportCode.Hook(code, options);
+ rules = [createSpy("rule 1"), createSpy("rule 2")];
+ astFilter = createSpy("AST filter");
+ spyOn(Cucumber.TagGroupParser, 'getTagGroupsFromStrings').andReturn(tagGroups);
+ spyOn(Cucumber.Ast, 'Filter').andReturn(astFilter);
+ spyOnStub(Cucumber.Ast.Filter, 'AnyOfTagsRule').andReturnSeveral(rules);
+
+ });
+
+ it("gets the tag groups from the 'tags' option", function() {
+ hook.getAstFilter();
+ expect(Cucumber.TagGroupParser.getTagGroupsFromStrings).toHaveBeenCalledWith(tags);
+ });
+
+ it("builds a new 'any of tags' AST filter rule based on each tag groupe", function() {
+ hook.getAstFilter();
+ tagGroups.forEach(function(tagGroup) {
+ expect(Cucumber.Ast.Filter.AnyOfTagsRule).toHaveBeenCalledWith(tagGroup);
+ });
+ });
+
+ it("instantiates AST filter based on the rules", function() {
+ hook.getAstFilter();
+ expect(Cucumber.Ast.Filter).toHaveBeenCalledWith(rules);
+ });
+
+ it("returns the AST filter", function() {
+ expect(hook.getAstFilter()).toBe(astFilter);
});
});
-});
+});
View
103 spec/cucumber/support_code/library/hooker_spec.js
@@ -20,85 +20,89 @@ describe("Cucumber.SupportCode.Library.Hooker", function() {
});
describe("addAroundHookCode", function() {
- var aroundHook, code;
+ var aroundHook, code, options;
beforeEach(function() {
code = createSpy("around code");
+ options = createSpy("options");
aroundHook = createSpy("around hook");
spyOn(Cucumber.SupportCode, "Hook").andReturn(aroundHook);
spyOnStub(aroundHooks, "add");
});
- it("creates an around hook with the code", function() {
- hooker.addAroundHookCode(code);
- expect(Cucumber.SupportCode.Hook).toHaveBeenCalledWith(code);
+ it("creates an around hook with the code and options", function() {
+ hooker.addAroundHookCode(code, options);
+ expect(Cucumber.SupportCode.Hook).toHaveBeenCalledWith(code, options);
});
it("unshifts the around hook to the around hook collection", function() {
- hooker.addAroundHookCode(code);
+ hooker.addAroundHookCode(code, options);
expect(aroundHooks.add).toHaveBeenCalledWith(aroundHook);
});
});
describe("addBeforeHookCode", function() {
- var beforeHook, code;
+ var beforeHook, code, options;
beforeEach(function() {
code = createSpy("before code");
+ options = createSpy("options");
beforeHook = createSpy("before hook");
spyOn(Cucumber.SupportCode, "Hook").andReturn(beforeHook);
spyOnStub(beforeHooks, "add");
});
- it("creates a before hook with the code", function() {
- hooker.addBeforeHookCode(code);
- expect(Cucumber.SupportCode.Hook).toHaveBeenCalledWith(code);
+ it("creates a before hook with the code and options", function() {
+ hooker.addBeforeHookCode(code, options);
+ expect(Cucumber.SupportCode.Hook).toHaveBeenCalledWith(code, options);
});
it("adds the before hook to the before hook collection", function() {
- hooker.addBeforeHookCode(code);
+ hooker.addBeforeHookCode(code, options);
expect(beforeHooks.add).toHaveBeenCalledWith(beforeHook);
});
});
describe("addAfterHookCode", function() {
- var afterHook, code;
+ var afterHook, code, options;
beforeEach(function() {
code = createSpy("after code");
+ options = createSpy("options");
afterHook = createSpy("after hook");
spyOn(Cucumber.SupportCode, "Hook").andReturn(afterHook);
spyOnStub(afterHooks, "unshift");
});
it("creates a after hook with the code", function() {
- hooker.addAfterHookCode(code);
- expect(Cucumber.SupportCode.Hook).toHaveBeenCalledWith(code);
+ hooker.addAfterHookCode(code, options);
+ expect(Cucumber.SupportCode.Hook).toHaveBeenCalledWith(code, options);
});
it("prepends the after hook to the after hook collection", function() {
- hooker.addAfterHookCode(code);
+ hooker.addAfterHookCode(code, options);
expect(afterHooks.unshift).toHaveBeenCalledWith(afterHook);
});
});
- describe("hookUpFunctionWithWorld()", function() {
- var userFunction, world;
+ describe("hookUpFunction()", function() {
+ var userFunction, world, scenario;
beforeEach(function() {
userFunction = createSpy("user function");
+ scenario = createSpy("scenario");
world = createSpy("world instance");
});
it("returns a function", function() {
- expect(hooker.hookUpFunctionWithWorld(userFunction, world)).toBeAFunction();
+ expect(hooker.hookUpFunction(userFunction, scenario, world)).toBeAFunction();
});
describe("returned function", function() {
var returnedFunction, callback, postScenarioAroundHookCallbacks;
beforeEach(function() {
- returnedFunction = hooker.hookUpFunctionWithWorld(userFunction, world);
+ returnedFunction = hooker.hookUpFunction(userFunction, scenario, world);
callback = createSpy("callback");
postScenarioAroundHookCallbacks = createSpy("post-scenario around hook callbacks");
spyOnStub(aroundHooks, 'forEach');
@@ -121,25 +125,26 @@ describe("Cucumber.SupportCode.Library.Hooker", function() {
var aroundHookIteration, aroundHook, iterationCallback;
beforeEach(function() {
- aroundHook = createSpyWithStubs("around hook", {invoke: null});
+ aroundHook = createSpyWithStubs("around hook", {invokeBesideScenario: null});
iterationCallback = createSpy("iteration callback");
returnedFunction(callback);
aroundHookIteration = aroundHooks.forEach.mostRecentCall.args[0];
});
- it("invokes the around hook with the world instance", function() {
+ it("invokes the around hook beside the scenario with the world instance", function() {
aroundHookIteration(aroundHook, iterationCallback);
- expect(aroundHook.invoke).toHaveBeenCalled();
- expect(aroundHook.invoke).toHaveBeenCalledWithValueAsNthParameter(world, 1);
- expect(aroundHook.invoke).toHaveBeenCalledWithAFunctionAsNthParameter(2);
+ expect(aroundHook.invokeBesideScenario).toHaveBeenCalled();
+ expect(aroundHook.invokeBesideScenario).toHaveBeenCalledWithValueAsNthParameter(scenario, 1);
+ expect(aroundHook.invokeBesideScenario).toHaveBeenCalledWithValueAsNthParameter(world, 2);
+ expect(aroundHook.invokeBesideScenario).toHaveBeenCalledWithAFunctionAsNthParameter(3);
});
describe("on around hook invocation completion", function() {
var invocationCompletionCallback, postScenarioAroundHookCallback;
beforeEach(function() {
aroundHookIteration(aroundHook, iterationCallback);
- invocationCompletionCallback = aroundHook.invoke.mostRecentCall.args[1];
+ invocationCompletionCallback = aroundHook.invokeBesideScenario.mostRecentCall.args[2];
postScenarioAroundHookCallback = createSpy("post-scenario around hook callback");
spyOnStub(postScenarioAroundHookCallbacks, 'unshift');
});
@@ -156,7 +161,7 @@ describe("Cucumber.SupportCode.Library.Hooker", function() {
});
});
- describe("on around hook look completion", function() {
+ describe("on around hook loop completion", function() {
var aroundHooksLoopCallback;
beforeEach(function() {
@@ -168,16 +173,17 @@ describe("Cucumber.SupportCode.Library.Hooker", function() {
it("triggers the before hooks", function() {
aroundHooksLoopCallback();
expect(hooker.triggerBeforeHooks).toHaveBeenCalled();
- expect(hooker.triggerBeforeHooks).toHaveBeenCalledWithValueAsNthParameter(world, 1);
- expect(hooker.triggerBeforeHooks).toHaveBeenCalledWithAFunctionAsNthParameter(2);
+ expect(hooker.triggerBeforeHooks).toHaveBeenCalledWithValueAsNthParameter(scenario, 1);
+ expect(hooker.triggerBeforeHooks).toHaveBeenCalledWithValueAsNthParameter(world, 2);
+ expect(hooker.triggerBeforeHooks).toHaveBeenCalledWithAFunctionAsNthParameter(3);
});
describe("on before hooks completion", function() {
var beforeHooksCompletionCallback;
beforeEach(function() {
aroundHooksLoopCallback();
- beforeHooksCompletionCallback = hooker.triggerBeforeHooks.mostRecentCall.args[1];
+ beforeHooksCompletionCallback = hooker.triggerBeforeHooks.mostRecentCall.args[2];
});
it("calls the user function", function() {
@@ -198,16 +204,17 @@ describe("Cucumber.SupportCode.Library.Hooker", function() {
it("triggers the after hooks", function() {
userFunctionCallback();
expect(hooker.triggerAfterHooks).toHaveBeenCalled();
- expect(hooker.triggerAfterHooks).toHaveBeenCalledWithValueAsNthParameter(world, 1);
- expect(hooker.triggerAfterHooks).toHaveBeenCalledWithAFunctionAsNthParameter(2);
+ expect(hooker.triggerAfterHooks).toHaveBeenCalledWithValueAsNthParameter(scenario, 1);
+ expect(hooker.triggerAfterHooks).toHaveBeenCalledWithValueAsNthParameter(world, 2);
+ expect(hooker.triggerAfterHooks).toHaveBeenCalledWithAFunctionAsNthParameter(3);
});
describe("on after hooks completion", function() {
var afterHooksCompletionCallback;
beforeEach(function() {
userFunctionCallback();
- afterHooksCompletionCallback = hooker.triggerAfterHooks.mostRecentCall.args[1];
+ afterHooksCompletionCallback = hooker.triggerAfterHooks.mostRecentCall.args[2];
spyOnStub(postScenarioAroundHookCallbacks, 'forEach');
});
@@ -242,16 +249,17 @@ describe("Cucumber.SupportCode.Library.Hooker", function() {
});
describe("triggerBeforeHooks", function() {
- var world, callback;
+ var scenario, world, callback;
beforeEach(function() {
- world = createSpy("world");
- callback = createSpy("callback");
+ scenario = createSpy("scenario");
+ world = createSpy("world");
+ callback = createSpy("callback");
spyOnStub(beforeHooks, 'forEach');
});
it("iterates over the before hooks", function() {
- hooker.triggerBeforeHooks(world, callback);
+ hooker.triggerBeforeHooks(scenario, world, callback);
expect(beforeHooks.forEach).toHaveBeenCalled();
expect(beforeHooks.forEach).toHaveBeenCalledWithAFunctionAsNthParameter(1);
expect(beforeHooks.forEach).toHaveBeenCalledWithValueAsNthParameter(callback, 2);
@@ -261,30 +269,31 @@ describe("Cucumber.SupportCode.Library.Hooker", function() {
var beforeHook, forEachBeforeHookFunction, forEachBeforeHookFunctionCallback;
beforeEach(function() {
- hooker.triggerBeforeHooks(world, callback);
+ hooker.triggerBeforeHooks(scenario, world, callback);
forEachBeforeHookFunction = beforeHooks.forEach.mostRecentCall.args[0];
forEachBeforeHookFunctionCallback = createSpy("for each before hook iteration callback");
- beforeHook = createSpyWithStubs("before hook", {invoke: null});
+ beforeHook = createSpyWithStubs("before hook", {invokeBesideScenario: null});
});
- it("invokes the hook", function() {
+ it("invokes the hook beside the scenario", function() {
forEachBeforeHookFunction(beforeHook, forEachBeforeHookFunctionCallback);
- expect(beforeHook.invoke).toHaveBeenCalledWith(world, forEachBeforeHookFunctionCallback);
+ expect(beforeHook.invokeBesideScenario).toHaveBeenCalledWith(scenario, world, forEachBeforeHookFunctionCallback);
});
});
});
describe("triggerAfterHooks", function() {
- var world, callback;
+ var scenario, world, callback;
beforeEach(function() {
- world = createSpy("world");
- callback = createSpy("callback");
+ scenario = createSpy("scenario");
+ world = createSpy("world");
+ callback = createSpy("callback");
spyOnStub(afterHooks, 'forEach');
});
it("iterates over the after hooks", function() {
- hooker.triggerAfterHooks(world, callback);
+ hooker.triggerAfterHooks(scenario, world, callback);
expect(afterHooks.forEach).toHaveBeenCalled();
expect(afterHooks.forEach).toHaveBeenCalledWithAFunctionAsNthParameter(1);
expect(afterHooks.forEach).toHaveBeenCalledWithValueAsNthParameter(callback, 2);
@@ -294,15 +303,15 @@ describe("Cucumber.SupportCode.Library.Hooker", function() {
var afterHook, forEachAfterHookFunction, forEachAfterHookFunctionCallback;
beforeEach(function() {
- hooker.triggerAfterHooks(world, callback);
+ hooker.triggerAfterHooks(scenario, world, callback);
forEachAfterHookFunction = afterHooks.forEach.mostRecentCall.args[0];
forEachAfterHookFunctionCallback = createSpy("for each after hook iteration callback");
- afterHook = createSpyWithStubs("after hook", {invoke: null});
+ afterHook = createSpyWithStubs("after hook", {invokeBesideScenario: null});
});
- it("invokes the hook", function() {
+ it("invokes the hook beside the scenario", function() {
forEachAfterHookFunction(afterHook, forEachAfterHookFunctionCallback);
- expect(afterHook.invoke).toHaveBeenCalledWith(world, forEachAfterHookFunctionCallback);
+ expect(afterHook.invokeBesideScenario).toHaveBeenCalledWith(scenario, world, forEachAfterHookFunctionCallback);
});
});
});
View
64 spec/cucumber/support_code/library_spec.js
@@ -149,27 +149,28 @@ describe("Cucumber.SupportCode.Library", function() {
});
});
- describe("hookUpFunctionWithWorld", function() {
- var userFunction, world, hookedUpFunction;
+ 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, 'hookUpFunctionWithWorld').andReturn(hookedUpFunction);
+ spyOnStub(hooker, 'hookUpFunction').andReturn(hookedUpFunction);
});
it("hooks up the function with the world instance", function() {
- library.hookUpFunctionWithWorld(userFunction, world);
- expect(hooker.hookUpFunctionWithWorld).toHaveBeenCalledWith(userFunction, world);
+ library.hookUpFunction(userFunction, scenario, world);
+ expect(hooker.hookUpFunction).toHaveBeenCalledWith(userFunction, scenario, world);
});
it("returns the hooked up function", function() {
- expect(library.hookUpFunctionWithWorld(userFunction, world)).toBe(hookedUpFunction);
+ expect(library.hookUpFunction(userFunction, scenario, world)).toBe(hookedUpFunction);
});
});
- describe("defineAroundHook", function() {
+ describe("defineAroundHook()", function() {
var code;
beforeEach(function() {
@@ -179,11 +180,24 @@ describe("Cucumber.SupportCode.Library", function() {
it("instructs the hooker to use the code as an around hook", function() {
library.defineAroundHook(code);
- expect(hooker.addAroundHookCode).toHaveBeenCalledWith(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() {
+ describe("defineBeforeHook()", function() {
var code;
beforeEach(function() {
@@ -193,11 +207,24 @@ describe("Cucumber.SupportCode.Library", function() {
it("instructs the hooker to use the code as an before hook", function() {
library.defineBeforeHook(code);
- expect(hooker.addBeforeHookCode).toHaveBeenCalledWith(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() {
+ describe("defineAfterHook()", function() {
var code;
beforeEach(function() {
@@ -207,7 +234,20 @@ describe("Cucumber.SupportCode.Library", function() {
it("instructs the hooker to use the code as an after hook", function() {
library.defineAfterHook(code);
- expect(hooker.addAfterHookCode).toHaveBeenCalledWith(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]});
});
});
View
32 spec/cucumber/tag_group_parser_spec.js
@@ -28,4 +28,36 @@ describe("Cucumber.TagGroupParser", function() {
expect(parsed).toEqual(["@foo", "~@bar", "@baz"]);
});
});
+
+ describe(".getTagGroupsFromStrings()", function() {
+ var tagGroupStrings, getTagGroupsFromStringsFunc;
+
+ beforeEach(function() {
+ getTagGroupsFromStringsFunc = Cucumber.TagGroupParser.getTagGroupsFromStrings;
+ tagGroupStrings = [createSpy("first tag group string"), createSpy("second tag group string"), createSpy("third tag group string")];
+ tagGroups = [createSpy("first tag group"), createSpy("second tag group"), createSpy("third tag group")];
+ tagGroupParsers = [createSpyWithStubs("first tag group parser", {parse: tagGroups[0]}),
+ createSpyWithStubs("second tag group parser", {parse: tagGroups[1]}),
+ createSpyWithStubs("third tag group parser", {parse: tagGroups[2]})];
+ spyOn(Cucumber, 'TagGroupParser').andReturnSeveral(tagGroupParsers);
+ });
+
+ it("creates a TagGroupParser instance for each tag group string", function() {
+ getTagGroupsFromStringsFunc(tagGroupStrings);
+ tagGroupStrings.forEach(function(tagGroupString) {
+ expect(Cucumber.TagGroupParser).toHaveBeenCalledWith(tagGroupString);
+ });
+ });
+
+ it("gets the parsed tag groups", function() {
+ getTagGroupsFromStringsFunc(tagGroupStrings);
+ tagGroupParsers.forEach(function(tagGroupParser) {
+ expect(tagGroupParser.parse).toHaveBeenCalled();
+ });
+ });
+
+ it("returns the tag groups", function() {
+ expect(getTagGroupsFromStringsFunc(tagGroupStrings)).toEqual(tagGroups);
+ });
+ });
});

0 comments on commit bf57ff1

Please sign in to comment.
Something went wrong with that request. Please try again.