From 91ab3617163f5ac6ea601f8c06e0c752ced11d10 Mon Sep 17 00:00:00 2001 From: Julien Biezemans Date: Tue, 6 Sep 2011 23:14:42 +0200 Subject: [PATCH] Stop polluting the global namespace with Given(), When() and Then() (close #2) --- features/cli.feature | 6 +- features/step_definitions/calculator_steps.js | 1 + features/step_definitions/cli_steps.js | 6 +- .../step_definitions/cucumber_js_mappings.rb | 4 +- features/step_definitions/cucumber_steps.js | 7 +- .../step_definitions/legacy/cucumber_steps.js | 11 +- lib/cucumber/cli/support_code_loader.js | 8 +- lib/cucumber/support_code/library.js | 41 ++----- spec/cucumber/cli/support_code_loader_spec.js | 33 +++-- spec/cucumber/support_code/library_spec.js | 114 +++++++----------- 10 files changed, 104 insertions(+), 127 deletions(-) diff --git a/features/cli.feature b/features/cli.feature index 24bce6a35..c95b860b4 100644 --- a/features/cli.feature +++ b/features/cli.feature @@ -13,7 +13,7 @@ Feature: Command line interface Given a file named "features/step_definitions/cucumber_steps.js" with: """ var cucumberSteps = function() { - When(/^a step is passing$/, function(callback) { callback(); }); + this.When(/^a step is passing$/, function(callback) { callback(); }); }; module.exports = cucumberSteps; """ @@ -54,7 +54,7 @@ Feature: Command line interface Given a file named "step_definitions/cucumber_steps.js" with: """ var cucumberSteps = function() { - When(/^a step is passing$/, function(callback) { callback(); }); + this.When(/^a step is passing$/, function(callback) { callback(); }); }; module.exports = cucumberSteps; """ @@ -78,7 +78,7 @@ Feature: Command line interface Given a file named "step_definitions/cucumber_steps.js" with: """ var cucumberSteps = function() { - When(/^a step is passing$/, function(callback) { callback(); }); + this.When(/^a step is passing$/, function(callback) { callback(); }); }; module.exports = cucumberSteps; """ diff --git a/features/step_definitions/calculator_steps.js b/features/step_definitions/calculator_steps.js index 59639f8b5..c84f5d19a 100644 --- a/features/step_definitions/calculator_steps.js +++ b/features/step_definitions/calculator_steps.js @@ -1,4 +1,5 @@ var calculatorSteps = function(Calculator) { + var Given = When = Then = this.defineStep; var calc; function isNumberWithinRangeOfValue(number, range, value) { diff --git a/features/step_definitions/cli_steps.js b/features/step_definitions/cli_steps.js index f09db0a56..70b645d2f 100644 --- a/features/step_definitions/cli_steps.js +++ b/features/step_definitions/cli_steps.js @@ -22,7 +22,7 @@ var cliSteps = function cliSteps() { } }; - Given(/^a file named "(.*)" with:$/, function(filePath, fileContent, callback) { + this.Given(/^a file named "(.*)" with:$/, function(filePath, fileContent, callback) { cleanseIfNeeded(); var absoluteFilePath = tmpPath(filePath); var filePathParts = absoluteFilePath.split('/'); @@ -37,7 +37,7 @@ var cliSteps = function cliSteps() { }); }); - When(/^I run `cucumber.js(| .+)`$/, function(args, callback) { + this.When(/^I run `cucumber.js(| .+)`$/, function(args, callback) { var initialCwd = process.cwd(); process.chdir(tmpDir); var command = baseDir + "/bin/cucumber.js" + args; @@ -52,7 +52,7 @@ var cliSteps = function cliSteps() { }); }); - Then(/^it should pass with exactly:$/, function(expectedOutput, callback) { + this.Then(/^it should pass with exactly:$/, function(expectedOutput, callback) { var actualOutput = lastRun['stdout']; if (actualOutput != expectedOutput) throw new Error("Expected output to match the following:\n'" + expectedOutput + "'\nGot:\n'" + actualOutput + "'."); diff --git a/features/step_definitions/cucumber_js_mappings.rb b/features/step_definitions/cucumber_js_mappings.rb index 4aad60176..1b45747ef 100644 --- a/features/step_definitions/cucumber_js_mappings.rb +++ b/features/step_definitions/cucumber_js_mappings.rb @@ -52,7 +52,7 @@ def write_mappings_for_calculator append_support_code <<-EOF var RpnCalculator = require('../support/rpn_calculator'); var calculatorSteps = require('./calculator_steps'); -calculatorSteps(RpnCalculator); +calculatorSteps.call(this, RpnCalculator); EOF end @@ -94,7 +94,7 @@ def failed_output def append_step_definition(step_name, code) indented_code = indent_code(code).rstrip append_support_code <<-EOF -Given(/#{step_name}/, function(callback) { +this.defineStep(/#{step_name}/, function(callback) { fs.writeFileSync("#{step_file(step_name)}", ""); #{indented_code} }); diff --git a/features/step_definitions/cucumber_steps.js b/features/step_definitions/cucumber_steps.js index 27061adc5..519f33c97 100644 --- a/features/step_definitions/cucumber_steps.js +++ b/features/step_definitions/cucumber_steps.js @@ -1,4 +1,6 @@ var cucumberSteps = function() { + var Given = When = Then = this.defineStep; + var shouldPrepare = true; var featureSource; var stepDefinitions; @@ -66,7 +68,7 @@ var cucumberSteps = function() { When(/^Cucumber runs the scenario with steps for a calculator$/, function(callback) { RpnCalculator = require('../support/rpn_calculator'); - supportCode = function() { require('./calculator_steps')(RpnCalculator) }; + supportCode = function() { require('./calculator_steps').call(this, RpnCalculator) }; runFeatureWithSupportCodeSource(supportCode, callback); }); @@ -127,7 +129,8 @@ var cucumberSteps = function() { function runFeature(callback) { var supportCode; - var supportCodeSource = "supportCode = function() {\n" + stepDefinitions + "};\n"; + var supportCodeSource = "supportCode = function() {\n var Given = When = Then = this.defineStep;\n" + + stepDefinitions + "};\n"; eval(supportCodeSource); runFeatureWithSupportCodeSource(supportCode, callback); } diff --git a/features/step_definitions/legacy/cucumber_steps.js b/features/step_definitions/legacy/cucumber_steps.js index 6e7542b4d..f57999d7c 100644 --- a/features/step_definitions/legacy/cucumber_steps.js +++ b/features/step_definitions/legacy/cucumber_steps.js @@ -1,6 +1,8 @@ var Cucumber = require('../../../lib/cucumber'); var stepDefinitions = function() { + var Given = When = Then = this.defineStep; + var GIVEN_KEYWORD = "Given"; var WHEN_KEYWORD = "When"; var THEN_KEYWORD = "Then"; @@ -210,18 +212,19 @@ var stepDefinitions = function() { var _stepName = RegExp(name); var stepDefinition; if (keyword == THEN_KEYWORD) { - stepDefinition = function() { Then(_stepName, content); }; + stepDefinition = function() { this.Then(_stepName, content); }; } else if (keyword == WHEN_KEYWORD) { - stepDefinition = function() { When(_stepName, content); }; + stepDefinition = function() { this.When(_stepName, content); }; } else { - stepDefinition = function() { Given(_stepName, content); }; + stepDefinition = function() { this.Given(_stepName, content); }; } _stepDefs.push(stepDefinition); }; function _getSupportCode() { + var supportCodeHelper = this; _stepDefs.forEach(function(defineStep) { - defineStep(); + defineStep.call(supportCodeHelper); }); }; diff --git a/lib/cucumber/cli/support_code_loader.js b/lib/cucumber/cli/support_code_loader.js index 068f2bf09..1741342e6 100644 --- a/lib/cucumber/cli/support_code_loader.js +++ b/lib/cucumber/cli/support_code_loader.js @@ -12,8 +12,9 @@ var SupportCodeLoader = function(supportCodeFilePaths) { var primeSupportCodeInitializer = self.getPrimeSupportCodeInitializer(); var secondarySupportCodeInitializer = self.getSecondarySupportCodeInitializer(); var initializer = function() { - primeSupportCodeInitializer(); - secondarySupportCodeInitializer(); + var supportCodeHelper = this; + primeSupportCodeInitializer.call(supportCodeHelper); + secondarySupportCodeInitializer.call(supportCodeHelper); }; return initializer; }, @@ -50,9 +51,10 @@ var SupportCodeLoader = function(supportCodeFilePaths) { buildSupportCodeInitializerFromPaths: function buildSupportCodeInitializerFromPaths(paths) { var wrapper = function(){ + var supportCodeHelper = this; paths.forEach(function(path) { var initializer = require(path); - initializer(); + initializer.call(supportCodeHelper); }); }; return wrapper; diff --git a/lib/cucumber/support_code/library.js b/lib/cucumber/support_code/library.js index bdc3dcb51..d17e4d033 100644 --- a/lib/cucumber/support_code/library.js +++ b/lib/cucumber/support_code/library.js @@ -20,42 +20,19 @@ var Library = function(supportCodeDefinition) { return (stepDefinition != undefined); }, - defineGivenStep: function defineGivenStep(name, code) { - defineStep(name, code); - }, - - defineWhenStep: function defineWhenStep(name, code) { - defineStep(name, code); - }, - - defineThenStep: function defineThenStep(name, code) { - defineStep(name, code); + defineStep: function defineStep(name, code) { + var stepDefinition = Cucumber.SupportCode.StepDefinition(name, code); + stepDefinitions.add(stepDefinition); } }; - withStepDefinitionHelpersDo(function() { - supportCodeDefinition(); - }); - - function withStepDefinitionHelpersDo(callback) { - var originals = { - Given: (typeof(Given) != 'undefined' ? Given : undefined), - When: (typeof(When) != 'undefined' ? When : undefined), - Then: (typeof(Then) != 'undefined' ? Then : undefined) - }; - Given = self.defineGivenStep; - When = self.defineWhenStep; - Then = self.defineThenStep; - callback(); - Given = originals['Given']; - When = originals['When']; - Then = originals['Then']; - }; - - function defineStep(name, code) { - var stepDefinition = Cucumber.SupportCode.StepDefinition(name, code); - stepDefinitions.add(stepDefinition); + var supportCodeHelper = { + Given : self.defineStep, + When : self.defineStep, + Then : self.defineStep, + defineStep : self.defineStep }; + supportCodeDefinition.call(supportCodeHelper); return self; }; diff --git a/spec/cucumber/cli/support_code_loader_spec.js b/spec/cucumber/cli/support_code_loader_spec.js index 3ece15924..2d92c2030 100644 --- a/spec/cucumber/cli/support_code_loader_spec.js +++ b/spec/cucumber/cli/support_code_loader_spec.js @@ -65,21 +65,32 @@ describe("Cucumber.Cli.SupportCodeLoader", function() { }); describe("returned function", function() { - var initializerFunction; + var initializerFunction, supportCodeHelper; beforeEach(function() { initializerFunction = supportCodeLoader.getSupportCodeInitializer(); + supportCodeHelper = createSpy("support code helper"); }); it("calls the prime support code", function() { - initializerFunction(); + initializerFunction.call(supportCodeHelper); expect(primeSupportCodeInitializer).toHaveBeenCalled(); }); + it("calls the prime support code with the support code helper as 'this'", function() { + initializerFunction.call(supportCodeHelper); + expect(primeSupportCodeInitializer.mostRecentCall.object).toBe(supportCodeHelper); + }); + it("calls the secondary support code", function() { - initializerFunction(); + initializerFunction.call(supportCodeHelper); expect(secondarySupportCodeInitializer).toHaveBeenCalled(); }); + + it("calls the secondary support code with the support code helper as 'this'", function() { + initializerFunction.call(supportCodeHelper); + expect(secondarySupportCodeInitializer.mostRecentCall.object).toBe(supportCodeHelper); + }); }); }); @@ -172,7 +183,7 @@ describe("Cucumber.Cli.SupportCodeLoader", function() { }); describe("returned wrapper function", function() { - var initializers, returnedFunction; + var initializers, returnedWrapperFunction, supportCodeHelper; beforeEach(function() { initializers = []; @@ -180,22 +191,30 @@ describe("Cucumber.Cli.SupportCodeLoader", function() { var initializer = spyOnModule(path); initializers.push(initializer); }); - returnedFunction = supportCodeLoader.buildSupportCodeInitializerFromPaths(paths); + returnedWrapperFunction = supportCodeLoader.buildSupportCodeInitializerFromPaths(paths); + supportCodeHelper = createSpy("support code helper"); }); it("requires each initializer", function() { - returnedFunction(); + returnedWrapperFunction.call(supportCodeHelper); initializers.forEach(function(initializer) { expect(initializer).toHaveBeenRequired(); }); }); it("calls each initializer function", function() { - returnedFunction(); + returnedWrapperFunction.call(supportCodeHelper); initializers.forEach(function(initializer) { expect(initializer).toHaveBeenCalled(); }); }); + + it("calls each initializer function with the support code helper as 'this'", function() { + returnedWrapperFunction.call(supportCodeHelper); + initializers.forEach(function(initializer) { + expect(initializer.mostRecentCall.object).toBe(supportCodeHelper); + }); + }); }); }); }); diff --git a/spec/cucumber/support_code/library_spec.js b/spec/cucumber/support_code/library_spec.js index 4890df95e..10bd2c0d7 100644 --- a/spec/cucumber/support_code/library_spec.js +++ b/spec/cucumber/support_code/library_spec.js @@ -7,11 +7,7 @@ describe("Cucumber.SupportCode.Library", function() { var spiesDuringSupportCodeDefinitionExecution = {}; beforeEach(function() { - rawSupportCode = createSpy("Raw support code").andCallFake(function() { - spiesDuringSupportCodeDefinitionExecution['Given'] = (typeof(Given) !== 'undefined' ? Given : undefined); - spiesDuringSupportCodeDefinitionExecution['When'] = (typeof(When) !== 'undefined' ? When : undefined); - spiesDuringSupportCodeDefinitionExecution['Then'] = (typeof(Then) !== 'undefined' ? Then : undefined); - }); + rawSupportCode = createSpy("Raw support code"); stepDefinitionCollection = [ createSpyWithStubs("First step definition", {matchesStepName:false}), createSpyWithStubs("Second step definition", {matchesStepName:false}), @@ -19,63 +15,47 @@ describe("Cucumber.SupportCode.Library", function() { ]; spyOnStub(stepDefinitionCollection, 'syncForEach').andCallFake(function(cb) { stepDefinitionCollection.forEach(cb); }); spyOn(Cucumber.Type, 'Collection').andReturn(stepDefinitionCollection); + library = Cucumber.SupportCode.Library(rawSupportCode); }); describe("constructor", function() { it("creates a collection of step definitions", function() { - library = Cucumber.SupportCode.Library(rawSupportCode); expect(Cucumber.Type.Collection).toHaveBeenCalled(); }); - describe("before executing the raw support code", function() { - beforeEach(function() { - library = Cucumber.SupportCode.Library(rawSupportCode); - }); + it("executes the raw support code", function() { + expect(rawSupportCode).toHaveBeenCalled(); + }); - it("binds the global 'Given' to the library", function() { - var given = spiesDuringSupportCodeDefinitionExecution['Given']; - expect(given).toBeAFunction(); - expect(given).toBe(library.defineGivenStep); - }); + it("executes the raw support code with a support code helper as 'this'", function() { + expect(rawSupportCode.mostRecentCall.object).toBeDefined(); + }); - it("binds the global 'When' to the library", function() { - var given = spiesDuringSupportCodeDefinitionExecution['When']; - expect(given).toBeAFunction(); - expect(given).toBe(library.defineWhenStep); - }); + describe("code support helper", function() { + var supportCodeHelper; - it("binds the global 'Then' to the library", function() { - var given = spiesDuringSupportCodeDefinitionExecution['Then']; - expect(given).toBeAFunction(); - expect(given).toBe(library.defineThenStep); + beforeEach(function() { + supportCodeHelper = rawSupportCode.mostRecentCall.object; }); - }); - it("executes the raw support code", function() { - library = Cucumber.SupportCode.Library(rawSupportCode); - expect(rawSupportCode).toHaveBeenCalled(); - }); + it("supplies a method to define Given steps", function() { + expect(supportCodeHelper.Given).toBeAFunction(); + expect(supportCodeHelper.Given).toBe(library.defineStep); + }); - describe("after executing the raw support code", function() { - it("restores the global 'Given' to its original value", function() { - var originalGiven = createSpy("Original Given"); - Given = originalGiven; - Cucumber.SupportCode.Library(rawSupportCode); - expect(Given).toBe(originalGiven); + it("supplies a method to define When steps", function() { + expect(supportCodeHelper.When).toBeAFunction(); + expect(supportCodeHelper.When).toBe(library.defineStep); }); - it("restores the global 'When' to its original value", function() { - var originalWhen = createSpy("Original When"); - When = originalWhen; - Cucumber.SupportCode.Library(rawSupportCode); - expect(When).toBe(originalWhen); + it("supplies a method to define Then steps", function() { + expect(supportCodeHelper.Then).toBeAFunction(); + expect(supportCodeHelper.Then).toBe(library.defineStep); }); - it("restores the global 'Then' to its original value", function() { - var originalThen = createSpy("Original Then"); - Then = originalThen; - Cucumber.SupportCode.Library(rawSupportCode); - expect(Then).toBe(originalThen); + it("supplies a method to define any step", function() { + expect(supportCodeHelper.defineStep).toBeAFunction(); + expect(supportCodeHelper.defineStep).toBe(library.defineStep); }); }); }); @@ -96,7 +76,7 @@ describe("Cucumber.SupportCode.Library", function() { }); it("returns the step definition that matches the name", function() { - var matchingStepDefinition = stepDefinitionCollection[1]; + var matchingStepDefinition = stepDefinitionCollection[1]; matchingStepDefinition.matchesStepName.andReturn(true); expect(library.lookupStepDefinitionByName(stepName)).toBe(matchingStepDefinition); }); @@ -139,34 +119,26 @@ describe("Cucumber.SupportCode.Library", function() { }); }); - function describeStepDefiner(methodName) { - describe(methodName + "()", function() { - var stepRegexp, stepCode; - var stepDefinition; - - beforeEach(function() { - library = Cucumber.SupportCode.Library(rawSupportCode); - stepRegexp = createSpy("Step name"); - stepCode = createSpy("Step code"); - stepDefinition = createSpy("Step definition"); - spyOn(Cucumber.SupportCode, 'StepDefinition').andReturn(stepDefinition); - spyOnStub(stepDefinitionCollection, 'add'); - }); + describe("defineStep()", function() { + var name, code, stepDefinition; - it("creates a step definition", function() { - library[methodName](stepRegexp, stepCode); - expect(Cucumber.SupportCode.StepDefinition).toHaveBeenCalledWith(stepRegexp, stepCode); - }); + 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("adds the step definition to the step definition collection", function() { - library[methodName](stepRegexp, stepCode); - expect(stepDefinitionCollection.add).toHaveBeenCalledWith(stepDefinition); - }); + it("creates a step definition with the name and code", function() { + library.defineStep(name, code); + expect(Cucumber.SupportCode.StepDefinition).toHaveBeenCalledWith(name, code); }); - }; - describeStepDefiner('defineGivenStep'); - describeStepDefiner('defineWhenStep'); - describeStepDefiner('defineThenStep'); + it("adds the step definition to the step collection", function() { + library.defineStep(name, code); + expect(stepDefinitionCollection.add).toHaveBeenCalledWith(stepDefinition); + }); + }); });