UNPKG

cucumber

Version:

The official JavaScript implementation of Cucumber.

462 lines (379 loc) 19.3 kB
require('../../support/spec_helper'); describe("Cucumber.SupportCode.StepDefinition", function () { var Cucumber = requireLib('cucumber'); var stepDefinition, pattern, stepDefinitionCode; beforeEach(function () { pattern = createSpyWithStubs("pattern", {test: null}); stepDefinitionCode = createSpy("step definition code"); spyOn(global, 'RegExp'); stepDefinition = Cucumber.SupportCode.StepDefinition(pattern, stepDefinitionCode); }); describe("getPatternRegexp()", function () { describe("when the pattern is a RegExp", function () { it("does not instantiate a RegExp", function () { expect(global.RegExp).not.toHaveBeenCalled(); }); it("returns the pattern itself", function () { expect(stepDefinition.getPatternRegexp()).toBe(pattern); }); }); describe("when the pattern is a String", function () { var regexp, quotedDollarParameterSubstitutedPattern, safeString, regexpString; beforeEach(function () { regexp = createSpy("regexp"); regexpString = "regexp string"; safeString = createSpyWithStubs("safe string"); quotedDollarParameterSubstitutedPattern = createSpyWithStubs("quoted dollar param substituted pattern", {replace: regexpString}); spyOnStub(pattern, 'replace').andReturn(safeString); spyOnStub(safeString, 'replace').andReturn(quotedDollarParameterSubstitutedPattern); global.RegExp.andReturn(regexp); }); it("escapes unsafe regexp characters from the string", function () { stepDefinition.getPatternRegexp(); expect(pattern.replace).toHaveBeenCalledWith(Cucumber.SupportCode.StepDefinition.UNSAFE_STRING_CHARACTERS_REGEXP, Cucumber.SupportCode.StepDefinition.PREVIOUS_REGEXP_MATCH); }); it("replaces quoted dollar-prefixed parameters with the regexp equivalent", function () { stepDefinition.getPatternRegexp(); expect(safeString.replace).toHaveBeenCalledWith(Cucumber.SupportCode.StepDefinition.QUOTED_DOLLAR_PARAMETER_REGEXP, Cucumber.SupportCode.StepDefinition.QUOTED_DOLLAR_PARAMETER_SUBSTITUTION); }); it("replaces other dollar-prefixed parameter with the regexp equivalent", function () { stepDefinition.getPatternRegexp(); expect(quotedDollarParameterSubstitutedPattern.replace).toHaveBeenCalledWith(Cucumber.SupportCode.StepDefinition.DOLLAR_PARAMETER_REGEXP, Cucumber.SupportCode.StepDefinition.DOLLAR_PARAMETER_SUBSTITUTION); }); it("instantiates a new RegExp", function () { stepDefinition.getPatternRegexp(); expect(global.RegExp).toHaveBeenCalledWith("^" + regexpString + "$"); }); it("returns the new RegExp", function () { expect(stepDefinition.getPatternRegexp()).toBe(regexp); }); }); }); describe("matchesStepName()", function () { var patternRegexp, stepName, matchResult; beforeEach(function () { stepName = createSpy("step name"); matchResult = createSpy("step match result (boolean)"); patternRegexp = createSpyWithStubs("pattern regexp", {test: matchResult}); spyOn(stepDefinition, 'getPatternRegexp').andReturn(patternRegexp); }); it("gets the pattern regexp", function () { stepDefinition.matchesStepName(stepName); expect(stepDefinition.getPatternRegexp).toHaveBeenCalled(); }); it("tests the string against the step name", function () { stepDefinition.matchesStepName(stepName); expect(patternRegexp.test).toHaveBeenCalledWith(stepName); }); it("returns the match result", function () { expect(stepDefinition.matchesStepName(stepName)).toBe(matchResult); }); }); describe("invoke()", function () { var step, world, scenario, stepDomain, callback; var parameters, exceptionHandler; var timestamp = 0; beforeEach(function () { step = createSpy("step"); world = createSpy("world"); scenario = createSpyWithStubs("scenario", {getAttachments: undefined}); stepDomain = createSpy("stepDomain"); callback = createSpy("callback"); parameters = createSpy("code execution parameters"); exceptionHandler = createSpy("exception handler"); spyOn(Cucumber.Util.Exception, 'registerUncaughtExceptionHandler'); spyOn(stepDefinition, 'buildCodeCallback').andCallFake(function (codeCallback) { return codeCallback; }); spyOn(stepDefinition, 'buildInvocationParameters').andReturn(parameters); spyOn(stepDefinition, 'buildExceptionHandlerToCodeCallback').andReturn(exceptionHandler); spyOn(stepDefinitionCode, 'apply'); if (process.hrtime) { spyOn(process, 'hrtime').andCallFake(function (time) { if (time) { return [0 - time[0], (timestamp * 1e6) - time[1]]; } else { return [0, timestamp * 1e6]; } }); } else { spyOn(global, 'Date').andCallFake(function () { return { getTime: function () { return timestamp; } }; }); } }); it("builds the step invocation parameters", function () { stepDefinition.invoke(step, world, scenario, stepDomain, callback); expect(stepDefinition.buildInvocationParameters).toHaveBeenCalled(); expect(stepDefinition.buildInvocationParameters).toHaveBeenCalledWithValueAsNthParameter(step, 1); expect(stepDefinition.buildInvocationParameters).toHaveBeenCalledWithValueAsNthParameter(scenario, 2); expect(stepDefinition.buildInvocationParameters).toHaveBeenCalledWithAFunctionAsNthParameter(3); }); it("builds an exception handler for the code callback", function () { stepDefinition.invoke(step, world, scenario, stepDomain, callback); expect(stepDefinition.buildExceptionHandlerToCodeCallback).toHaveBeenCalledWithAFunctionAsNthParameter(1); var codeExecutionCallbackPassedToParameterBuilder = stepDefinition.buildInvocationParameters.mostRecentCall.args[2]; var codeExecutionCallbackPassedToExceptionHandlerBuilder = stepDefinition.buildExceptionHandlerToCodeCallback.mostRecentCall.args[0]; var domainPassedToExceptionHandlerBuilder = stepDefinition.buildExceptionHandlerToCodeCallback.mostRecentCall.args[1]; expect(codeExecutionCallbackPassedToExceptionHandlerBuilder).toBe(codeExecutionCallbackPassedToParameterBuilder); expect(domainPassedToExceptionHandlerBuilder).toBe(stepDomain); }); it("registers the exception handler for uncaught exceptions", function () { stepDefinition.invoke(step, world, scenario, stepDomain, callback); expect(Cucumber.Util.Exception.registerUncaughtExceptionHandler).toHaveBeenCalledWith(exceptionHandler, stepDomain); }); it("calls the step definition code with the parameters and World as 'this'", function () { stepDefinition.invoke(step, world, scenario, stepDomain, callback); expect(stepDefinitionCode.apply).toHaveBeenCalledWith(world, parameters); }); it("builds the code callback", function () { stepDefinition.invoke(step, world, scenario, stepDomain, callback); expect(stepDefinition.buildCodeCallback).toHaveBeenCalled(); expect(stepDefinition.buildCodeCallback).toHaveBeenCalledWithAFunctionAsNthParameter(1); }); describe("callback used to build the code callback", function () { var codeExecutionCallback, successfulStepResult, attachments; beforeEach(function () { stepDefinition.invoke(step, world, scenario, stepDomain, callback); codeExecutionCallback = stepDefinition.buildCodeCallback.mostRecentCall.args[0]; successfulStepResult = createSpy("successful step result"); attachments = createSpy("attachments"); spyOn(Cucumber.Runtime, 'SuccessfulStepResult').andReturn(successfulStepResult); spyOn(Cucumber.Util.Exception, 'unregisterUncaughtExceptionHandler'); }); it("is passed to the step definition code", function () { expect(stepDefinition.buildInvocationParameters.mostRecentCall.args[2]).toBe(codeExecutionCallback); }); describe("when called without an error", function () { beforeEach(function () { timestamp = 1; spyOn(codeExecutionCallback, 'fail'); spyOnStub(scenario, 'getAttachments').andReturn(attachments); codeExecutionCallback(); }); it("gets the attachments from the scenario", function () { expect(scenario.getAttachments).toHaveBeenCalled(); }); it("creates a successful step result", function () { expect(Cucumber.Runtime.SuccessfulStepResult).toHaveBeenCalledWith({step: step, duration: 1e6, attachments: attachments}); }); it("unregisters the exception handler", function () { expect(Cucumber.Util.Exception.unregisterUncaughtExceptionHandler).toHaveBeenCalledWith(exceptionHandler, stepDomain); }); it("calls back", function () { expect(callback).toHaveBeenCalledWith(successfulStepResult); }); it("does not fail", function () { expect(codeExecutionCallback.fail).not.toHaveBeenCalled(); }); afterEach(function () { timestamp = 0; }); }); describe("when called with an error", function () { var error; beforeEach(function () { error = createSpy("error"); spyOn(codeExecutionCallback, 'fail'); codeExecutionCallback(error); }); it("does not create a successful step result", function () { expect(Cucumber.Runtime.SuccessfulStepResult).not.toHaveBeenCalled(); }); it("does not unregister the exception handler", function () { expect(Cucumber.Util.Exception.unregisterUncaughtExceptionHandler).not.toHaveBeenCalled(); }); it("does not call back", function () { expect(callback).not.toHaveBeenCalled(); }); it("fails", function () { expect(codeExecutionCallback.fail).toHaveBeenCalledWith(error); }); }); describe("pending()", function () { var pendingReason, pendingStepResult; beforeEach(function () { pendingReason = createSpy("pending reason"); pendingStepResult = createSpy("pending step result"); spyOnStub(scenario, "getAttachments").andReturn(attachments); spyOn(Cucumber.Runtime, 'PendingStepResult').andReturn(pendingStepResult); }); it("gets the attachments from the scenario", function () { codeExecutionCallback.pending(pendingReason); expect(scenario.getAttachments).toHaveBeenCalled(); }); it("creates a pending step result", function () { codeExecutionCallback.pending(pendingReason); expect(Cucumber.Runtime.PendingStepResult).toHaveBeenCalledWith({step: step, pendingReason: pendingReason, attachments: attachments}); }); it("unregisters the exception handler", function () { codeExecutionCallback.pending(pendingReason); expect(Cucumber.Util.Exception.unregisterUncaughtExceptionHandler).toHaveBeenCalledWith(exceptionHandler, stepDomain); }); it("calls back", function () { codeExecutionCallback.pending(pendingReason); expect(callback).toHaveBeenCalledWith(pendingStepResult); }); }); describe("fail()", function () { var failureReason, failedStepResult; beforeEach(function () { timestamp = 1; failureReason = createSpy("failure reason"); failedStepResult = createSpy("failed step result"); spyOnStub(scenario, "getAttachments").andReturn(attachments); spyOn(Cucumber.Runtime, 'FailedStepResult').andReturn(failedStepResult); }); it("gets the attachments from the scenario", function () { codeExecutionCallback.fail(failureReason); expect(scenario.getAttachments).toHaveBeenCalled(); }); it("creates a failing step result", function () { codeExecutionCallback.fail(failureReason); expect(Cucumber.Runtime.FailedStepResult).toHaveBeenCalledWith({step: step, failureException: failureReason, duration: 1e6, attachments: attachments}); }); describe("when no failure reason is given", function () { it("creates a failing step result with a generic step failure exception", function () { codeExecutionCallback.fail(); var payload = Cucumber.Runtime.FailedStepResult.mostRecentCall.args[0]; expect(payload.step).toBe(step); expect(payload.failureException).toBeAnInstanceOf(Error); }); }); it("unregisters the exception handler", function () { codeExecutionCallback.fail(failureReason); expect(Cucumber.Util.Exception.unregisterUncaughtExceptionHandler).toHaveBeenCalledWith(exceptionHandler, stepDomain); }); it("calls back", function () { codeExecutionCallback.fail(failureReason); expect(callback).toHaveBeenCalledWith(failedStepResult); }); afterEach(function () { timestamp = 0; }); }); }); describe("when the step definition code throws an exception", function () { var failureException; beforeEach(function () { failureException = createSpy("failing step definition exception"); stepDefinitionCode.apply.andThrow(failureException); }); it("handles the exception with the exception handler", function () { stepDefinition.invoke(step, world, scenario, callback); expect(exceptionHandler).toHaveBeenCalledWith(failureException); }); }); describe("when the step definition is synchronous (no callback, no promise)", function () { xit("calls the code callback without error"); }); describe("when the step definition returns a promise", function () { describe("when the promise resolves", function () { xit("calls the code callback without error"); }); describe("when the promise is rejected", function () { xit("calls the code callback with an error"); }); }); }); describe("buildCodeCallback", function () { it("is just a pass through", function () { var callback = createSpy("callback"); var returnValue = stepDefinition.buildCodeCallback(callback); expect(returnValue).toBe(callback); }); }); describe("buildInvocationParameters()", function () { var patternRegexp, step, stepName, stepAttachmentContents; var matches, scenario, callback; beforeEach(function () { stepName = createSpy("step name to match"); matches = createSpyWithStubs("matches", {shift: null, push: null}); patternRegexp = createSpyWithStubs("pattern regexp", {test: matches}); stepAttachmentContents = createSpy("step attachment contents"); step = createSpyWithStubs("step", {hasAttachment: null, getName: stepName, getAttachmentContents: stepAttachmentContents}); scenario = createSpy("scenario"); callback = createSpy("callback"); spyOn(stepDefinition, 'getPatternRegexp').andReturn(patternRegexp); spyOnStub(patternRegexp, 'exec').andReturn(matches); }); it("gets the step name", function () { stepDefinition.buildInvocationParameters(step, scenario, callback); expect(step.getName).toHaveBeenCalled(); }); it("gets the pattern regexp", function () { stepDefinition.buildInvocationParameters(step, scenario, callback); expect(stepDefinition.getPatternRegexp).toHaveBeenCalled(); }); it("executes the pattern regexp against the step name", function () { stepDefinition.buildInvocationParameters(step, scenario, callback); expect(patternRegexp.exec).toHaveBeenCalledWith(stepName); }); it("removes the whole matched string of the regexp result array (to only keep matching groups)", function () { stepDefinition.buildInvocationParameters(step, scenario, callback); expect(matches.shift).toHaveBeenCalled(); }); it("checks whether the step has an attachment or not", function () { stepDefinition.buildInvocationParameters(step, scenario, callback); expect(step.hasAttachment).toHaveBeenCalled(); }); describe("when the step has an attachment", function () { beforeEach(function () { step.hasAttachment.andReturn(true); }); it("gets the attachment contents", function () { stepDefinition.buildInvocationParameters(step, scenario, callback); expect(step.getAttachmentContents).toHaveBeenCalled(); }); it("adds the attachment contents to the parameter array", function () { stepDefinition.buildInvocationParameters(step, scenario, callback); expect(matches.push).toHaveBeenCalledWith(stepAttachmentContents); }); }); describe("when the step has no attachment", function () { beforeEach(function () { step.hasAttachment.andReturn(false); }); it("does not get the attachment contents", function () { stepDefinition.buildInvocationParameters(step, scenario, callback); expect(step.getAttachmentContents).not.toHaveBeenCalled(); }); it("does not add the attachement contents to the parameter array", function () { stepDefinition.buildInvocationParameters(step, scenario, callback); expect(matches.push).toHaveBeenCalledNTimes(1); }); }); it("adds the callback to the parameter array", function () { stepDefinition.buildInvocationParameters(step, scenario, callback); expect(matches.push).toHaveBeenCalledWith(callback); }); it("returns the parameters", function () { expect(stepDefinition.buildInvocationParameters(step, scenario, callback)).toBe(matches); }); }); describe("buildExceptionHandlerToCodeCallback()", function () { var codeCallback, exceptionHandler, stepDomain; beforeEach(function () { codeCallback = createSpyWithStubs("code callback", {fail: null}); stepDomain = createSpy("step domain"); exceptionHandler = stepDefinition.buildExceptionHandlerToCodeCallback(codeCallback, stepDomain); }); it("returns an exception handler", function () { expect(exceptionHandler).toBeAFunction (); }); describe("returned exception handler", function () { var exception; beforeEach(function () { exception = createSpy("exception"); }); it("calls back as a failure with the exception", function () { exceptionHandler(exception); expect(codeCallback.fail).toHaveBeenCalledWith(exception); }); }); }); });