cucumber
Version:
The official JavaScript implementation of Cucumber.
240 lines (199 loc) • 8.09 kB
JavaScript
var World = function(callback) {
this.touchedSteps = [];
this.featureSource = "";
this.stepDefinitions = "";
this.runOutput = "";
this.cycleEvents = "";
this.stepCount = 0;
this.runSucceeded = false;
World.mostRecentInstance = this;
callback(this);
};
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 Around = this.Around, Before = this.Before, After = this.After;\n" +
this.stepDefinitions + "};\n";
var world = this;
eval(supportCodeSource);
this.runFeatureWithSupportCodeSource(supportCode, options, callback);
}
proto.runFeatureWithSupportCodeSource = function runFeatureWithSupportCodeSource(supportCode, options, callback) {
var world = this;
var Cucumber = require('../../lib/cucumber');
options = options || {};
var tags = options['tags'] || [];
var cucumber = Cucumber(this.featureSource, supportCode, {tags: tags});
var formatter = Cucumber.Listener.ProgressFormatter({logToConsole: false});
cucumber.attachListener(formatter);
try {
cucumber.start(function(succeeded) {
world.runSucceeded = succeeded;
world.runOutput = formatter.getLogs();
Cucumber.Debug.notice(world.runOutput, 'cucumber output', 5);
callback();
});
} catch(e) {
world.runOutput += e.toString();
Cucumber.Debug.notice(world.runOutput, 'cucumber output', 5);
callback();
}
}
proto.runAScenario = function runAScenario(callback) {
this.addScenario("", "Given a step");
this.stepDefinitions += "Given(/^a step$/, function(callback) {\
world.logCycleEvent('step 1');\
callback();\
});";
this.runFeature({}, callback);
}
proto.logCycleEvent = function logCycleEvent(event) {
this.cycleEvents += " -> " + event;
}
proto.touchStep = function touchStep(string) {
this.touchedSteps.push(string);
}
proto.isStepTouched = function isStepTouched(pattern) {
return (this.touchedSteps.indexOf(pattern) >= 0);
}
proto.addScenario = function addScenario(name, contents, options) {
options = options || {};
var tags = options['tags'] || [];
var tagString = (tags.length > 0 ? tags.join(" ") + "\n" : "");
var scenarioName = tagString + "Scenario: " + name;
this.createEmptyFeature();
this.featureSource += this.indentCode(scenarioName, 1);
this.featureSource += this.indentCode(contents, 2);
};
proto.addPassingScenarioWithTags = function addPassingScenarioWithTags(tags) {
tags = Array.prototype.slice.call(arguments, 0);
var stepName = this.makeNumberedStepName();
var scenarioName = "A scenario tagged with " + tags.join(', ');
var step = "Given " + stepName + "\n";
this.addScenario(scenarioName, step, {tags: tags});
this.stepDefinitions += "Given(/^" + stepName + "$/, function(callback) {\
world.logCycleEvent('" + stepName + "');\
callback();\
});\n";
};
proto.addPassingScenarioWithoutTags = function addPassingScenarioWithoutTags() {
this.addPassingScenarioWithTags();
};
proto.createEmptyFeature = function createEmptyFeature(options) {
options = options || {};
tags = options['tags'] || [];
if (!this.emptyFeatureReady) {
if (tags.length > 0)
this.featureSource += tags.join(' ') + "\n";
this.featureSource += "Feature: A feature\n\n";
this.emptyFeatureReady = true;
}
};
proto.makeNumberedStepName = function makeNumberedStepName(index) {
var index = index || (++this.stepCount);
var stepName = "step " + index;
return stepName;
}
proto.assertPassedFeature = function assertPassedFeature() {
this.assertNoPartialOutput("failed", this.runOutput);
this.assertSuccess();
}
proto.assertPassedScenario = function assertPassedScenario() {
this.assertPartialOutput("1 scenario (1 passed)", this.runOutput);
this.assertSuccess();
}
proto.assertFailedScenario = function assertFailedScenario() {
this.assertPartialOutput("1 scenario (1 failed)", this.runOutput);
this.assertFailure();
}
proto.assertPendingScenario = function assertPendingScenario() {
this.assertPartialOutput("1 scenario (1 pending)", this.runOutput);
this.assertSuccess();
}
proto.assertUndefinedScenario = function assertUndefinedScenario() {
this.assertPartialOutput("1 scenario (1 undefined)", this.runOutput);
this.assertSuccess();
}
proto.assertScenarioReportedAsFailing = function assertScenarioReportedAsFailing(scenarioName) {
this.assertPartialOutput("# Scenario: " + scenarioName, this.runOutput);
this.assertFailure();
}
proto.assertScenarioNotReportedAsFailing = function assertScenarioNotReportedAsFailing(scenarioName) {
this.assertNoPartialOutput("# Scenario: " + scenarioName, this.runOutput);
}
proto.assertPassedStep = function assertPassedStep(stepName) {
if (!this.isStepTouched(stepName))
throw(new Error("Expected step \"" + stepName + "\" to have passed."));
}
proto.assertSkippedStep = function assertSkippedStep(stepName) {
if (this.isStepTouched(stepName))
throw(new Error("Expected step \"" + stepName + "\" to have been skipped."));
}
proto.assertSuccess = function assertSuccess() {
if (!this.runSucceeded)
throw(new Error("Expected Cucumber to succeed but it failed."));
}
proto.assertFailure = function assertFailure() {
if (this.runSucceeded)
throw(new Error("Expected Cucumber to fail but it succeeded."));
}
proto.assertFailureMessage = function assertFailureMessage(message) {
this.assertPartialOutput(message, this.runOutput);
this.assertFailure();
}
proto.assertPartialOutput = function assertPartialOutput(expected, actual) {
if (actual.indexOf(expected) < 0)
throw(new Error("Expected:\n\"" + actual + "\"\nto match:\n\"" + expected + "\""));
}
proto.assertNoPartialOutput = function assertNoPartialOutput(expected, actual) {
if (actual.indexOf(expected) >= 0)
throw(new Error("Expected:\n\"" + actual + "\"\nnot to match:\n\"" + expected + "\""));
}
proto.assertEqual = function assertRawDataTable(expected, actual) {
var expectedJSON = JSON.stringify(expected);
var actualJSON = JSON.stringify(actual);
if (actualJSON != expectedJSON)
throw(new Error("Expected:\n\"" + actualJSON + "\"\nto match:\n\"" + expectedJSON + "\""));
}
proto.assertExecutedNumberedScenarios = function assertExecutedNumberedScenarios() {
var self = this;
var scenarioIndexes = Array.prototype.slice.apply(arguments);
var stepNames = [];
scenarioIndexes.forEach(function(scenarioIndex) {
var stepName = self.makeNumberedStepName(scenarioIndex);
stepNames.push(stepName);
});
this.assertCompleteCycleSequence.apply(this, stepNames);
}
proto.assertCycleSequence = function assertCycleSequence() {
var events = Array.prototype.slice.apply(arguments);
var partialSequence = ' -> ' + events.join(' -> ');
if (this.cycleEvents.indexOf(partialSequence) < 0)
throw(new Error("Expected cycle sequence \"" + this.cycleEvents + "\" to contain \"" + partialSequence + "\""));
}
proto.assertCompleteCycleSequence = function assertCompleteCycleSequence() {
var events = Array.prototype.slice.apply(arguments);
var sequence = ' -> ' + events.join(' -> ');
if (this.cycleEvents != sequence)
throw(new Error("Expected cycle sequence \"" + this.cycleEvents + "\" to be \"" + sequence + "\""));
}
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");
levels = levels || 1;
lines.forEach(function(line) {
var indent = (line == "" ? "" : Array(levels + 1).join(" "));
indented += indent + line + "\n";
});
return indented;
};
exports.World = World;