Skip to content

Commit

Permalink
Add support for pending steps (close #18)
Browse files Browse the repository at this point in the history
- This marks the third core.feature scenario passing
- The progress formatter is also ready for pending steps
  • Loading branch information
jbpros committed Jul 23, 2011
1 parent eabb847 commit b517623
Show file tree
Hide file tree
Showing 17 changed files with 632 additions and 164 deletions.
10 changes: 8 additions & 2 deletions lib/cucumber/ast/tree_walker.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,10 @@ var TreeWalker = function(features, supportCodeLibrary, listeners) {
},

visitStepResult: function visitStepResult(stepResult, callback) {
if (!stepResult.isSuccessful())
if (stepResult.isFailed())
self.witnessFailedStep();
else if (stepResult.isPending())
self.witnessPendingStep();
var payload = { stepResult: stepResult };
var event = TreeWalker.Event(TreeWalker.STEP_RESULT_EVENT_NAME, payload);
self.broadcastEvent(event, callback);
Expand Down Expand Up @@ -102,6 +104,10 @@ var TreeWalker = function(features, supportCodeLibrary, listeners) {
skippingSteps = true;
},

witnessPendingStep: function witnessPendingStep() {
skippingSteps = true;
},

witnessNewScenario: function witnessNewScenario() {
skippingSteps = false;
},
Expand Down Expand Up @@ -139,4 +145,4 @@ TreeWalker.AFTER_EVENT_NAME_PREFIX = 'After';
TreeWalker.NON_EVENT_LEADING_PARAMETERS_COUNT = 0;
TreeWalker.NON_EVENT_TRAILING_PARAMETERS_COUNT = 2;
TreeWalker.Event = require('./tree_walker/event');
module.exports = TreeWalker;
module.exports = TreeWalker;
65 changes: 55 additions & 10 deletions lib/cucumber/listener/progress_formatter.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,19 @@ var ProgressFormatter = function(options) {
var beforeEachScenarioUserFunctions = Cucumber.Type.Collection();
var logs = "";
var passedScenarios = 0;
var pendingScenarios = 0;
var failedScenarios = 0;
var passedSteps = 0;
var failedSteps = 0;
var skippedSteps = 0;
var pendingSteps = 0;
var currentScenarioFailing = false;
var currentScenarioPending = false;

if (!options)
options = {};
if (options['logToConsole'] == undefined)
options['logToConsole'] = true;

var self = {
beforeEachScenarioDo: function beforeEachScenarioDo(userFunction) {
beforeEachScenarioUserFunctions.add(userFunction);
Expand Down Expand Up @@ -68,7 +70,11 @@ var ProgressFormatter = function(options) {
var stepResult = event.getPayloadItem('stepResult');
if (stepResult.isSuccessful()) {
self.witnessPassedStep();
self.log(ProgressFormatter.PASSING_STEP_CHARACTER);
self.log(ProgressFormatter.PASSED_STEP_CHARACTER);
} else if (stepResult.isPending()) {
self.witnessPendingStep();
self.markCurrentScenarioAsPending();
self.log(ProgressFormatter.PENDING_STEP_CHARACTER);
} else {
self.witnessFailedStep();
self.markCurrentScenarioAsFailing();
Expand All @@ -91,6 +97,8 @@ var ProgressFormatter = function(options) {
handleAfterScenarioEvent: function handleAfterScenarioEvent(event, callback) {
if (self.isCurrentScenarioFailing()) {
self.witnessFailedScenario();
} else if (self.isCurrentScenarioPending()) {
self.witnessPendingScenario();
} else {
self.witnessPassedScenario();
}
Expand All @@ -99,16 +107,25 @@ var ProgressFormatter = function(options) {

prepareBeforeScenario: function prepareBeforeScenario() {
currentScenarioFailing = false;
currentScenarioPending = false;
},

markCurrentScenarioAsFailing: function markCurrentScenarioAsFailing() {
currentScenarioFailing = true;
},

markCurrentScenarioAsPending: function markCurrentScenarioAsPending() {
currentScenarioPending = true;
},

isCurrentScenarioFailing: function isCurrentScenarioFailing() {
return currentScenarioFailing;
},

isCurrentScenarioPending: function isCurrentScenarioPending() {
return currentScenarioPending;
},

logSummary: function logSummary() {
self.log("\n\n");
self.logScenariosSummary();
Expand All @@ -117,14 +134,17 @@ var ProgressFormatter = function(options) {
},

logScenariosSummary: function logScenariosSummary() {
var scenarioCount = self.getScenarioCount();
var passedScenarioCount = self.getPassedScenarioCount();
var failedScenarioCount = self.getFailedScenarioCount();
var details = [];
var scenarioCount = self.getScenarioCount();
var passedScenarioCount = self.getPassedScenarioCount();
var pendingScenarioCount = self.getPendingScenarioCount();
var failedScenarioCount = self.getFailedScenarioCount();
var details = [];

self.log(scenarioCount + " scenario" + (scenarioCount != 1 ? "s" : ""));
if (failedScenarioCount > 0)
details.push(failedScenarioCount + " failed");
if (pendingScenarioCount > 0)
details.push(pendingScenarioCount + " pending");
if (passedScenarioCount > 0)
details.push(passedScenarioCount + " passed");
self.log(" (" + details.join(', ') + ")\n");
Expand All @@ -134,12 +154,15 @@ var ProgressFormatter = function(options) {
var stepCount = self.getStepCount();
var passedStepCount = self.getPassedStepCount();
var skippedStepCount = self.getSkippedStepCount();
var pendingStepCount = self.getPendingStepCount();
var failedStepCount = self.getFailedStepCount();
var details = [];

self.log(stepCount + " step" + (stepCount != 1 ? "s" : ""));
if (failedStepCount > 0)
details.push(failedStepCount + " failed");
if (pendingStepCount > 0)
details.push(pendingStepCount + " pending");
if (skippedStepCount > 0)
details.push(skippedStepCount + " skipped");
if (passedStepCount > 0)
Expand All @@ -151,6 +174,10 @@ var ProgressFormatter = function(options) {
passedScenarios++;
},

witnessPendingScenario: function witnessPendingScenario() {
pendingScenarios++;
},

witnessFailedScenario: function witnessFailedScenario() {
failedScenarios++;
},
Expand All @@ -159,6 +186,10 @@ var ProgressFormatter = function(options) {
passedSteps++;
},

witnessPendingStep: function witnessPendingStep() {
pendingSteps++;
},

witnessFailedStep: function witnessFailedStep() {
failedSteps++;
},
Expand All @@ -168,25 +199,38 @@ var ProgressFormatter = function(options) {
},

getScenarioCount: function getScenarioCount() {
return self.getPassedScenarioCount() + self.getFailedScenarioCount();
return self.getPassedScenarioCount() + self.getPendingScenarioCount() + self.getFailedScenarioCount();
},

getPassedScenarioCount: function getPassedScenarioCount() {
return passedScenarios;
},

getPendingScenarioCount: function getPendingScenarioCount() {
return pendingScenarios;
},

getFailedScenarioCount: function getFailedScenarioCount() {
return failedScenarios;
},

getStepCount: function getStepCount() {
return self.getPassedStepCount() + self.getSkippedStepCount() + self.getFailedStepCount();
var stepCount =
self.getPassedStepCount() +
self.getSkippedStepCount() +
self.getPendingStepCount() +
self.getFailedStepCount();
return stepCount;
},

getPassedStepCount: function getPassedStepCount() {
return passedSteps;
},

getPendingStepCount: function getPendingStepCount() {
return pendingSteps;
},

getFailedStepCount: function getFailedStepCount() {
return failedSteps;
},
Expand All @@ -197,8 +241,9 @@ var ProgressFormatter = function(options) {
};
return self;
};
ProgressFormatter.PASSING_STEP_CHARACTER = '.';
ProgressFormatter.SKIPPED_STEP_CHARACTER = '-';
ProgressFormatter.PASSED_STEP_CHARACTER = '.';
ProgressFormatter.SKIPPED_STEP_CHARACTER = '-';
ProgressFormatter.PENDING_STEP_CHARACTER = 'P';
ProgressFormatter.FAILED_STEP_CHARACTER = 'F';
ProgressFormatter.EVENT_HANDLER_NAME_PREFIX = 'handle';
ProgressFormatter.EVENT_HANDLER_NAME_SUFFIX = 'Event';
Expand Down
7 changes: 5 additions & 2 deletions lib/cucumber/runtime.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
var Runtime = {};
Runtime.StepResult = require('./runtime/step_result');
module.exports = Runtime;
Runtime.PendingStepException = require('./runtime/pending_step_exception');
Runtime.SuccessfulStepResult = require('./runtime/successful_step_result');
Runtime.PendingStepResult = require('./runtime/pending_step_result');
Runtime.FailedStepResult = require('./runtime/failed_step_result');
module.exports = Runtime;
9 changes: 9 additions & 0 deletions lib/cucumber/runtime/failed_step_result.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
var FailedStepResult = function(status) {
var self = {
isSuccessful: function isSuccessful() { return false; },
isPending: function isPending() { return false; },
isFailed: function isFailed() { return true; }
};
return self;
};
module.exports = FailedStepResult;
5 changes: 5 additions & 0 deletions lib/cucumber/runtime/pending_step_exception.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
var PendingStepException = function PendingStepException(reason) {
if (!(this instanceof PendingStepException))
return new PendingStepException(reason);
};
module.exports = PendingStepException;
9 changes: 9 additions & 0 deletions lib/cucumber/runtime/pending_step_result.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
var PendingStepResult = function(status) {
var self = {
isSuccessful: function isSuccessful() { return false; },
isPending: function isPending() { return true; },
isFailed: function isFailed() { return false; }
};
return self;
};
module.exports = PendingStepResult;
9 changes: 0 additions & 9 deletions lib/cucumber/runtime/step_result.js

This file was deleted.

9 changes: 9 additions & 0 deletions lib/cucumber/runtime/successful_step_result.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
var SuccessfulStepResult = function(status) {
var self = {
isSuccessful: function isSuccessful() { return true; },
isPending: function isPending() { return false; },
isFailed: function isFailed() { return false; }
};
return self;
};
module.exports = SuccessfulStepResult;
12 changes: 9 additions & 3 deletions lib/cucumber/support_code/step_definition.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,20 @@ var StepDefinition = function(regexp, code) {

invoke: function invoke(stepName, docString, callback) {
var codeCallback = function() {
var stepResult = Cucumber.Runtime.StepResult(true);
callback(stepResult);
var successfulStepResult = Cucumber.Runtime.SuccessfulStepResult();
callback(successfulStepResult);
};
codeCallback.pending = function pending(reason) {
throw Cucumber.Runtime.PendingStepException(reason);
};

var parameters = self.buildInvocationParameters(stepName, docString, codeCallback);
try {
code.apply(undefined, parameters);
} catch (err) {
var stepResult = Cucumber.Runtime.StepResult(false);
var stepResult = (err instanceof Cucumber.Runtime.PendingStepException) ?
Cucumber.Runtime.PendingStepResult() :
Cucumber.Runtime.FailedStepResult();
callback(stepResult);
}
},
Expand Down
65 changes: 52 additions & 13 deletions spec/cucumber/ast/tree_walker_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -240,7 +240,7 @@ describe("Cucumber.Ast.TreeWalker", function() {
var stepResult, callback, event, payload;

beforeEach(function() {
stepResult = createSpyWithStubs("Step result", {isSuccessful: undefined});
stepResult = createSpyWithStubs("Step result", {isFailed: undefined, isPending: undefined});
callback = createSpy("Callback");
event = createSpy("Event");
payload = {stepResult: stepResult};
Expand All @@ -259,30 +259,58 @@ describe("Cucumber.Ast.TreeWalker", function() {
expect(treeWalker.broadcastEvent).toHaveBeenCalledWith(event, callback);
});

it("checks wether the step succeeded or not", function() {
it("checks wether the step failed or not", function() {
treeWalker.visitStepResult(stepResult, callback);
expect(stepResult.isSuccessful).toHaveBeenCalled();
expect(stepResult.isFailed).toHaveBeenCalled();
});

describe("when the step succeeded", function() {
describe("when the step failed", function() {
beforeEach(function() {
stepResult.isSuccessful.andReturn(true);
stepResult.isFailed.andReturn(true);
});

it("does not witness a failed step", function() {
it("witnesses a failed step", function() {
treeWalker.visitStepResult(stepResult, callback);
expect(treeWalker.witnessFailedStep).not.toHaveBeenCalled();
expect(treeWalker.witnessFailedStep).toHaveBeenCalled();
});
});

describe("when the step failed", function() {
describe("when the step did not fail", function() {
beforeEach(function() {
stepResult.isSuccessful.andReturn(false);
stepResult.isFailed.andReturn(false);
spyOn(treeWalker, 'witnessPendingStep');
});

it("witnesses a failed step", function() {
it("does not witness a failed step", function() {
treeWalker.visitStepResult(stepResult, callback);
expect(treeWalker.witnessFailedStep).toHaveBeenCalled();
expect(treeWalker.witnessFailedStep).not.toHaveBeenCalled();
});

it("checks wether the step was pending or not", function() {
treeWalker.visitStepResult(stepResult, callback);
expect(stepResult.isPending).toHaveBeenCalled();
});

describe("when the step was pending", function() {
beforeEach(function() {
stepResult.isPending.andReturn(true);
});

it("witnesses a pending step", function() {
treeWalker.visitStepResult(stepResult, callback);
expect(treeWalker.witnessPendingStep).toHaveBeenCalled();
});
});

describe("when the step was not pending", function() {
beforeEach(function() {
stepResult.isPending.andReturn(false);
});

it("does not witness a pending step", function() {
treeWalker.visitStepResult(stepResult, callback);
expect(treeWalker.witnessPendingStep).not.toHaveBeenCalled();
});
});
});

Expand Down Expand Up @@ -486,8 +514,8 @@ describe("Cucumber.Ast.TreeWalker", function() {
});
});

describe("isSkippingSteps() [witnessFailedSteps()]", function() {
it("returns false when no failure was encountered", function() {
describe("isSkippingSteps() [witnessFailedSteps(), witnessPendingSteps(), witnessNewScenario()]", function() {
it("returns false when no failed or pending step were encountered", function() {
expect(treeWalker.isSkippingSteps()).toBeFalsy();
});

Expand All @@ -501,6 +529,17 @@ describe("Cucumber.Ast.TreeWalker", function() {
treeWalker.witnessNewScenario();
expect(treeWalker.isSkippingSteps()).toBeFalsy();
});

it("returns true when a pending step was encountered", function() {
treeWalker.witnessPendingStep();
expect(treeWalker.isSkippingSteps()).toBeTruthy();
});

it("returns false when a pending step was encountered but not in the current scenario", function() {
treeWalker.witnessPendingStep();
treeWalker.witnessNewScenario();
expect(treeWalker.isSkippingSteps()).toBeFalsy();
});
});

describe("executeStep()", function() {
Expand Down
Loading

0 comments on commit b517623

Please sign in to comment.