From a23d0b133406bd59e9d6f868fab26246b7602d18 Mon Sep 17 00:00:00 2001 From: Chris Young Date: Mon, 21 May 2012 22:46:30 +0100 Subject: [PATCH 01/44] Skeletal JSON formatter --- features/a.feature | 3 + lib/cucumber/cli.js | 2 + lib/cucumber/listener.js | 1 + lib/cucumber/listener/json_formatter.js | 427 ++++ spec/cucumber/listener/json_formatter_spec.js | 1819 +++++++++++++++++ 5 files changed, 2252 insertions(+) create mode 100644 features/a.feature create mode 100644 lib/cucumber/listener/json_formatter.js create mode 100644 spec/cucumber/listener/json_formatter_spec.js diff --git a/features/a.feature b/features/a.feature new file mode 100644 index 000000000..d5f65c765 --- /dev/null +++ b/features/a.feature @@ -0,0 +1,3 @@ +Feature: Some Feature + Scenario: A Passing Step + When a step is passing diff --git a/lib/cucumber/cli.js b/lib/cucumber/cli.js index dd8351236..1a5e21764 100644 --- a/lib/cucumber/cli.js +++ b/lib/cucumber/cli.js @@ -15,7 +15,9 @@ var Cli = function(argv) { runSuiteWithConfiguration: function runSuiteWithConfiguration(configuration, callback) { var runtime = Cucumber.Runtime(configuration); var progressFormatter = Cucumber.Listener.ProgressFormatter(); + var jsonFormatter = Cucumber.Listener.JsonFormatter(); runtime.attachListener(progressFormatter); + runtime.attachListener(jsonFormatter); runtime.start(callback); }, diff --git a/lib/cucumber/listener.js b/lib/cucumber/listener.js index 396281cbd..87bf87e11 100644 --- a/lib/cucumber/listener.js +++ b/lib/cucumber/listener.js @@ -1,3 +1,4 @@ var Listener = {}; Listener.ProgressFormatter = require('./listener/progress_formatter'); +Listener.JsonFormatter = require('./listener/json_formatter'); module.exports = Listener; diff --git a/lib/cucumber/listener/json_formatter.js b/lib/cucumber/listener/json_formatter.js new file mode 100644 index 000000000..5d19c6013 --- /dev/null +++ b/lib/cucumber/listener/json_formatter.js @@ -0,0 +1,427 @@ +var JsonFormatter = function(options) { + var Cucumber = require('../../cucumber'); + + var logs = ""; + var failedScenarioLogBuffer = ""; + var undefinedStepLogBuffer = ""; + var passedScenarioCount = 0; + var undefinedScenarioCount = 0; + var pendingScenarioCount = 0; + var failedScenarioCount = 0; + var passedStepCount = 0; + var failedStepCount = 0; + var skippedStepCount = 0; + var undefinedStepCount = 0; + var pendingStepCount = 0; + var currentScenarioFailing = false; + var currentScenarioUndefined = false; + var currentScenarioPending = false; + var failedStepResults = Cucumber.Type.Collection(); + + var blob = {"name": "", + "id": ""} // Where we're going to stick the data we want to output as json + + if (!options) + options = {}; + if (options['logToConsole'] == undefined) + options['logToConsole'] = true; + + var self = { + log: function log(string) { + logs += string; + if (options['logToConsole']) + process.stdout.write(string); // This is where the results get blatted onto the console + if (typeof(options['logToFunction']) == 'function') + options['logToFunction'](string); + }, + + getLogs: function getLogs() { + return logs; + }, + + hear: function hear(event, callback) { + + if (self.hasHandlerForEvent(event)) { + var handler = self.getHandlerForEvent(event); + handler(event, callback); + } else { + console.log('I GOT NOTHING FOR YA') + callback(); + + } + }, + + hasHandlerForEvent: function hasHandlerForEvent(event) { + var handlerName = self.buildHandlerNameForEvent(event); + return self[handlerName] != undefined; + }, + + buildHandlerNameForEvent: function buildHandlerNameForEvent(event) { + var handlerName = + JsonFormatter.EVENT_HANDLER_NAME_PREFIX + + event.getName() + + JsonFormatter.EVENT_HANDLER_NAME_SUFFIX; + return handlerName; + }, + + getHandlerForEvent: function getHandlerForEvent(event) { + var eventHandlerName = self.buildHandlerNameForEvent(event); + return self[eventHandlerName]; + }, + + handleBeforeFeatureEvent: function handleBeforeFeatureEvent(event, callback) { + blob["name"] = event.getPayloadItem('feature').getName(); + var description = event.getPayloadItem('feature').getDescription(); + if (description != "") + blob["description"] = description + callback(); + }, + + + handleBeforeScenarioEvent: function handleBeforeScenarioEvent(event, callback) { + + self.prepareBeforeScenario(); // Can probably get rid of this + + var scenario = {"name": event.getPayloadItem('scenario').getName()}; + if (!("elements" in blob)) { + blob["elements"] = []; + } + + blob["elements"].push(scenario) + + callback(); + }, + + handleStepResultEvent: function handleStepResult(event, callback) { + var stepResult = event.getPayloadItem('stepResult'); + + if (!(blob["elements"][blob["elements"].length-1]["steps"])) { + blob["elements"][blob["elements"].length-1]["steps"] = []; + } + + if (stepResult.isSuccessful()) + self.handleSuccessfulStepResult(); + else if (stepResult.isPending()) + self.handlePendingStepResult(); + else if (stepResult.isSkipped()) + self.handleSkippedStepResult(); + else if (stepResult.isUndefined()) + self.handleUndefinedStepResult(stepResult); + else + self.handleFailedStepResult(stepResult); + callback(); + }, + + handleSuccessfulStepResult: function handleSuccessfulStepResult() { + self.witnessPassedStep(); + self.log(JsonFormatter.PASSED_STEP_CHARACTER); + }, + + handlePendingStepResult: function handlePendingStepResult() { + self.witnessPendingStep(); + self.markCurrentScenarioAsPending(); + self.log(JsonFormatter.PENDING_STEP_CHARACTER); + }, + + handleSkippedStepResult: function handleSkippedStepResult() { + self.witnessSkippedStep(); + self.log(JsonFormatter.SKIPPED_STEP_CHARACTER); + }, + + handleUndefinedStepResult: function handleUndefinedStepResult(stepResult) { + var step = stepResult.getStep(); + self.storeUndefinedStep(step); + self.witnessUndefinedStep(); + self.markCurrentScenarioAsUndefined(); + self.log(JsonFormatter.UNDEFINED_STEP_CHARACTER); + }, + + handleFailedStepResult: function handleFailedStepResult(stepResult) { + self.storeFailedStepResult(stepResult); + self.witnessFailedStep(); + self.markCurrentScenarioAsFailing(); + self.log(JsonFormatter.FAILED_STEP_CHARACTER); + }, + + handleAfterFeaturesEvent: function handleAfterFeaturesEvent(event, callback) { + self.logSummary(); + // Is this where we would dump the JSON? + console.log(blob) + callback(); + }, + + handleAfterScenarioEvent: function handleAfterScenarioEvent(event, callback) { + if (self.isCurrentScenarioFailing()) { + var scenario = event.getPayloadItem('scenario'); + self.storeFailedScenario(scenario); + self.witnessFailedScenario(); + } else if (self.isCurrentScenarioUndefined()) { + self.witnessUndefinedScenario(); + } else if (self.isCurrentScenarioPending()) { + self.witnessPendingScenario(); + } else { + self.witnessPassedScenario(); + } + callback(); + }, + + prepareBeforeScenario: function prepareBeforeScenario() { + + + + currentScenarioFailing = false; + currentScenarioPending = false; + currentScenarioUndefined = false; + }, + + markCurrentScenarioAsFailing: function markCurrentScenarioAsFailing() { + currentScenarioFailing = true; + }, + + markCurrentScenarioAsUndefined: function markCurrentScenarioAsUndefined() { + currentScenarioUndefined = true; + }, + + markCurrentScenarioAsPending: function markCurrentScenarioAsPending() { + currentScenarioPending = true; + }, + + isCurrentScenarioFailing: function isCurrentScenarioFailing() { + return currentScenarioFailing; + }, + + isCurrentScenarioUndefined: function isCurrentScenarioUndefined() { + return currentScenarioUndefined; + }, + + isCurrentScenarioPending: function isCurrentScenarioPending() { + return currentScenarioPending; + }, + + storeFailedStepResult: function storeFailedStepResult(failedStepResult) { + failedStepResults.add(failedStepResult); + }, + + storeFailedScenario: function storeFailedScenario(failedScenario) { + var name = failedScenario.getName(); + var line = failedScenario.getLine(); + self.appendStringToFailedScenarioLogBuffer(":" + line + " # Scenario: " + name); + }, + + storeUndefinedStep: function storeUndefinedStep(step) { + var snippetBuilder = Cucumber.SupportCode.StepDefinitionSnippetBuilder(step); + var snippet = snippetBuilder.buildSnippet(); + self.appendStringToUndefinedStepLogBuffer(snippet); + }, + + appendStringToFailedScenarioLogBuffer: function appendStringToFailedScenarioLogBuffer(string) { + failedScenarioLogBuffer += string + "\n"; + }, + + appendStringToUndefinedStepLogBuffer: function appendStringToUndefinedStepLogBuffer(string) { + if (undefinedStepLogBuffer.indexOf(string) == -1) + undefinedStepLogBuffer += string + "\n"; + }, + + getFailedScenarioLogBuffer: function getFailedScenarioLogBuffer() { + return failedScenarioLogBuffer; + }, + + getUndefinedStepLogBuffer: function getUndefinedStepLogBuffer() { + return undefinedStepLogBuffer; + }, + + logSummary: function logSummary() { + self.log("\n\n"); + if (self.witnessedAnyFailedStep()) + self.logFailedStepResults(); + self.logScenariosSummary(); + self.logStepsSummary(); + if (self.witnessedAnyUndefinedStep()) + self.logUndefinedStepSnippets(); + }, + + logFailedStepResults: function logFailedStepResults() { + self.log("(::) failed steps (::)\n\n"); + failedStepResults.syncForEach(function(stepResult) { + self.logFailedStepResult(stepResult); + }); + self.log("Failing scenarios:\n"); + var failedScenarios = self.getFailedScenarioLogBuffer(); + self.log(failedScenarios); + self.log("\n"); + }, + + logFailedStepResult: function logFailedStepResult(stepResult) { + var failureMessage = stepResult.getFailureException(); + self.log(failureMessage.stack || failureMessage); + self.log("\n\n"); + }, + + logScenariosSummary: function logScenariosSummary() { + var scenarioCount = self.getScenarioCount(); + var passedScenarioCount = self.getPassedScenarioCount(); + var undefinedScenarioCount = self.getUndefinedScenarioCount(); + var pendingScenarioCount = self.getPendingScenarioCount(); + var failedScenarioCount = self.getFailedScenarioCount(); + var details = []; + + self.log(scenarioCount + " scenario" + (scenarioCount != 1 ? "s" : "")); + if (scenarioCount > 0 ) { + if (failedScenarioCount > 0) + details.push(failedScenarioCount + " failed"); + if (undefinedScenarioCount > 0) + details.push(undefinedScenarioCount + " undefined"); + if (pendingScenarioCount > 0) + details.push(pendingScenarioCount + " pending"); + if (passedScenarioCount > 0) + details.push(passedScenarioCount + " passed"); + self.log(" (" + details.join(', ') + ")"); + } + self.log("\n"); + }, + + logStepsSummary: function logStepsSummary() { + var stepCount = self.getStepCount(); + var passedStepCount = self.getPassedStepCount(); + var undefinedStepCount = self.getUndefinedStepCount(); + var skippedStepCount = self.getSkippedStepCount(); + var pendingStepCount = self.getPendingStepCount(); + var failedStepCount = self.getFailedStepCount(); + var details = []; + + self.log(stepCount + " step" + (stepCount != 1 ? "s" : "")); + if (stepCount > 0) { + if (failedStepCount > 0) + details.push(failedStepCount + " failed"); + if (undefinedStepCount > 0) + details.push(undefinedStepCount + " undefined"); + if (pendingStepCount > 0) + details.push(pendingStepCount + " pending"); + if (skippedStepCount > 0) + details.push(skippedStepCount + " skipped"); + if (passedStepCount > 0) + details.push(passedStepCount + " passed"); + self.log(" (" + details.join(', ') + ")"); + } + self.log("\n"); + }, + + logUndefinedStepSnippets: function logUndefinedStepSnippets() { + var undefinedStepLogBuffer = self.getUndefinedStepLogBuffer(); + self.log("\nYou can implement step definitions for undefined steps with these snippets:\n\n"); + self.log(undefinedStepLogBuffer); + }, + + witnessPassedScenario: function witnessPassedScenario() { + passedScenarioCount++; + }, + + witnessUndefinedScenario: function witnessUndefinedScenario() { + undefinedScenarioCount++; + }, + + witnessPendingScenario: function witnessPendingScenario() { + pendingScenarioCount++; + }, + + witnessFailedScenario: function witnessFailedScenario() { + failedScenarioCount++; + }, + + witnessPassedStep: function witnessPassedStep() { + passedStepCount++; + }, + + witnessUndefinedStep: function witnessUndefinedStep() { + undefinedStepCount++; + }, + + witnessPendingStep: function witnessPendingStep() { + pendingStepCount++; + }, + + witnessFailedStep: function witnessFailedStep() { + failedStepCount++; + }, + + witnessSkippedStep: function witnessSkippedStep() { + skippedStepCount++; + }, + + getScenarioCount: function getScenarioCount() { + var scenarioCount = + self.getPassedScenarioCount() + + self.getUndefinedScenarioCount() + + self.getPendingScenarioCount() + + self.getFailedScenarioCount(); + return scenarioCount; + }, + + getPassedScenarioCount: function getPassedScenarioCount() { + return passedScenarioCount; + }, + + getUndefinedScenarioCount: function getUndefinedScenarioCount() { + return undefinedScenarioCount; + }, + + getPendingScenarioCount: function getPendingScenarioCount() { + return pendingScenarioCount; + }, + + getFailedScenarioCount: function getFailedScenarioCount() { + return failedScenarioCount; + }, + + getStepCount: function getStepCount() { + var stepCount = + self.getPassedStepCount() + + self.getUndefinedStepCount() + + self.getSkippedStepCount() + + self.getPendingStepCount() + + self.getFailedStepCount(); + return stepCount; + }, + + getPassedStepCount: function getPassedStepCount() { + return passedStepCount; + }, + + getPendingStepCount: function getPendingStepCount() { + return pendingStepCount; + }, + + getFailedStepCount: function getFailedStepCount() { + return failedStepCount; + }, + + getSkippedStepCount: function getSkippedStepCount() { + return skippedStepCount; + }, + + getUndefinedStepCount: function getUndefinedStepCount() { + return undefinedStepCount; + }, + + witnessedAnyFailedStep: function witnessedAnyFailedStep() { + return failedStepCount > 0; + }, + + witnessedAnyUndefinedStep: function witnessedAnyUndefinedStep() { + return undefinedStepCount > 0; + } + }; + return self; +}; +JsonFormatter.PASSED_STEP_CHARACTER = '.'; +JsonFormatter.SKIPPED_STEP_CHARACTER = '-'; +JsonFormatter.UNDEFINED_STEP_CHARACTER = 'U'; +JsonFormatter.PENDING_STEP_CHARACTER = 'P'; +JsonFormatter.FAILED_STEP_CHARACTER = 'F'; + +// TODO: Factor out to make common to all handlers +JsonFormatter.EVENT_HANDLER_NAME_PREFIX = 'handle'; +JsonFormatter.EVENT_HANDLER_NAME_SUFFIX = 'Event'; + +module.exports = JsonFormatter; diff --git a/spec/cucumber/listener/json_formatter_spec.js b/spec/cucumber/listener/json_formatter_spec.js new file mode 100644 index 000000000..1a6c225ae --- /dev/null +++ b/spec/cucumber/listener/json_formatter_spec.js @@ -0,0 +1,1819 @@ +require('../../support/spec_helper'); + +describe("Cucumber.Listener.ProgressFormatter", function() { + var Cucumber = requireLib('cucumber'); + var listener, failedStepResults; + + beforeEach(function() { + failedStepResults = createSpy("Failed steps"); + spyOn(Cucumber.Type, 'Collection').andReturn(failedStepResults); + listener = Cucumber.Listener.ProgressFormatter(); + }); + + describe("constructor", function() { + it("creates a collection to store the failed steps", function() { + expect(Cucumber.Type.Collection).toHaveBeenCalled(); + }); + }); + + describe("log()", function() { + var logged, alsoLogged, loggedBuffer; + + beforeEach(function() { + logged = "this was logged"; + alsoLogged = "this was also logged"; + loggedBuffer = logged + alsoLogged; + spyOn(process.stdout, 'write'); + }); + + it("records logged strings", function() { + listener.log(logged); + listener.log(alsoLogged); + expect(listener.getLogs()).toBe(loggedBuffer); + }); + + it("outputs the logged string to STDOUT by default", function() { + listener.log(logged); + expect(process.stdout.write).toHaveBeenCalledWith(logged); + }); + + describe("when asked to output to STDOUT", function() { + beforeEach(function() { + listener = Cucumber.Listener.ProgressFormatter({logToConsole: true}); + }); + + it("outputs the logged string to STDOUT", function() { + listener.log(logged); + expect(process.stdout.write).toHaveBeenCalledWith(logged); + }); + }); + + describe("when asked to not output to STDOUT", function() { + beforeEach(function() { + listener = Cucumber.Listener.ProgressFormatter({logToConsole: false}); + }); + + it("does not output anything to STDOUT", function() { + listener.log(logged); + expect(process.stdout.write).not.toHaveBeenCalledWith(logged); + }); + }); + + describe("when asked to output to a function", function() { + var userFunction; + + beforeEach(function() { + userFunction = createSpy("output user function"); + listener = Cucumber.Listener.ProgressFormatter({logToFunction: userFunction}); + }); + + it("calls the function with the logged string", function() { + listener.log(logged); + expect(userFunction).toHaveBeenCalledWith(logged); + }); + }); + }); + + describe("getLogs()", function() { + it("returns the logged buffer", function() { + var logged = "this was logged"; + var alsoLogged = "this was also logged"; + var loggedBuffer = logged + alsoLogged; + spyOn(process.stdout, 'write'); // prevent actual output during spec execution + listener.log(logged); + listener.log(alsoLogged); + expect(listener.getLogs()).toBe(loggedBuffer); + }); + + it("returns an empty string when the listener did not log anything yet", function() { + expect(listener.getLogs()).toBe(""); + }); + }); + + describe("hear()", function() { + var event, callback; + var eventHandler; + + beforeEach(function() { + event = createSpy("Event"); + callback = createSpy("Callback"); + spyOn(listener, 'hasHandlerForEvent'); + spyOn(listener, 'getHandlerForEvent'); + }); + + it("checks wether there is a handler for the event", function() { + listener.hear(event, callback); + expect(listener.hasHandlerForEvent).toHaveBeenCalledWith(event); + }); + + describe("when there is a handler for that event", function() { + beforeEach(function() { + eventHandler = createSpy("Event handler (function)"); + listener.hasHandlerForEvent.andReturn(true); + listener.getHandlerForEvent.andReturn(eventHandler); + }); + + it("gets the handler for that event", function() { + listener.hear(event, callback); + expect(listener.getHandlerForEvent).toHaveBeenCalledWith(event); + }); + + it("calls the handler with the event and the callback", function() { + listener.hear(event, callback); + expect(eventHandler).toHaveBeenCalledWith(event, callback); + }); + + it("does not callback", function() { + listener.hear(event, callback); + expect(callback).not.toHaveBeenCalled(); + }); + }); + + describe("when there are no handlers for that event", function() { + beforeEach(function() { + listener.hasHandlerForEvent.andReturn(false); + }); + + it("calls back", function() { + listener.hear(event, callback); + expect(callback).toHaveBeenCalled(); + }); + + it("does not get the handler for the event", function() { + listener.hear(event, callback); + expect(listener.getHandlerForEvent).not.toHaveBeenCalled(); + }); + }); + }); + + describe("hasHandlerForEvent", function() { + var event, eventHandlerName, eventHandler; + + beforeEach(function() { + event = createSpy("Event"); + eventHandlerName = createSpy("event handler name"); + spyOn(listener, 'buildHandlerNameForEvent').andReturn(eventHandlerName); + }); + + it("builds the name of the handler for that event", function() { + listener.hasHandlerForEvent(event); + expect(listener.buildHandlerNameForEvent).toHaveBeenCalledWith(event); + }); + + describe("when the handler exists", function() { + beforeEach(function() { + eventHandler = createSpy("event handler"); + listener[eventHandlerName] = eventHandler; + }); + + it("returns true", function() { + expect(listener.hasHandlerForEvent(event)).toBeTruthy(); + }); + }); + + describe("when the handler does not exist", function() { + it("returns false", function() { + expect(listener.hasHandlerForEvent(event)).toBeFalsy(); + }); + }); + }); + + describe("buildHandlerNameForEvent", function() { + var event, eventName; + + beforeEach(function() { + eventName = "SomeEventName"; + event = createSpyWithStubs("Event", {getName: eventName}); + }); + + it("gets the name of the event", function() { + listener.buildHandlerNameForEvent(event); + expect(event.getName).toHaveBeenCalled(); + }); + + it("returns the name of the event with prefix 'handle' and suffix 'Event'", function() { + expect(listener.buildHandlerNameForEvent(event)).toBe("handle" + eventName + "Event"); + }); + }); + + describe("getHandlerForEvent()", function() { + var event; + var eventHandlerName, eventHandler; + + beforeEach(function() { + event = createSpy("event"); + eventHandlerName = 'handleSomeEvent'; + eventHandler = createSpy("event handler"); + spyOn(listener, 'buildHandlerNameForEvent').andReturn(eventHandlerName); + }); + + it("gets the name of the handler for the event", function() { + listener.getHandlerForEvent(event); + expect(listener.buildHandlerNameForEvent).toHaveBeenCalledWith(event); + }); + + describe("when an event handler exists for the event", function() { + beforeEach(function() { + listener[eventHandlerName] = eventHandler; + }); + + it("returns the event handler", function() { + expect(listener.getHandlerForEvent(event)).toBe(eventHandler); + }); + }); + + describe("when no event handlers exist for the event", function() { + it("returns nothing", function() { + expect(listener.getHandlerForEvent(event)).toBeUndefined(); + }); + }); + }); + + describe("handleStepResultEvent()", function() { + var event, callback, stepResult; + + beforeEach(function() { + stepResult = createSpyWithStubs("step result", { + isSuccessful: undefined, + isPending: undefined, + isFailed: undefined, + isSkipped: undefined, + isUndefined: undefined + }); + event = createSpyWithStubs("event", {getPayloadItem: stepResult}); + callback = createSpy("Callback"); + spyOn(listener, 'handleFailedStepResult'); + }); + + it("gets the step result from the event payload", function() { + listener.handleStepResultEvent(event, callback); + expect(event.getPayloadItem).toHaveBeenCalledWith('stepResult'); + }); + + it("checks wether the step was successful or not", function() { + listener.handleStepResultEvent(event, callback); + expect(stepResult.isSuccessful).toHaveBeenCalled(); + }); + + describe("when the step passed", function() { + beforeEach(function() { + stepResult.isSuccessful.andReturn(true); + spyOn(listener, 'handleSuccessfulStepResult'); + }); + + it("handles the successful step result", function() { + listener.handleStepResultEvent(event, callback); + expect(listener.handleSuccessfulStepResult).toHaveBeenCalled(); + }); + }); + + describe("when the step did not pass", function() { + beforeEach(function() { + stepResult.isSuccessful.andReturn(false); + spyOn(listener, 'handleSuccessfulStepResult'); + }); + + it("does not handle a successful step result", function() { + listener.handleStepResultEvent(event, callback); + expect(listener.handleSuccessfulStepResult).not.toHaveBeenCalled(); + }); + + it("checks wether the step is pending", function() { + listener.handleStepResultEvent(event, callback); + expect(stepResult.isPending).toHaveBeenCalled(); + }); + + describe("when the step was pending", function() { + beforeEach(function() { + stepResult.isPending.andReturn(true); + spyOn(listener, 'handlePendingStepResult'); + }); + + it("handles the pending step result", function() { + listener.handleStepResultEvent(event, callback); + expect(listener.handlePendingStepResult).toHaveBeenCalled(); + }); + }); + + describe("when the step was not pending", function() { + beforeEach(function() { + stepResult.isPending.andReturn(false); + spyOn(listener, 'handlePendingStepResult'); + }); + + it("does not handle a pending step result", function() { + listener.handleStepResultEvent(event, callback); + expect(listener.handlePendingStepResult).not.toHaveBeenCalled(); + }); + + it("checks wether the step was skipped", function() { + listener.handleStepResultEvent(event, callback); + expect(stepResult.isSkipped).toHaveBeenCalled(); + }); + + describe("when the step was skipped", function() { + beforeEach(function() { + stepResult.isSkipped.andReturn(true); + spyOn(listener, 'handleSkippedStepResult'); + }); + + it("handles the skipped step result", function() { + listener.handleStepResultEvent(event, callback); + expect(listener.handleSkippedStepResult).toHaveBeenCalled(); + }); + }); + + describe("when the step was not skipped", function() { + beforeEach(function() { + stepResult.isSkipped.andReturn(false); + spyOn(listener, 'handleSkippedStepResult'); + }); + + it("does not handle a skipped step result", function() { + listener.handleStepResultEvent(event, callback); + expect(listener.handleSkippedStepResult).not.toHaveBeenCalled(); + }); + + it("checks wether the step was undefined", function() { + listener.handleStepResultEvent(event, callback); + expect(stepResult.isUndefined).toHaveBeenCalled(); + }); + + describe("when the step was undefined", function() { + beforeEach(function() { + stepResult.isUndefined.andReturn(true); + spyOn(listener, 'handleUndefinedStepResult'); + }); + + it("handles the undefined step result", function() { + listener.handleStepResultEvent(event, callback); + expect(listener.handleUndefinedStepResult).toHaveBeenCalledWith(stepResult); + }); + }); + + describe("when the step was not undefined", function() { + beforeEach(function() { + stepResult.isUndefined.andReturn(false); + spyOn(listener, 'handleUndefinedStepResult'); + }); + + it("does not handle a skipped step result", function() { + listener.handleStepResultEvent(event, callback); + expect(listener.handleSkippedStepResult).not.toHaveBeenCalled(); + }); + + it("handles a failed step result", function() { + listener.handleStepResultEvent(event, callback); + expect(listener.handleFailedStepResult).toHaveBeenCalledWith(stepResult); + }); + }); + }); + }); + }); + + it("calls back", function() { + listener.handleStepResultEvent(event, callback); + expect(callback).toHaveBeenCalled(); + }); + }); + + describe("handleSuccessfulStepResult()", function() { + beforeEach(function() { + spyOn(listener, 'witnessPassedStep'); + spyOn(listener, 'log'); + }); + + it("witnesses a passed step", function() { + listener.handleSuccessfulStepResult(); + expect(listener.witnessPassedStep).toHaveBeenCalled(); + }); + + it("logs the passing step character", function() { + listener.handleSuccessfulStepResult(); + expect(listener.log).toHaveBeenCalledWith(Cucumber.Listener.ProgressFormatter.PASSED_STEP_CHARACTER); + }); + }); + + describe("handlePendingStepResult()", function() { + beforeEach(function() { + spyOn(listener, 'witnessPendingStep'); + spyOn(listener, 'markCurrentScenarioAsPending'); + spyOn(listener, 'log') + }); + + it("witnesses a pending step", function() { + listener.handlePendingStepResult(); + expect(listener.witnessPendingStep).toHaveBeenCalled(); + }); + + it("marks the current scenario as pending", function() { + listener.handlePendingStepResult(); + expect(listener.markCurrentScenarioAsPending).toHaveBeenCalled(); + }); + + it("logs the pending step character", function() { + listener.handlePendingStepResult(); + expect(listener.log).toHaveBeenCalledWith(Cucumber.Listener.ProgressFormatter.PENDING_STEP_CHARACTER); + }); + }); + + describe("handleSkippedStepResult()", function() { + beforeEach(function() { + spyOn(listener, 'witnessSkippedStep'); + spyOn(listener, 'log'); + }); + + it("counts one more skipped step", function() { + listener.handleSkippedStepResult(); + expect(listener.witnessSkippedStep).toHaveBeenCalled(); + }); + + it("logs the skipped step character", function() { + listener.handleSkippedStepResult(); + expect(listener.log).toHaveBeenCalledWith(Cucumber.Listener.ProgressFormatter.SKIPPED_STEP_CHARACTER); + }); + }); + + describe("handleUndefinedStepResult()", function() { + var stepResult, step; + + beforeEach(function() { + step = createSpy("step"); + stepResult = createSpyWithStubs("step result", {getStep: step}); + spyOn(listener, 'storeUndefinedStep'); + spyOn(listener, 'witnessUndefinedStep'); + spyOn(listener, 'markCurrentScenarioAsUndefined'); + spyOn(listener, 'log'); + }); + + it("gets the step from the step result", function() { + listener.handleUndefinedStepResult(stepResult); + expect(stepResult.getStep).toHaveBeenCalled(); + }); + + it("stores the undefined step", function() { + listener.handleUndefinedStepResult(stepResult); + expect(listener.storeUndefinedStep).toHaveBeenCalledWith(step); + }); + + it("witnesses an undefined step", function() { + listener.handleUndefinedStepResult(stepResult); + expect(listener.witnessUndefinedStep).toHaveBeenCalled(); + }); + + it("marks the current scenario as undefined", function() { + listener.handleUndefinedStepResult(stepResult); + expect(listener.markCurrentScenarioAsUndefined).toHaveBeenCalled(); + }); + + it("logs the undefined step character", function() { + listener.handleUndefinedStepResult(stepResult); + expect(listener.log).toHaveBeenCalledWith(Cucumber.Listener.ProgressFormatter.UNDEFINED_STEP_CHARACTER); + }); + }); + + describe("handleFailedStepResult()", function() { + var stepResult; + + beforeEach(function() { + stepResult = createSpy("failed step result"); + spyOn(listener, 'storeFailedStepResult'); + spyOn(listener, 'witnessFailedStep'); + spyOn(listener, 'markCurrentScenarioAsFailing'); + spyOn(listener, 'log'); + }); + + it("stores the failed step result", function() { + listener.handleFailedStepResult(stepResult); + expect(listener.storeFailedStepResult).toHaveBeenCalledWith(stepResult); + }); + + it("witnesses a failed step", function() { + listener.handleFailedStepResult(stepResult); + expect(listener.witnessFailedStep).toHaveBeenCalled(); + }); + + it("marks the current scenario as failing", function() { + listener.handleFailedStepResult(stepResult); + expect(listener.markCurrentScenarioAsFailing).toHaveBeenCalled(); + }); + + it("logs the failed step character", function() { + listener.handleFailedStepResult(stepResult); + expect(listener.log).toHaveBeenCalledWith(Cucumber.Listener.ProgressFormatter.FAILED_STEP_CHARACTER); + }); + }); + + describe("handleBeforeScenarioEvent", function() { + var event, callback; + + beforeEach(function() { + event = createSpy("event"); + callback = createSpy("callback"); + spyOn(listener, 'prepareBeforeScenario'); + }); + + it("prepares for a new scenario", function() { + listener.handleBeforeScenarioEvent(event, callback); + expect(listener.prepareBeforeScenario).toHaveBeenCalled(); + }); + + it("calls back", function() { + listener.handleBeforeScenarioEvent(event, callback); + expect(callback).toHaveBeenCalled(); + }); + }); + + describe("handleAfterFeaturesEvent()", function() { + var features, callback; + + beforeEach(function() { + event = createSpy("Event"); + callback = createSpy("Callback"); + spyOn(listener, "logSummary"); + }); + + it("displays a summary", function() { + listener.handleAfterFeaturesEvent(event, callback); + expect(listener.logSummary).toHaveBeenCalled(); + }); + + it("calls back", function() { + listener.handleAfterFeaturesEvent(event, callback); + expect(callback).toHaveBeenCalled(); + }); + }); + + describe("handleAfterScenarioEvent()", function() { + var event, callback; + + beforeEach(function() { + event = createSpy("event"); + callback = createSpy("callback"); + spyOn(listener, 'isCurrentScenarioFailing'); + spyOn(listener, 'witnessPassedScenario'); + spyOn(listener, 'witnessUndefinedScenario'); + spyOn(listener, 'witnessPendingScenario'); + spyOn(listener, 'witnessFailedScenario'); + }); + + it("checks wether the current scenario failed", function() { + listener.handleAfterScenarioEvent(event, callback); + expect(listener.isCurrentScenarioFailing).toHaveBeenCalled(); + }); + + describe("when the current scenario failed", function() { + var scenario; + + beforeEach(function() { + scenario = createSpy("scenario"); + listener.isCurrentScenarioFailing.andReturn(true); + spyOn(listener, 'storeFailedScenario'); + spyOnStub(event, 'getPayloadItem').andReturn(scenario); + }); + + it("witnesses a failed scenario", function() { + listener.handleAfterScenarioEvent(event, callback); + expect(listener.witnessFailedScenario).toHaveBeenCalled(); + }); + + it("gets the scenario from the payload", function() { + listener.handleAfterScenarioEvent(event, callback); + expect(event.getPayloadItem).toHaveBeenCalledWith('scenario'); + }); + + it("stores the failed scenario", function() { + listener.handleAfterScenarioEvent(event, callback); + expect(listener.storeFailedScenario).toHaveBeenCalledWith(scenario); + }); + }); + + describe("when the current scenario did not fail", function() { + beforeEach(function() { + listener.isCurrentScenarioFailing.andReturn(false); + spyOn(listener, 'isCurrentScenarioUndefined'); + }); + + it("checks wether the current scenario is undefined", function() { + listener.handleAfterScenarioEvent(event, callback); + expect(listener.isCurrentScenarioUndefined).toHaveBeenCalled(); + }); + + describe("when the current scenario is undefined", function() { + beforeEach(function() { + listener.isCurrentScenarioUndefined.andReturn(true); + }); + + it("witnesses an undefined scenario", function() { + listener.handleAfterScenarioEvent(event, callback); + expect(listener.witnessUndefinedScenario).toHaveBeenCalled(); + }); + }); + + describe("when the current scenario is not undefined", function() { + beforeEach(function() { + listener.isCurrentScenarioUndefined.andReturn(false); + spyOn(listener, 'isCurrentScenarioPending'); + }); + + it("checks wether the current scenario is pending", function() { + listener.handleAfterScenarioEvent(event, callback); + expect(listener.isCurrentScenarioPending).toHaveBeenCalled(); + }); + + describe("when the current scenario is pending", function() { + beforeEach(function() { + listener.isCurrentScenarioPending.andReturn(true); + }); + + it("witnesses a pending scenario", function() { + listener.handleAfterScenarioEvent(event, callback); + expect(listener.witnessPendingScenario).toHaveBeenCalled(); + }); + }); + + describe("when the current scenario is not pending (passed)", function() { + beforeEach(function() { + listener.isCurrentScenarioPending.andReturn(false); + }); + + it("witnesses a passed scenario", function() { + listener.handleAfterScenarioEvent(event, callback); + expect(listener.witnessPassedScenario).toHaveBeenCalled(); + }); + }); + }); + }); + it("calls back", function() { + listener.handleAfterScenarioEvent(event, callback); + expect(callback).toHaveBeenCalled(); + }); + }); + + describe("isCurrentScenarioFailing()", function() { + it("returns false when the current scenario did not fail yet", function() { + expect(listener.isCurrentScenarioFailing()).toBeFalsy(); + }); + + it("returns true when a step in the current scenario failed", function() { + listener.markCurrentScenarioAsFailing(); + expect(listener.isCurrentScenarioFailing()).toBeTruthy(); + }); + }); + + describe("isCurrentScenarioPending()", function() { + it("returns false when the current scenario was not set pending yet", function() { + expect(listener.isCurrentScenarioPending()).toBeFalsy(); + }); + + it("returns true when the current scenario was set pending", function() { + listener.markCurrentScenarioAsPending(); + expect(listener.isCurrentScenarioPending()).toBeTruthy(); + }); + }); + + describe("isCurrentScenarioUndefined()", function() { + it("returns false when the current scenario was not set undefined yet", function() { + expect(listener.isCurrentScenarioUndefined()).toBeFalsy(); + }); + + it("returns true when the current scenario was set undefined", function() { + listener.markCurrentScenarioAsUndefined(); + expect(listener.isCurrentScenarioUndefined()).toBeTruthy(); + }); + }); + + describe("prepareBeforeScenario()", function() { + it("unmarks the current scenario as pending", function() { + listener.markCurrentScenarioAsPending(); + listener.prepareBeforeScenario(); + expect(listener.isCurrentScenarioPending()).toBeFalsy(); + }); + + it("unmarks the current scenario as failing", function() { + listener.markCurrentScenarioAsFailing(); + listener.prepareBeforeScenario(); + expect(listener.isCurrentScenarioFailing()).toBeFalsy(); + }); + + it("unmarks the current scenario as undefined", function() { + listener.markCurrentScenarioAsUndefined(); + listener.prepareBeforeScenario(); + expect(listener.isCurrentScenarioUndefined()).toBeFalsy(); + }); + }); + + describe("storeFailedStepResult()", function() { + var failedStepResult; + + beforeEach(function() { + failedStepResult = createSpy("failed step result"); + spyOnStub(failedStepResults, 'add'); + }); + + it("adds the result to the failed step result collection", function() { + listener.storeFailedStepResult(failedStepResult); + expect(failedStepResults.add).toHaveBeenCalledWith(failedStepResult); + }); + }); + + describe("storeFailedScenario()", function() { + var failedScenario, name, line; + + beforeEach(function() { + name = "some failed scenario"; + line = "123"; + string = ":" + line + " # Scenario: " + name; + failedScenario = createSpyWithStubs("failedScenario", {getName: name, getLine: line}); + spyOn(listener, 'appendStringToFailedScenarioLogBuffer'); + }); + + it("gets the name of the scenario", function() { + listener.storeFailedScenario(failedScenario); + expect(failedScenario.getName).toHaveBeenCalled(); + }); + + it("gets the line of the scenario", function() { + listener.storeFailedScenario(failedScenario); + expect(failedScenario.getLine).toHaveBeenCalled(); + }); + + it("appends the scenario details to the failed scenario log buffer", function() { + listener.storeFailedScenario(failedScenario); + expect(listener.appendStringToFailedScenarioLogBuffer).toHaveBeenCalledWith(string); + }); + }); + + describe("storeUndefinedStep()", function() { + var snippetBuilder, snippet, step; + + beforeEach(function() { + stpe = createSpy("step"); + snippet = createSpy("step definition snippet"); + snippetBuilder = createSpyWithStubs("snippet builder", {buildSnippet: snippet}); + spyOn(Cucumber.SupportCode, 'StepDefinitionSnippetBuilder').andReturn(snippetBuilder); + spyOn(listener, 'appendStringToUndefinedStepLogBuffer'); + }); + + it("creates a new step definition snippet builder", function() { + listener.storeUndefinedStep(step); + expect(Cucumber.SupportCode.StepDefinitionSnippetBuilder).toHaveBeenCalledWith(step); + }); + + it("builds the step definition", function() { + listener.storeUndefinedStep(step); + expect(snippetBuilder.buildSnippet).toHaveBeenCalled(); + }); + + it("appends the snippet to the undefined step log buffer", function() { + listener.storeUndefinedStep(step); + expect(listener.appendStringToUndefinedStepLogBuffer).toHaveBeenCalledWith(snippet); + }); + }); + + describe("getFailedScenarioLogBuffer() [appendStringToFailedScenarioLogBuffer()]", function() { + it("returns the logged failed scenario details", function() { + listener.appendStringToFailedScenarioLogBuffer("abc"); + expect(listener.getFailedScenarioLogBuffer()).toBe("abc\n"); + }); + + it("returns all logged failed scenario lines joined with a line break", function() { + listener.appendStringToFailedScenarioLogBuffer("abc"); + listener.appendStringToFailedScenarioLogBuffer("def"); + expect(listener.getFailedScenarioLogBuffer()).toBe("abc\ndef\n"); + }); + }); + + describe("getUndefinedStepLogBuffer() [appendStringToUndefinedStepLogBuffer()]", function() { + it("returns the logged undefined step details", function() { + listener.appendStringToUndefinedStepLogBuffer("abc"); + expect(listener.getUndefinedStepLogBuffer()).toBe("abc\n"); + }); + + it("returns all logged failed scenario lines joined with a line break", function() { + listener.appendStringToUndefinedStepLogBuffer("abc"); + listener.appendStringToUndefinedStepLogBuffer("def"); + expect(listener.getUndefinedStepLogBuffer()).toBe("abc\ndef\n"); + }); + }); + + describe("appendStringToUndefinedStepLogBuffer() [getUndefinedStepLogBuffer()]", function() { + it("does not log the same string twice", function() { + listener.appendStringToUndefinedStepLogBuffer("abcdef"); + listener.appendStringToUndefinedStepLogBuffer("abcdef"); + expect(listener.getUndefinedStepLogBuffer()).toBe("abcdef\n"); + }); + }); + + describe("logSummary()", function() { + var scenarioCount, passedScenarioCount, failedScenarioCount; + var stepCount, passedStepCount; + + beforeEach(function() { + spyOn(listener, 'log'); + spyOn(listener, 'witnessedAnyFailedStep'); + spyOn(listener, 'witnessedAnyUndefinedStep'); + spyOn(listener, 'logFailedStepResults'); + spyOn(listener, 'logScenariosSummary'); + spyOn(listener, 'logStepsSummary'); + spyOn(listener, 'logUndefinedStepSnippets'); + }); + + it("logs two line feeds", function() { + listener.logSummary(); + expect(listener.log).toHaveBeenCalledWith("\n\n"); + }); + + it("checks wether there are failed steps or not", function() { + listener.logSummary(); + expect(listener.witnessedAnyFailedStep).toHaveBeenCalled(); + }); + + describe("when there are failed steps", function() { + beforeEach(function() { + listener.witnessedAnyFailedStep.andReturn(true); + }); + + it("logs the failed steps", function() { + listener.logSummary(); + expect(listener.logFailedStepResults).toHaveBeenCalled(); + }); + }); + + describe("when there are no failed steps", function() { + beforeEach(function() { + listener.witnessedAnyFailedStep.andReturn(false); + }); + + it("does not log failed steps", function() { + listener.logSummary(); + expect(listener.logFailedStepResults).not.toHaveBeenCalled(); + }); + }); + + it("logs the scenarios summary", function() { + listener.logSummary(); + expect(listener.logScenariosSummary).toHaveBeenCalled(); + }); + + it("logs the steps summary", function() { + listener.logSummary(); + expect(listener.logStepsSummary).toHaveBeenCalled(); + }); + + it("checks wether there are undefined steps or not", function() { + listener.logSummary(); + expect(listener.witnessedAnyUndefinedStep).toHaveBeenCalled(); + }); + + describe("when there are undefined steps", function() { + beforeEach(function() { + listener.witnessedAnyUndefinedStep.andReturn(true); + }); + + it("logs the undefined step snippets", function() { + listener.logSummary(); + expect(listener.logUndefinedStepSnippets).toHaveBeenCalled(); + }); + }); + + describe("when there are no undefined steps", function() { + beforeEach(function() { + listener.witnessedAnyUndefinedStep.andReturn(false); + }); + + it("does not log the undefined step snippets", function() { + listener.logSummary(); + expect(listener.logUndefinedStepSnippets).not.toHaveBeenCalled(); + }); + }); + }); + + describe("logFailedStepResults()", function() { + var failedScenarioLogBuffer; + + beforeEach(function() { + failedScenarioLogBuffer = createSpy("failed scenario log buffer"); + spyOnStub(failedStepResults, 'syncForEach'); + spyOn(listener, 'log'); + spyOn(listener, 'getFailedScenarioLogBuffer').andReturn(failedScenarioLogBuffer); + }); + + it("logs a failed steps header", function() { + listener.logFailedStepResults(); + expect(listener.log).toHaveBeenCalledWith("(::) failed steps (::)\n\n"); + }); + + it("iterates synchronously over the failed step results", function() { + listener.logFailedStepResults(); + expect(failedStepResults.syncForEach).toHaveBeenCalled(); + expect(failedStepResults.syncForEach).toHaveBeenCalledWithAFunctionAsNthParameter(1); + }); + + describe("for each failed step result", function() { + var userFunction, failedStep, forEachCallback; + + beforeEach(function() { + listener.logFailedStepResults(); + userFunction = failedStepResults.syncForEach.mostRecentCall.args[0]; + failedStepResult = createSpy("failed step result"); + spyOn(listener, 'logFailedStepResult'); + }); + + it("tells the visitor to visit the feature and call back when finished", function() { + userFunction(failedStepResult); + expect(listener.logFailedStepResult).toHaveBeenCalledWith(failedStepResult); + }); + }); + + it("logs a failed scenarios header", function() { + listener.logFailedStepResults(); + expect(listener.log).toHaveBeenCalledWith("Failing scenarios:\n"); + }); + + it("gets the failed scenario details from its log buffer", function() { + listener.logFailedStepResults(); + expect(listener.getFailedScenarioLogBuffer).toHaveBeenCalled(); + }); + + it("logs the failed scenario details", function() { + listener.logFailedStepResults(); + expect(listener.log).toHaveBeenCalledWith(failedScenarioLogBuffer); + }); + + it("logs a line break", function() { + listener.logFailedStepResults(); + expect(listener.log).toHaveBeenCalledWith("\n"); + }); + }); + + describe("logFailedStepResult()", function() { + var stepResult, failureException; + + beforeEach(function() { + spyOn(listener, 'log'); + failureException = createSpy('caught exception'); + stepResult = createSpyWithStubs("failed step result", { getFailureException: failureException }); + }); + + it("gets the failure exception from the step result", function() { + listener.logFailedStepResult(stepResult); + expect(stepResult.getFailureException).toHaveBeenCalled(); + }); + + describe("when the failure exception has a stack", function() { + beforeEach(function() { + failureException.stack = createSpy('failure exception stack'); + }); + + it("logs the stack", function() { + listener.logFailedStepResult(stepResult); + expect(listener.log).toHaveBeenCalledWith(failureException.stack); + }); + }); + + describe("when the failure exception has no stack", function() { + it("logs the exception itself", function() { + listener.logFailedStepResult(stepResult); + expect(listener.log).toHaveBeenCalledWith(failureException); + }); + }); + + it("logs two line breaks", function() { + listener.logFailedStepResult(stepResult); + expect(listener.log).toHaveBeenCalledWith("\n\n"); + }); + }); + + describe("logScenariosSummary()", function() { + var scenarioCount, passedScenarioCount, pendingScenarioCount, failedScenarioCount; + + beforeEach(function() { + scenarioCount = 12; + passedScenarioCount = 9; + undefinedScenarioCount = 17; + pendingScenarioCount = 7; + failedScenarioCount = 15; + spyOn(listener, 'log'); + spyOn(listener, 'getScenarioCount').andReturn(scenarioCount); + spyOn(listener, 'getPassedScenarioCount').andReturn(passedScenarioCount); + spyOn(listener, 'getUndefinedScenarioCount').andReturn(undefinedScenarioCount); + spyOn(listener, 'getPendingScenarioCount').andReturn(pendingScenarioCount); + spyOn(listener, 'getFailedScenarioCount').andReturn(failedScenarioCount); + }); + + it("gets the number of scenarios", function() { + listener.logScenariosSummary(); + expect(listener.getScenarioCount).toHaveBeenCalled(); + }); + + describe("when there are no scenarios", function() { + beforeEach(function() { listener.getScenarioCount.andReturn(0); }); + + it("logs 0 scenarios", function() { + listener.logScenariosSummary(); + expect(listener.log).toHaveBeenCalledWithStringMatching(/0 scenarios/); + }); + + it("does not log any details", function() { + listener.logScenariosSummary(); + expect(listener.log).not.toHaveBeenCalledWithStringMatching(/\(.*\)/); + }); + }); + + describe("when there are scenarios", function() { + beforeEach(function() { listener.getScenarioCount.andReturn(12); }); + + describe("when there is one scenario", function() { + beforeEach(function() { listener.getScenarioCount.andReturn(1); }); + + it("logs one scenario", function() { + listener.logScenariosSummary(); + expect(listener.log).toHaveBeenCalledWithStringMatching(/1 scenario([^s]|$)/); + }); + }); + + describe("when there are 2 or more scenarios", function() { + beforeEach(function() { listener.getScenarioCount.andReturn(2); }); + + it("logs two or more scenarios", function() { + listener.logScenariosSummary(); + expect(listener.log).toHaveBeenCalledWithStringMatching(/2 scenarios/); + }); + }); + + it("gets the number of failed scenarios", function() { + listener.logScenariosSummary(); + expect(listener.getFailedScenarioCount).toHaveBeenCalled(); + }); + + describe("when there are no failed scenarios", function() { + beforeEach(function() { listener.getFailedScenarioCount.andReturn(0); }); + + it("does not log failed scenarios", function() { + listener.logScenariosSummary(); + expect(listener.log).not.toHaveBeenCalledWithStringMatching(/failed/); + }); + }); + + describe("when there is one failed scenario", function() { + beforeEach(function() { listener.getFailedScenarioCount.andReturn(1); }); + + it("logs a failed scenario", function() { + listener.logScenariosSummary(); + expect(listener.log).toHaveBeenCalledWithStringMatching(/1 failed/); + }); + }); + + describe("when there are two or more failed scenarios", function() { + beforeEach(function() { listener.getFailedScenarioCount.andReturn(2); }); + + it("logs the number of failed scenarios", function() { + listener.logScenariosSummary(); + expect(listener.log).toHaveBeenCalledWithStringMatching(/2 failed/); + }); + }); + + it("gets the number of undefined scenarios", function() { + listener.logScenariosSummary(); + expect(listener.getUndefinedScenarioCount).toHaveBeenCalled(); + }); + + describe("when there are no undefined scenarios", function() { + beforeEach(function() { listener.getUndefinedScenarioCount.andReturn(0); }); + + it("does not log passed scenarios", function() { + listener.logScenariosSummary(); + expect(listener.log).not.toHaveBeenCalledWithStringMatching(/undefined/); + }); + }); + + describe("when there is one undefined scenario", function() { + beforeEach(function() { listener.getUndefinedScenarioCount.andReturn(1); }); + + it("logs one undefined scenario", function() { + listener.logScenariosSummary(); + expect(listener.log).toHaveBeenCalledWithStringMatching(/1 undefined/); + }); + }); + + describe("when there are two or more undefined scenarios", function() { + beforeEach(function() { listener.getUndefinedScenarioCount.andReturn(2); }); + + it("logs the undefined scenarios", function() { + listener.logScenariosSummary(); + expect(listener.log).toHaveBeenCalledWithStringMatching(/2 undefined/); + }); + }); + + it("gets the number of pending scenarios", function() { + listener.logScenariosSummary(); + expect(listener.getPendingScenarioCount).toHaveBeenCalled(); + }); + + describe("when there are no pending scenarios", function() { + beforeEach(function() { listener.getPendingScenarioCount.andReturn(0); }); + + it("does not log passed scenarios", function() { + listener.logScenariosSummary(); + expect(listener.log).not.toHaveBeenCalledWithStringMatching(/pending/); + }); + }); + + describe("when there is one pending scenario", function() { + beforeEach(function() { listener.getPendingScenarioCount.andReturn(1); }); + + it("logs one pending scenario", function() { + listener.logScenariosSummary(); + expect(listener.log).toHaveBeenCalledWithStringMatching(/1 pending/); + }); + }); + + describe("when there are two or more pending scenarios", function() { + beforeEach(function() { listener.getPendingScenarioCount.andReturn(2); }); + + it("logs the pending scenarios", function() { + listener.logScenariosSummary(); + expect(listener.log).toHaveBeenCalledWithStringMatching(/2 pending/); + }); + }); + + it("gets the number of passed scenarios", function() { + listener.logScenariosSummary(); + expect(listener.getPassedScenarioCount).toHaveBeenCalled(); + }); + + describe("when there are no passed scenarios", function() { + beforeEach(function() { listener.getPassedScenarioCount.andReturn(0); }); + + it("does not log passed scenarios", function() { + listener.logScenariosSummary(); + expect(listener.log).not.toHaveBeenCalledWithStringMatching(/passed/); + }); + }); + + describe("when there is one passed scenario", function() { + beforeEach(function() { listener.getPassedScenarioCount.andReturn(1); }); + + it("logs 1 passed scenarios", function() { + listener.logScenariosSummary(); + expect(listener.log).toHaveBeenCalledWithStringMatching(/1 passed/); + }); + }); + + describe("when there are two or more passed scenarios", function() { + beforeEach(function() { listener.getPassedScenarioCount.andReturn(2); }); + + it("logs the number of passed scenarios", function() { + listener.logScenariosSummary(); + expect(listener.log).toHaveBeenCalledWithStringMatching(/2 passed/); + }); + }); + }); + }); + + describe("logStepsSummary()", function() { + var stepCount, passedStepCount, failedStepCount, skippedStepCount, pendingStepCount; + + beforeEach(function() { + stepCount = 34; + passedStepCount = 31; + failedStepCount = 7; + skippedStepCount = 5; + undefinedStepCount = 4; + pendingStepCount = 2; + spyOn(listener, 'log'); + spyOn(listener, 'getStepCount').andReturn(stepCount); + spyOn(listener, 'getPassedStepCount').andReturn(passedStepCount); + spyOn(listener, 'getFailedStepCount').andReturn(failedStepCount); + spyOn(listener, 'getSkippedStepCount').andReturn(skippedStepCount); + spyOn(listener, 'getUndefinedStepCount').andReturn(undefinedStepCount); + spyOn(listener, 'getPendingStepCount').andReturn(pendingStepCount); + }); + + it("gets the number of steps", function() { + listener.logStepsSummary(); + expect(listener.getStepCount).toHaveBeenCalled(); + }); + + describe("when there are no steps", function() { + beforeEach(function() { + listener.getStepCount.andReturn(0); + }); + + it("logs 0 steps", function() { + listener.logStepsSummary(); + expect(listener.log).toHaveBeenCalledWithStringMatching(/0 steps/); + }); + + it("does not log any details", function() { + listener.logStepsSummary(); + expect(listener.log).not.toHaveBeenCalledWithStringMatching(/\(.*\)/); + }); + }); + + describe("when there are steps", function() { + beforeEach(function() { listener.getStepCount.andReturn(13); }); + + describe("when there is one step", function() { + beforeEach(function() { + listener.getStepCount.andReturn(1); + }); + + it("logs 1 step", function() { + listener.logStepsSummary(); + expect(listener.log).toHaveBeenCalledWithStringMatching(/1 step/); + }); + }); + + describe("when there are two or more steps", function() { + beforeEach(function() { + listener.getStepCount.andReturn(2); + }); + + it("logs the number of steps", function() { + listener.logStepsSummary(); + expect(listener.log).toHaveBeenCalledWithStringMatching(/2 steps/); + }); + }); + + it("gets the number of failed steps", function() { + listener.logStepsSummary(); + expect(listener.getFailedStepCount).toHaveBeenCalled(); + }); + + describe("when there are no failed steps", function() { + beforeEach(function() { + listener.getFailedStepCount.andReturn(0); + }); + + it("does not log failed steps", function() { + listener.logStepsSummary(); + expect(listener.log).not.toHaveBeenCalledWithStringMatching(/failed/); + }); + }); + + describe("when there is one failed step", function() { + beforeEach(function() { + listener.getFailedStepCount.andReturn(1); + }); + + it("logs one failed step", function() { + listener.logStepsSummary(); + expect(listener.log).toHaveBeenCalledWithStringMatching(/1 failed/); + }); + }); + + describe("when there is two or more failed steps", function() { + beforeEach(function() { + listener.getFailedStepCount.andReturn(2); + }); + + it("logs the number of failed steps", function() { + listener.logStepsSummary(); + expect(listener.log).toHaveBeenCalledWithStringMatching(/2 failed/); + }); + }); + + it("gets the number of undefined steps", function() { + listener.logStepsSummary(); + expect(listener.getUndefinedStepCount).toHaveBeenCalled(); + }); + + describe("when there are no undefined steps", function() { + beforeEach(function() { + listener.getUndefinedStepCount.andReturn(0); + }); + + it("does not log undefined steps", function() { + listener.logStepsSummary(); + expect(listener.log).not.toHaveBeenCalledWithStringMatching(/undefined/); + }); + }); + + describe("when there is one undefined step", function() { + beforeEach(function() { + listener.getUndefinedStepCount.andReturn(1); + }); + + it("logs one undefined steps", function() { + listener.logStepsSummary(); + expect(listener.log).toHaveBeenCalledWithStringMatching(/1 undefined/); + }); + }); + + describe("when there are two or more undefined steps", function() { + beforeEach(function() { + listener.getUndefinedStepCount.andReturn(2); + }); + + it("logs the number of undefined steps", function() { + listener.logStepsSummary(); + expect(listener.log).toHaveBeenCalledWithStringMatching(/2 undefined/); + }); + }); + + it("gets the number of pending steps", function() { + listener.logStepsSummary(); + expect(listener.getPendingStepCount).toHaveBeenCalled(); + }); + + describe("when there are no pending steps", function() { + beforeEach(function() { + listener.getPendingStepCount.andReturn(0); + }); + + it("does not log pending steps", function() { + listener.logStepsSummary(); + expect(listener.log).not.toHaveBeenCalledWithStringMatching(/pending/); + }); + }); + + describe("when there is one pending step", function() { + beforeEach(function() { + listener.getPendingStepCount.andReturn(1); + }); + + it("logs one pending steps", function() { + listener.logStepsSummary(); + expect(listener.log).toHaveBeenCalledWithStringMatching(/1 pending/); + }); + }); + + describe("when there are two or more pending steps", function() { + beforeEach(function() { + listener.getPendingStepCount.andReturn(2); + }); + + it("logs the number of pending steps", function() { + listener.logStepsSummary(); + expect(listener.log).toHaveBeenCalledWithStringMatching(/2 pending/); + }); + }); + + it("gets the number of skipped steps", function() { + listener.logStepsSummary(); + expect(listener.getSkippedStepCount).toHaveBeenCalled(); + }); + + describe("when there are no skipped steps", function() { + beforeEach(function() { + listener.getSkippedStepCount.andReturn(0); + }); + + it("does not log skipped steps", function() { + listener.logStepsSummary(); + expect(listener.log).not.toHaveBeenCalledWithStringMatching(/skipped/); + }); + }); + + describe("when there is one skipped step", function() { + beforeEach(function() { + listener.getSkippedStepCount.andReturn(1); + }); + + it("logs one skipped steps", function() { + listener.logStepsSummary(); + expect(listener.log).toHaveBeenCalledWithStringMatching(/1 skipped/); + }); + }); + + describe("when there are two or more skipped steps", function() { + beforeEach(function() { + listener.getSkippedStepCount.andReturn(2); + }); + + it("logs the number of skipped steps", function() { + listener.logStepsSummary(); + expect(listener.log).toHaveBeenCalledWithStringMatching(/2 skipped/); + }); + }); + + it("gets the number of passed steps", function() { + listener.logStepsSummary(); + expect(listener.getPassedStepCount).toHaveBeenCalled(); + }); + + describe("when there are no passed steps", function() { + beforeEach(function() { + listener.getPassedStepCount.andReturn(0); + }); + + it("does not log passed steps", function() { + listener.logStepsSummary(); + expect(listener.log).not.toHaveBeenCalledWithStringMatching(/passed/); + }); + }); + + describe("when there is one passed step", function() { + beforeEach(function() { + listener.getPassedStepCount.andReturn(1); + }); + + it("logs one passed step", function() { + listener.logStepsSummary(); + expect(listener.log).toHaveBeenCalledWithStringMatching(/1 passed/); + }); + }); + + describe("when there is two or more passed steps", function() { + beforeEach(function() { + listener.getPassedStepCount.andReturn(2); + }); + + it("logs the number of passed steps", function() { + listener.logStepsSummary(); + expect(listener.log).toHaveBeenCalledWithStringMatching(/2 passed/); + }); + }); + }); + }); + + describe("logUndefinedStepSnippets()", function() { + var undefinedStepLogBuffer; + + beforeEach(function() { + undefinedStepLogBuffer = createSpy("undefined step log buffer"); + spyOn(listener, 'log'); + spyOn(listener, 'getUndefinedStepLogBuffer').andReturn(undefinedStepLogBuffer); + }); + + it("logs a little explanation about the snippets", function() { + listener.logUndefinedStepSnippets(); + expect(listener.log).toHaveBeenCalledWith("\nYou can implement step definitions for undefined steps with these snippets:\n\n"); + }); + + it("gets the undefined steps log buffer", function() { + listener.logUndefinedStepSnippets(); + expect(listener.getUndefinedStepLogBuffer).toHaveBeenCalled(); + }); + + it("logs the undefined steps", function() { + listener.logUndefinedStepSnippets(); + expect(listener.log).toHaveBeenCalledWith(undefinedStepLogBuffer); + }); + }); + + describe("getScenarioCount()", function() { + var passedScenarioCount, undefinedScenarioCount, pendingScenarioCount, failedScenarioCount; + + beforeEach(function() { + passedScenarioCount = Math.floor(Math.random()*11) + 1; + undefinedScenarioCount = Math.floor(Math.random()*11) + 1; + pendingScenarioCount = Math.floor(Math.random()*11) + 1; + failedScenarioCount = Math.floor(Math.random()*11) + 1; + spyOn(listener, 'getPassedScenarioCount').andReturn(passedScenarioCount); + spyOn(listener, 'getUndefinedScenarioCount').andReturn(undefinedScenarioCount); + spyOn(listener, 'getPendingScenarioCount').andReturn(pendingScenarioCount); + spyOn(listener, 'getFailedScenarioCount').andReturn(failedScenarioCount); + }); + + it("gets the number of passed scenarios", function() { + listener.getScenarioCount(); + expect(listener.getPassedScenarioCount).toHaveBeenCalled(); + }); + + it("gets the number of undefined scenarios", function() { + listener.getScenarioCount(); + expect(listener.getUndefinedScenarioCount).toHaveBeenCalled(); + }); + + it("gets the number of pending scenarios", function() { + listener.getScenarioCount(); + expect(listener.getPendingScenarioCount).toHaveBeenCalled(); + }); + + it("gets the number of failed scenarios", function() { + listener.getScenarioCount(); + expect(listener.getFailedScenarioCount).toHaveBeenCalled(); + }); + + it("returns the sum of passed, undefined, pending aand failed scenarios", function() { + expect(listener.getScenarioCount()).toBe(passedScenarioCount + undefinedScenarioCount + pendingScenarioCount + failedScenarioCount); + }); + }); + + describe("getStepCount()", function() { + var passedStepCount, undefinedStepCount, skippedStepCount, pendingStepCount, failedStepCount, stepCount; + + beforeEach(function() { + passedStepCount = Math.floor(Math.random()*11) + 1; + undefinedStepCount = Math.floor(Math.random()*11) + 1; + skippedStepCount = Math.floor(Math.random()*11) + 1; + pendingStepCount = Math.floor(Math.random()*11) + 1; + failedStepCount = Math.floor(Math.random()*11) + 1; + stepCount = + undefinedStepCount + + passedStepCount + + skippedStepCount + + pendingStepCount + + failedStepCount; + spyOn(listener, 'getPassedStepCount').andReturn(passedStepCount); + spyOn(listener, 'getUndefinedStepCount').andReturn(undefinedStepCount); + spyOn(listener, 'getSkippedStepCount').andReturn(skippedStepCount); + spyOn(listener, 'getPendingStepCount').andReturn(pendingStepCount); + spyOn(listener, 'getFailedStepCount').andReturn(failedStepCount); + }); + + it("gets the number of passed steps", function() { + listener.getStepCount(); + expect(listener.getPassedStepCount).toHaveBeenCalled(); + }); + + it("gets the number of undefined steps", function() { + listener.getStepCount(); + expect(listener.getUndefinedStepCount).toHaveBeenCalled(); + }); + + it("gets the number of skipped steps", function() { + listener.getStepCount(); + expect(listener.getSkippedStepCount).toHaveBeenCalled(); + }); + + it("gets the number of pending steps", function() { + listener.getStepCount(); + expect(listener.getPendingStepCount).toHaveBeenCalled(); + }); + + it("gets the number of failed steps", function() { + listener.getStepCount(); + expect(listener.getFailedStepCount).toHaveBeenCalled(); + }); + + it("returns the sum of passed steps and failed steps", function() { + expect(listener.getStepCount()).toBe(stepCount); + }); + }); + + describe("passed scenario counting", function() { + describe("witnessPassedScenario()", function() { + it("counts one more passed scenario", function() { + var beforeCountOne = listener.getPassedScenarioCount(); + listener.witnessPassedScenario(); + expect(listener.getPassedScenarioCount()).toBe(beforeCountOne + 1); + }); + }); + + describe("getPassedScenarioCount()", function() { + it("returns 0 when no scenario passed", function() { + expect(listener.getPassedScenarioCount()).toBe(0); + }); + + it("returns 1 when one scenario passed", function() { + listener.witnessPassedScenario(); + expect(listener.getPassedScenarioCount()).toBe(1); + }); + + it("returns 2 when two scenarios passed", function() { + listener.witnessPassedScenario(); + listener.witnessPassedScenario(); + expect(listener.getPassedScenarioCount()).toBe(2); + }); + + it("returns 3 when three scenarios passed", function() { + listener.witnessPassedScenario(); + listener.witnessPassedScenario(); + listener.witnessPassedScenario(); + expect(listener.getPassedScenarioCount()).toBe(3); + }); + }); + }); + + describe("undefined scenario counting", function() { + describe("getUndefinedScenarioCount()", function() { + it("returns 0 when no scenarios undefined", function() { + expect(listener.getUndefinedScenarioCount()).toBe(0); + }); + + it("returns 1 when one scenario passed", function() { + listener.witnessUndefinedScenario(); + expect(listener.getUndefinedScenarioCount()).toBe(1); + }); + + it("returns 2 when two scenarios passed", function() { + listener.witnessUndefinedScenario(); + listener.witnessUndefinedScenario(); + expect(listener.getUndefinedScenarioCount()).toBe(2); + }); + + it("returns 3 when two scenarios passed", function() { + listener.witnessUndefinedScenario(); + listener.witnessUndefinedScenario(); + listener.witnessUndefinedScenario(); + expect(listener.getUndefinedScenarioCount()).toBe(3); + }); + }); + }); + + describe("pending scenario counting", function() { + describe("getPendingScenarioCount()", function() { + it("returns 0 when no scenarios pending", function() { + expect(listener.getPendingScenarioCount()).toBe(0); + }); + + it("returns 1 when one scenario passed", function() { + listener.witnessPendingScenario(); + expect(listener.getPendingScenarioCount()).toBe(1); + }); + + it("returns 2 when two scenarios passed", function() { + listener.witnessPendingScenario(); + listener.witnessPendingScenario(); + expect(listener.getPendingScenarioCount()).toBe(2); + }); + + it("returns 3 when two scenarios passed", function() { + listener.witnessPendingScenario(); + listener.witnessPendingScenario(); + listener.witnessPendingScenario(); + expect(listener.getPendingScenarioCount()).toBe(3); + }); + }); + }); + + describe("failed scenario counting", function() { + describe("getFailedScenarioCount()", function() { + it("returns 0 when no scenarios failed", function() { + expect(listener.getFailedScenarioCount()).toBe(0); + }); + + it("returns 1 when one scenario passed", function() { + listener.witnessFailedScenario(); + expect(listener.getFailedScenarioCount()).toBe(1); + }); + + it("returns 2 when two scenarios passed", function() { + listener.witnessFailedScenario(); + listener.witnessFailedScenario(); + expect(listener.getFailedScenarioCount()).toBe(2); + }); + + it("returns 3 when two scenarios passed", function() { + listener.witnessFailedScenario(); + listener.witnessFailedScenario(); + listener.witnessFailedScenario(); + expect(listener.getFailedScenarioCount()).toBe(3); + }); + }); + }); + + describe("passed step counting", function() { + describe("witnessPassedStep()", function() { + it("counts one more passed step", function() { + var beforeCountOne = listener.getPassedStepCount(); + listener.witnessPassedStep(); + expect(listener.getPassedStepCount()).toBe(beforeCountOne + 1); + }); + }); + + describe("getPassedStepCount()", function() { + it("returns 0 when no step passed", function() { + expect(listener.getPassedStepCount()).toBe(0); + }); + + it("returns 1 when one step passed", function() { + listener.witnessPassedStep(); + expect(listener.getPassedStepCount()).toBe(1); + }); + + it("returns 2 when two steps passed", function() { + listener.witnessPassedStep(); + listener.witnessPassedStep(); + expect(listener.getPassedStepCount()).toBe(2); + }); + + it("returns 3 when three steps passed", function() { + listener.witnessPassedStep(); + listener.witnessPassedStep(); + listener.witnessPassedStep(); + expect(listener.getPassedStepCount()).toBe(3); + }); + }); + }); + + describe("failed step counting", function() { + describe("getFailedStepCount()", function() { + it("returns 0 when no steps failed", function() { + expect(listener.getFailedStepCount()).toBe(0); + }); + + it("returns 1 when one step passed", function() { + listener.witnessFailedStep(); + expect(listener.getFailedStepCount()).toBe(1); + }); + + it("returns 2 when two steps passed", function() { + listener.witnessFailedStep(); + listener.witnessFailedStep(); + expect(listener.getFailedStepCount()).toBe(2); + }); + + it("returns 3 when two steps passed", function() { + listener.witnessFailedStep(); + listener.witnessFailedStep(); + listener.witnessFailedStep(); + expect(listener.getFailedStepCount()).toBe(3); + }); + }); + }); + + describe("skipped step counting", function() { + describe("getSkippedStepCount()", function() { + it("returns 0 when no steps skipped", function() { + expect(listener.getSkippedStepCount()).toBe(0); + }); + + it("returns 1 when one step passed", function() { + listener.witnessSkippedStep(); + expect(listener.getSkippedStepCount()).toBe(1); + }); + + it("returns 2 when two steps passed", function() { + listener.witnessSkippedStep(); + listener.witnessSkippedStep(); + expect(listener.getSkippedStepCount()).toBe(2); + }); + + it("returns 3 when two steps passed", function() { + listener.witnessSkippedStep(); + listener.witnessSkippedStep(); + listener.witnessSkippedStep(); + expect(listener.getSkippedStepCount()).toBe(3); + }); + }); + }); + + describe("undefined step counting", function() { + describe("getUndefinedStepCount()", function() { + it("returns 0 when no steps undefined", function() { + expect(listener.getUndefinedStepCount()).toBe(0); + }); + + it("returns 1 when one step passed", function() { + listener.witnessUndefinedStep(); + expect(listener.getUndefinedStepCount()).toBe(1); + }); + + it("returns 2 when two steps passed", function() { + listener.witnessUndefinedStep(); + listener.witnessUndefinedStep(); + expect(listener.getUndefinedStepCount()).toBe(2); + }); + + it("returns 3 when two steps passed", function() { + listener.witnessUndefinedStep(); + listener.witnessUndefinedStep(); + listener.witnessUndefinedStep(); + expect(listener.getUndefinedStepCount()).toBe(3); + }); + }); + }); + + describe("pending step counting", function() { + describe("getPendingStepCount()", function() { + it("returns 0 when no steps pending", function() { + expect(listener.getPendingStepCount()).toBe(0); + }); + + it("returns 1 when one step passed", function() { + listener.witnessPendingStep(); + expect(listener.getPendingStepCount()).toBe(1); + }); + + it("returns 2 when two steps passed", function() { + listener.witnessPendingStep(); + listener.witnessPendingStep(); + expect(listener.getPendingStepCount()).toBe(2); + }); + + it("returns 3 when two steps passed", function() { + listener.witnessPendingStep(); + listener.witnessPendingStep(); + listener.witnessPendingStep(); + expect(listener.getPendingStepCount()).toBe(3); + }); + }); + }); + + describe("witnessedAnyFailedStep()", function() { + it("returns false when no failed step were encountered", function() { + expect(listener.witnessedAnyFailedStep()).toBeFalsy(); + }); + + it("returns true when one or more steps were witnessed", function() { + listener.witnessFailedStep(); + expect(listener.witnessedAnyFailedStep()).toBeTruthy(); + }); + }); + + describe("witnessedAnyUndefinedStep()", function() { + it("returns false when no undefined step were encountered", function() { + expect(listener.witnessedAnyUndefinedStep()).toBeFalsy(); + }); + + it("returns true when one or more steps were witnessed", function() { + listener.witnessUndefinedStep(); + expect(listener.witnessedAnyUndefinedStep()).toBeTruthy(); + }); + }); +}); From 3f23299b07d328fe44561212e6bc74a61071a0e7 Mon Sep 17 00:00:00 2001 From: Chris Young Date: Thu, 24 May 2012 17:57:06 +0100 Subject: [PATCH 02/44] Getting my head around Jasmine Spys... --- features/cli.feature | 2 +- features/json_formatter.feature | 14 ++++++++++++++ features/simple.feature | 5 +++++ features/step_definitions/cli_steps.js | 10 ++++++++-- lib/cucumber/cli.js | 11 ++++++----- lib/cucumber/cli/argument_parser.js | 8 ++++++++ lib/cucumber/cli/configuration.js | 18 +++++++++++++++++- lib/cucumber/listener/json_formatter.js | 4 +--- spec/cucumber/cli/argument_parser_spec.js | 10 ++++++++++ spec/cucumber/cli/configuration_spec.js | 16 ++++++++++++++++ spec/cucumber/cli_spec.js | 9 ++------- 11 files changed, 88 insertions(+), 19 deletions(-) create mode 100644 features/json_formatter.feature create mode 100644 features/simple.feature diff --git a/features/cli.feature b/features/cli.feature index 5b3424f71..277b24790 100644 --- a/features/cli.feature +++ b/features/cli.feature @@ -102,4 +102,4 @@ Feature: Command line interface Scenario: display help (short flag) When I run `cucumber.js -h` - Then I see the help of Cucumber + Then I see the help of Cucumber \ No newline at end of file diff --git a/features/json_formatter.feature b/features/json_formatter.feature new file mode 100644 index 000000000..0ec3ad882 --- /dev/null +++ b/features/json_formatter.feature @@ -0,0 +1,14 @@ +Feature: JSON Formatter + In order to simplify processing of Cucumber features and results + Developers should be able to consume features as JSON + + Scenario: output JSON for a feature with no scenarios + Given a file named "features/a.feature" with: + """ + Feature: some feature + """ + When I run `cucumber.js -f json` + Then it should pass with: + """ + { name: 'some feature', id: '' } + """ \ No newline at end of file diff --git a/features/simple.feature b/features/simple.feature new file mode 100644 index 000000000..cee1a50e8 --- /dev/null +++ b/features/simple.feature @@ -0,0 +1,5 @@ +Feature: Simple Feature + + Scenario: display Cucumber version + When I run `cucumber.js --version` + Then I see the version of Cucumber diff --git a/features/step_definitions/cli_steps.js b/features/step_definitions/cli_steps.js index c731abdf3..ea66f2181 100644 --- a/features/step_definitions/cli_steps.js +++ b/features/step_definitions/cli_steps.js @@ -8,7 +8,7 @@ var cliSteps = function cliSteps() { var tmpDir = baseDir + "/tmp/cucumber-js-sandbox"; var cleansingNeeded = true; - var lastRun; + var lastRun = { error: null, stdout: "", stderr: "" }; function tmpPath(path) { return (tmpDir + "/" + path); @@ -64,8 +64,14 @@ var cliSteps = function cliSteps() { this.Then(/^it should pass with:$/, function(expectedOutput, callback) { var actualOutput = lastRun['stdout']; + + var actualError = lastRun['error']; + var actualStderr = lastRun['stderr']; + if (actualOutput.indexOf(expectedOutput) == -1) - throw new Error("Expected output to match the following:\n'" + expectedOutput + "'\nGot:\n'" + actualOutput + "'."); + throw new Error("Expected output to match the following:\n'" + expectedOutput + "'\nGot:\n'" + actualOutput + "'.\n" + + "Error:\n'" + actualError + "'.\n" + + "stderr:\n'" + actualStderr +"'."); callback(); }); diff --git a/lib/cucumber/cli.js b/lib/cucumber/cli.js index 1a5e21764..3c32236e1 100644 --- a/lib/cucumber/cli.js +++ b/lib/cucumber/cli.js @@ -13,11 +13,9 @@ var Cli = function(argv) { }, runSuiteWithConfiguration: function runSuiteWithConfiguration(configuration, callback) { - var runtime = Cucumber.Runtime(configuration); - var progressFormatter = Cucumber.Listener.ProgressFormatter(); - var jsonFormatter = Cucumber.Listener.JsonFormatter(); - runtime.attachListener(progressFormatter); - runtime.attachListener(jsonFormatter); + var runtime = Cucumber.Runtime(configuration); + var formatter = configuration.getFormatter(); + runtime.attachListener(formatter); runtime.start(callback); }, @@ -34,6 +32,9 @@ var Cli = function(argv) { Files under directories named \"support\" are always\n\ loaded first.\n\ \n\ +-f, --format FORMAT How to format features (Default: json). Available formats:\n\ + json : Prints the feature as JSON\n\ +\n\ -t, --tags TAG_EXPRESSION Only execute the features or scenarios with tags\n\ matching TAG_EXPRESSION. Scenarios inherit tags\n\ declared on the Feature level. The simplest\n\ diff --git a/lib/cucumber/cli/argument_parser.js b/lib/cucumber/cli/argument_parser.js index f04557453..6b65e1ca7 100644 --- a/lib/cucumber/cli/argument_parser.js +++ b/lib/cucumber/cli/argument_parser.js @@ -63,6 +63,7 @@ var ArgumentParser = function(argv) { definitions[ArgumentParser.TAGS_OPTION_NAME] = [String, Array]; definitions[ArgumentParser.HELP_FLAG_NAME] = Boolean; definitions[ArgumentParser.VERSION_FLAG_NAME] = Boolean; + definitions[ArgumentParser.FORMAT_OPTION_NAME] = String; return definitions; }, @@ -91,6 +92,11 @@ var ArgumentParser = function(argv) { return options; }, + getFormat: function getFormat() { + var format = self.getOptionOrDefault(ArgumentParser.FORMAT_OPTION_NAME, "progress"); // TODO: Remove magic word + return format; + }, + getOptionOrDefault: function getOptionOrDefault(optionName, defaultValue) { var options = self.getOptions(); var optionValue = options[optionName]; @@ -105,6 +111,8 @@ ArgumentParser.FEATURE_FILENAME_REGEXP = /[\/\\][^\/\\]+\.feature$/i; ArgumentParser.LONG_OPTION_PREFIX = "--"; ArgumentParser.REQUIRE_OPTION_NAME = "require"; ArgumentParser.REQUIRE_OPTION_SHORT_NAME = "r"; +ArgumentParser.FORMAT_OPTION_NAME = "format"; +ArgumentParser.FORMAT_OPTION_SHORT_NAME = "f"; ArgumentParser.TAGS_OPTION_NAME = "tags"; ArgumentParser.TAGS_OPTION_SHORT_NAME = "t"; ArgumentParser.HELP_FLAG_NAME = "help"; diff --git a/lib/cucumber/cli/configuration.js b/lib/cucumber/cli/configuration.js index 177ded2d6..bed66a7dd 100644 --- a/lib/cucumber/cli/configuration.js +++ b/lib/cucumber/cli/configuration.js @@ -43,7 +43,23 @@ var Configuration = function(argv) { isVersionRequested: function isVersionRequested() { var isVersionRequested = argumentParser.isVersionRequested(); return isVersionRequested; - } + }, + + getFormatter: function getFormatter() { + var formatter, + formatterOption = argumentParser.getFormat(); + + switch(formatterOption) { + case "json": + formatter = Cucumber.Listener.JsonFormatter(); + break; + case "progress": + default: + formatter = Cucumber.Listener.ProgressFormatter(); + } + return formatter; + }, + }; return self; }; diff --git a/lib/cucumber/listener/json_formatter.js b/lib/cucumber/listener/json_formatter.js index 5d19c6013..c1ba6b833 100644 --- a/lib/cucumber/listener/json_formatter.js +++ b/lib/cucumber/listener/json_formatter.js @@ -45,9 +45,7 @@ var JsonFormatter = function(options) { var handler = self.getHandlerForEvent(event); handler(event, callback); } else { - console.log('I GOT NOTHING FOR YA') - callback(); - + callback(); } }, diff --git a/spec/cucumber/cli/argument_parser_spec.js b/spec/cucumber/cli/argument_parser_spec.js index 0cfd7ed81..5fdf0561c 100644 --- a/spec/cucumber/cli/argument_parser_spec.js +++ b/spec/cucumber/cli/argument_parser_spec.js @@ -73,6 +73,12 @@ describe("Cucumber.Cli.ArgumentParser", function() { var knownOptionDefinitions = argumentParser.getKnownOptionDefinitions(); expect(knownOptionDefinitions[Cucumber.Cli.ArgumentParser.VERSION_FLAG_NAME]).toEqual(Boolean); }); + + it("defines an --output option to specify output format", function() { + var knownOptionDefinitions = argumentParser.getKnownOptionDefinitions(); + expect(knownOptionDefinitions[Cucumber.Cli.ArgumentParser.FORMAT_OPTION_NAME]).toEqual(String); + }); + }); describe("getShortenedOptionDefinitions()", function() { @@ -96,6 +102,10 @@ describe("Cucumber.Cli.ArgumentParser", function() { var shortenedOptionDefinitions = argumentParser.getShortenedOptionDefinitions(); expect(shortenedOptionDefinitions[aliasName]).toEqual(aliasValue); }); + + it("defines an alias to --output as -o", function() { + }); + }); describe("getFeatureFilePaths()", function() { diff --git a/spec/cucumber/cli/configuration_spec.js b/spec/cucumber/cli/configuration_spec.js index 21d0e2a1a..cd0e8042a 100644 --- a/spec/cucumber/cli/configuration_spec.js +++ b/spec/cucumber/cli/configuration_spec.js @@ -177,4 +177,20 @@ describe("Cucumber.Cli.Configuration", function() { expect(configuration.isVersionRequested()).toBe(isVersionRequested); }); }); + + describe("getFormatter()", function() { + beforeEach(function() { + spyOnStub(argumentParser, 'getFormat'); + }); + + it("asks the argument parser which format we should be outputting in", function() { + configuration.getFormatter(); + expect(argumentParser.getFormat).toHaveBeenCalled(); + }); +// +// it("returns the corresponding formatter for us to use", function() { +// // TODO: Fill this out when my head is in a better place +// }); + }); + }); diff --git a/spec/cucumber/cli_spec.js b/spec/cucumber/cli_spec.js index 5dd87e769..cd50ea7b2 100644 --- a/spec/cucumber/cli_spec.js +++ b/spec/cucumber/cli_spec.js @@ -112,8 +112,8 @@ describe("Cucumber.Cli", function() { var progressFormatter; beforeEach(function() { - configuration = createSpy("CLI configuration"); - runtime = createSpyWithStubs("runtime", {start: null, attachListener: null}); + configuration = createSpyWithStubs("CLI configuration", {getFormatter: progressFormatter}); + runtime = createSpyWithStubs("runtime", {start: null, attachListener: progressFormatter}); // TODO: What should this return? callback = createSpy("callback"); spyOn(Cucumber, 'Runtime').andReturn(runtime); spyOn(Cucumber.Listener, 'ProgressFormatter').andReturn(progressFormatter); @@ -124,11 +124,6 @@ describe("Cucumber.Cli", function() { expect(Cucumber.Runtime).toHaveBeenCalledWith(configuration); }); - it("creates a new progress formatter", function() { - cli.runSuiteWithConfiguration(configuration, callback); - expect(Cucumber.Listener.ProgressFormatter).toHaveBeenCalled(); - }); - it("attaches the progress formatter to the runtime", function() { cli.runSuiteWithConfiguration(configuration, callback); expect(runtime.attachListener).toHaveBeenCalledWith(progressFormatter); From b4f1b0c659862fe71a45cdfcd325919af530d1f3 Mon Sep 17 00:00:00 2001 From: Chris Young Date: Thu, 24 May 2012 18:29:12 +0100 Subject: [PATCH 03/44] Tests now ignore whitespace / indentation and just check JSON is valid --- features/err | 0 features/json_formatter.feature | 31 ++++++++++++++++++++++--- features/step_definitions/cli_steps.js | 19 +++++++++++++++ lib/cucumber/listener/json_formatter.js | 4 ++-- 4 files changed, 49 insertions(+), 5 deletions(-) create mode 100644 features/err diff --git a/features/err b/features/err new file mode 100644 index 000000000..e69de29bb diff --git a/features/json_formatter.feature b/features/json_formatter.feature index 0ec3ad882..8d68ad6bb 100644 --- a/features/json_formatter.feature +++ b/features/json_formatter.feature @@ -8,7 +8,32 @@ Feature: JSON Formatter Feature: some feature """ When I run `cucumber.js -f json` - Then it should pass with: + Then it should pass with this json: """ - { name: 'some feature', id: '' } - """ \ No newline at end of file +{ + "name": "some feature", + "id": "" +} + """ + + Scenario: output JSON for a feature with one undefined scenario + Given a file named "features/a.feature" with: + """ + Feature: some feature + + Scenario: I havn't done anything yet + """ + When I run `cucumber.js -f json` + Then it should pass with this json: + """ +{ + "name": "some feature", + "id": "", + "elements": [ + { + "name": "I havn't done anything yet" + } + ] +} + """ + diff --git a/features/step_definitions/cli_steps.js b/features/step_definitions/cli_steps.js index ea66f2181..9a8d7690f 100644 --- a/features/step_definitions/cli_steps.js +++ b/features/step_definitions/cli_steps.js @@ -75,6 +75,25 @@ var cliSteps = function cliSteps() { callback(); }); + this.Then(/^it should pass with this json:$/, function(expectedOutput, callback) { + var actualOutput = lastRun['stdout']; + + var actualError = lastRun['error']; + var actualStderr = lastRun['stderr']; + + var actualJson = JSON.parse(actualOutput); + var expectedJson = JSON.parse(expectedOutput); + + var actualJsonString = JSON.stringify(actualJson, null, 2); + var expectedJsonString = JSON.stringify(expectedJson, null, 2); + + if (actualJsonString.indexOf(expectedJsonString) == -1) + throw new Error("Expected output to match the following:\n'" + expectedJsonString + "'\nGot:\n'" + actualJsonString + "'.\n" + + "Error:\n'" + actualError + "'.\n" + + "stderr:\n'" + actualStderr +"'."); + callback(); + }); + this.Then(/^I see the version of Cucumber$/, function(callback) { var Cucumber = require('../../lib/cucumber'); var actualOutput = lastRun['stdout']; diff --git a/lib/cucumber/listener/json_formatter.js b/lib/cucumber/listener/json_formatter.js index c1ba6b833..85adfa858 100644 --- a/lib/cucumber/listener/json_formatter.js +++ b/lib/cucumber/listener/json_formatter.js @@ -30,7 +30,7 @@ var JsonFormatter = function(options) { log: function log(string) { logs += string; if (options['logToConsole']) - process.stdout.write(string); // This is where the results get blatted onto the console + // process.stdout.write(string); // This is where the results get blatted onto the console if (typeof(options['logToFunction']) == 'function') options['logToFunction'](string); }, @@ -144,7 +144,7 @@ var JsonFormatter = function(options) { handleAfterFeaturesEvent: function handleAfterFeaturesEvent(event, callback) { self.logSummary(); // Is this where we would dump the JSON? - console.log(blob) + process.stdout.write(JSON.stringify(blob, null, 2)); callback(); }, From 953f8245d7b94b4770803cc291779acf67d8b4ac Mon Sep 17 00:00:00 2001 From: Chris Young Date: Thu, 24 May 2012 18:32:29 +0100 Subject: [PATCH 04/44] Updated README to show status of my hacks --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ee61441c0..5cb98be55 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,7 @@ Cucumber.js is still a work in progress. Here is its current status. | [Failing steps](/~https://github.com/cucumber/cucumber-tck/blob/master/failing_steps.feature) | Done | | [Hooks](/~https://github.com/cucumber/cucumber-tck/blob/master/hooks.feature) | Done | | [I18n](/~https://github.com/cucumber/cucumber-tck/blob/master/i18n.feature) | To do | -| [JSON formatter](/~https://github.com/cucumber/cucumber-tck/blob/master/json_formatter.feature) | To do | +| [JSON formatter](/~https://github.com/cucumber/cucumber-tck/blob/master/json_formatter.feature) | Hacking away... | | [Pretty formatter](/~https://github.com/cucumber/cucumber-tck/blob/master/pretty_formatter.feature) | To do2 | | [Scenario outlines and examples](/~https://github.com/cucumber/cucumber-tck/blob/master/scenario_outlines_and_examples.feature) | To do | | [Stats collector](/~https://github.com/cucumber/cucumber-tck/blob/master/stats_collector.feature) | To do | From 8eb10743355f30437c2608e8853178e399087cbd Mon Sep 17 00:00:00 2001 From: Chris Young Date: Fri, 25 May 2012 08:31:49 +0100 Subject: [PATCH 05/44] Removed redundant ProgressFormatter methods from the JsonFormatter and fixed up the Jasmine tests for the latter --- lib/cucumber/listener/json_formatter.js | 139 +--- spec/cucumber/listener/json_formatter_spec.js | 765 +----------------- spec/tmp.txt | 728 +++++++++++++++++ 3 files changed, 755 insertions(+), 877 deletions(-) create mode 100644 spec/tmp.txt diff --git a/lib/cucumber/listener/json_formatter.js b/lib/cucumber/listener/json_formatter.js index 85adfa858..03b8b3a03 100644 --- a/lib/cucumber/listener/json_formatter.js +++ b/lib/cucumber/listener/json_formatter.js @@ -1,7 +1,6 @@ var JsonFormatter = function(options) { var Cucumber = require('../../cucumber'); - var logs = ""; var failedScenarioLogBuffer = ""; var undefinedStepLogBuffer = ""; var passedScenarioCount = 0; @@ -18,26 +17,10 @@ var JsonFormatter = function(options) { var currentScenarioPending = false; var failedStepResults = Cucumber.Type.Collection(); - var blob = {"name": "", - "id": ""} // Where we're going to stick the data we want to output as json - - if (!options) - options = {}; - if (options['logToConsole'] == undefined) - options['logToConsole'] = true; + var output = {"name": "", + "id": ""} var self = { - log: function log(string) { - logs += string; - if (options['logToConsole']) - // process.stdout.write(string); // This is where the results get blatted onto the console - if (typeof(options['logToFunction']) == 'function') - options['logToFunction'](string); - }, - - getLogs: function getLogs() { - return logs; - }, hear: function hear(event, callback) { @@ -68,10 +51,10 @@ var JsonFormatter = function(options) { }, handleBeforeFeatureEvent: function handleBeforeFeatureEvent(event, callback) { - blob["name"] = event.getPayloadItem('feature').getName(); + output["name"] = event.getPayloadItem('feature').getName(); var description = event.getPayloadItem('feature').getDescription(); if (description != "") - blob["description"] = description + output["description"] = description callback(); }, @@ -80,12 +63,15 @@ var JsonFormatter = function(options) { self.prepareBeforeScenario(); // Can probably get rid of this - var scenario = {"name": event.getPayloadItem('scenario').getName()}; - if (!("elements" in blob)) { - blob["elements"] = []; + var scenario = event.getPayloadItem('scenario'); + + var scenarioOutput = {"name": scenario.getName()}; + + if (!("elements" in output)) { + output["elements"] = []; } - blob["elements"].push(scenario) + output["elements"].push(scenarioOutput); callback(); }, @@ -93,8 +79,12 @@ var JsonFormatter = function(options) { handleStepResultEvent: function handleStepResult(event, callback) { var stepResult = event.getPayloadItem('stepResult'); - if (!(blob["elements"][blob["elements"].length-1]["steps"])) { - blob["elements"][blob["elements"].length-1]["steps"] = []; + if (!("elements" in output)) { + output["elements"] = []; //TODO: Make Dry + } + + if (!([output["elements"].length-1]["steps"])) { + [output["elements"].length-1]["steps"] = []; } if (stepResult.isSuccessful()) @@ -112,18 +102,15 @@ var JsonFormatter = function(options) { handleSuccessfulStepResult: function handleSuccessfulStepResult() { self.witnessPassedStep(); - self.log(JsonFormatter.PASSED_STEP_CHARACTER); }, handlePendingStepResult: function handlePendingStepResult() { self.witnessPendingStep(); self.markCurrentScenarioAsPending(); - self.log(JsonFormatter.PENDING_STEP_CHARACTER); }, handleSkippedStepResult: function handleSkippedStepResult() { self.witnessSkippedStep(); - self.log(JsonFormatter.SKIPPED_STEP_CHARACTER); }, handleUndefinedStepResult: function handleUndefinedStepResult(stepResult) { @@ -131,20 +118,16 @@ var JsonFormatter = function(options) { self.storeUndefinedStep(step); self.witnessUndefinedStep(); self.markCurrentScenarioAsUndefined(); - self.log(JsonFormatter.UNDEFINED_STEP_CHARACTER); }, handleFailedStepResult: function handleFailedStepResult(stepResult) { self.storeFailedStepResult(stepResult); self.witnessFailedStep(); self.markCurrentScenarioAsFailing(); - self.log(JsonFormatter.FAILED_STEP_CHARACTER); }, handleAfterFeaturesEvent: function handleAfterFeaturesEvent(event, callback) { - self.logSummary(); - // Is this where we would dump the JSON? - process.stdout.write(JSON.stringify(blob, null, 2)); + process.stdout.write(JSON.stringify(output, null, 2)); callback(); }, @@ -164,9 +147,6 @@ var JsonFormatter = function(options) { }, prepareBeforeScenario: function prepareBeforeScenario() { - - - currentScenarioFailing = false; currentScenarioPending = false; currentScenarioUndefined = false; @@ -229,87 +209,13 @@ var JsonFormatter = function(options) { return undefinedStepLogBuffer; }, - logSummary: function logSummary() { - self.log("\n\n"); - if (self.witnessedAnyFailedStep()) - self.logFailedStepResults(); - self.logScenariosSummary(); - self.logStepsSummary(); - if (self.witnessedAnyUndefinedStep()) - self.logUndefinedStepSnippets(); - }, - - logFailedStepResults: function logFailedStepResults() { - self.log("(::) failed steps (::)\n\n"); - failedStepResults.syncForEach(function(stepResult) { - self.logFailedStepResult(stepResult); - }); - self.log("Failing scenarios:\n"); - var failedScenarios = self.getFailedScenarioLogBuffer(); - self.log(failedScenarios); - self.log("\n"); - }, - - logFailedStepResult: function logFailedStepResult(stepResult) { - var failureMessage = stepResult.getFailureException(); - self.log(failureMessage.stack || failureMessage); - self.log("\n\n"); - }, - - logScenariosSummary: function logScenariosSummary() { - var scenarioCount = self.getScenarioCount(); - var passedScenarioCount = self.getPassedScenarioCount(); - var undefinedScenarioCount = self.getUndefinedScenarioCount(); - var pendingScenarioCount = self.getPendingScenarioCount(); - var failedScenarioCount = self.getFailedScenarioCount(); - var details = []; - - self.log(scenarioCount + " scenario" + (scenarioCount != 1 ? "s" : "")); - if (scenarioCount > 0 ) { - if (failedScenarioCount > 0) - details.push(failedScenarioCount + " failed"); - if (undefinedScenarioCount > 0) - details.push(undefinedScenarioCount + " undefined"); - if (pendingScenarioCount > 0) - details.push(pendingScenarioCount + " pending"); - if (passedScenarioCount > 0) - details.push(passedScenarioCount + " passed"); - self.log(" (" + details.join(', ') + ")"); - } - self.log("\n"); - }, - - logStepsSummary: function logStepsSummary() { - var stepCount = self.getStepCount(); - var passedStepCount = self.getPassedStepCount(); - var undefinedStepCount = self.getUndefinedStepCount(); - var skippedStepCount = self.getSkippedStepCount(); - var pendingStepCount = self.getPendingStepCount(); - var failedStepCount = self.getFailedStepCount(); - var details = []; - - self.log(stepCount + " step" + (stepCount != 1 ? "s" : "")); - if (stepCount > 0) { - if (failedStepCount > 0) - details.push(failedStepCount + " failed"); - if (undefinedStepCount > 0) - details.push(undefinedStepCount + " undefined"); - if (pendingStepCount > 0) - details.push(pendingStepCount + " pending"); - if (skippedStepCount > 0) - details.push(skippedStepCount + " skipped"); - if (passedStepCount > 0) - details.push(passedStepCount + " passed"); - self.log(" (" + details.join(', ') + ")"); - } - self.log("\n"); - }, - +/* logUndefinedStepSnippets: function logUndefinedStepSnippets() { var undefinedStepLogBuffer = self.getUndefinedStepLogBuffer(); self.log("\nYou can implement step definitions for undefined steps with these snippets:\n\n"); self.log(undefinedStepLogBuffer); }, +*/ witnessPassedScenario: function witnessPassedScenario() { passedScenarioCount++; @@ -412,11 +318,6 @@ var JsonFormatter = function(options) { }; return self; }; -JsonFormatter.PASSED_STEP_CHARACTER = '.'; -JsonFormatter.SKIPPED_STEP_CHARACTER = '-'; -JsonFormatter.UNDEFINED_STEP_CHARACTER = 'U'; -JsonFormatter.PENDING_STEP_CHARACTER = 'P'; -JsonFormatter.FAILED_STEP_CHARACTER = 'F'; // TODO: Factor out to make common to all handlers JsonFormatter.EVENT_HANDLER_NAME_PREFIX = 'handle'; diff --git a/spec/cucumber/listener/json_formatter_spec.js b/spec/cucumber/listener/json_formatter_spec.js index 1a6c225ae..cfa395512 100644 --- a/spec/cucumber/listener/json_formatter_spec.js +++ b/spec/cucumber/listener/json_formatter_spec.js @@ -1,13 +1,13 @@ require('../../support/spec_helper'); -describe("Cucumber.Listener.ProgressFormatter", function() { +describe("Cucumber.Listener.JsonFormatter", function() { var Cucumber = requireLib('cucumber'); var listener, failedStepResults; beforeEach(function() { failedStepResults = createSpy("Failed steps"); spyOn(Cucumber.Type, 'Collection').andReturn(failedStepResults); - listener = Cucumber.Listener.ProgressFormatter(); + listener = Cucumber.Listener.JsonFormatter(); }); describe("constructor", function() { @@ -16,80 +16,6 @@ describe("Cucumber.Listener.ProgressFormatter", function() { }); }); - describe("log()", function() { - var logged, alsoLogged, loggedBuffer; - - beforeEach(function() { - logged = "this was logged"; - alsoLogged = "this was also logged"; - loggedBuffer = logged + alsoLogged; - spyOn(process.stdout, 'write'); - }); - - it("records logged strings", function() { - listener.log(logged); - listener.log(alsoLogged); - expect(listener.getLogs()).toBe(loggedBuffer); - }); - - it("outputs the logged string to STDOUT by default", function() { - listener.log(logged); - expect(process.stdout.write).toHaveBeenCalledWith(logged); - }); - - describe("when asked to output to STDOUT", function() { - beforeEach(function() { - listener = Cucumber.Listener.ProgressFormatter({logToConsole: true}); - }); - - it("outputs the logged string to STDOUT", function() { - listener.log(logged); - expect(process.stdout.write).toHaveBeenCalledWith(logged); - }); - }); - - describe("when asked to not output to STDOUT", function() { - beforeEach(function() { - listener = Cucumber.Listener.ProgressFormatter({logToConsole: false}); - }); - - it("does not output anything to STDOUT", function() { - listener.log(logged); - expect(process.stdout.write).not.toHaveBeenCalledWith(logged); - }); - }); - - describe("when asked to output to a function", function() { - var userFunction; - - beforeEach(function() { - userFunction = createSpy("output user function"); - listener = Cucumber.Listener.ProgressFormatter({logToFunction: userFunction}); - }); - - it("calls the function with the logged string", function() { - listener.log(logged); - expect(userFunction).toHaveBeenCalledWith(logged); - }); - }); - }); - - describe("getLogs()", function() { - it("returns the logged buffer", function() { - var logged = "this was logged"; - var alsoLogged = "this was also logged"; - var loggedBuffer = logged + alsoLogged; - spyOn(process.stdout, 'write'); // prevent actual output during spec execution - listener.log(logged); - listener.log(alsoLogged); - expect(listener.getLogs()).toBe(loggedBuffer); - }); - - it("returns an empty string when the listener did not log anything yet", function() { - expect(listener.getLogs()).toBe(""); - }); - }); - describe("hear()", function() { var event, callback; var eventHandler; @@ -380,7 +306,6 @@ describe("Cucumber.Listener.ProgressFormatter", function() { describe("handleSuccessfulStepResult()", function() { beforeEach(function() { spyOn(listener, 'witnessPassedStep'); - spyOn(listener, 'log'); }); it("witnesses a passed step", function() { @@ -388,17 +313,12 @@ describe("Cucumber.Listener.ProgressFormatter", function() { expect(listener.witnessPassedStep).toHaveBeenCalled(); }); - it("logs the passing step character", function() { - listener.handleSuccessfulStepResult(); - expect(listener.log).toHaveBeenCalledWith(Cucumber.Listener.ProgressFormatter.PASSED_STEP_CHARACTER); - }); }); describe("handlePendingStepResult()", function() { beforeEach(function() { spyOn(listener, 'witnessPendingStep'); spyOn(listener, 'markCurrentScenarioAsPending'); - spyOn(listener, 'log') }); it("witnesses a pending step", function() { @@ -411,16 +331,11 @@ describe("Cucumber.Listener.ProgressFormatter", function() { expect(listener.markCurrentScenarioAsPending).toHaveBeenCalled(); }); - it("logs the pending step character", function() { - listener.handlePendingStepResult(); - expect(listener.log).toHaveBeenCalledWith(Cucumber.Listener.ProgressFormatter.PENDING_STEP_CHARACTER); - }); }); describe("handleSkippedStepResult()", function() { beforeEach(function() { spyOn(listener, 'witnessSkippedStep'); - spyOn(listener, 'log'); }); it("counts one more skipped step", function() { @@ -428,10 +343,6 @@ describe("Cucumber.Listener.ProgressFormatter", function() { expect(listener.witnessSkippedStep).toHaveBeenCalled(); }); - it("logs the skipped step character", function() { - listener.handleSkippedStepResult(); - expect(listener.log).toHaveBeenCalledWith(Cucumber.Listener.ProgressFormatter.SKIPPED_STEP_CHARACTER); - }); }); describe("handleUndefinedStepResult()", function() { @@ -443,7 +354,6 @@ describe("Cucumber.Listener.ProgressFormatter", function() { spyOn(listener, 'storeUndefinedStep'); spyOn(listener, 'witnessUndefinedStep'); spyOn(listener, 'markCurrentScenarioAsUndefined'); - spyOn(listener, 'log'); }); it("gets the step from the step result", function() { @@ -466,10 +376,6 @@ describe("Cucumber.Listener.ProgressFormatter", function() { expect(listener.markCurrentScenarioAsUndefined).toHaveBeenCalled(); }); - it("logs the undefined step character", function() { - listener.handleUndefinedStepResult(stepResult); - expect(listener.log).toHaveBeenCalledWith(Cucumber.Listener.ProgressFormatter.UNDEFINED_STEP_CHARACTER); - }); }); describe("handleFailedStepResult()", function() { @@ -480,7 +386,6 @@ describe("Cucumber.Listener.ProgressFormatter", function() { spyOn(listener, 'storeFailedStepResult'); spyOn(listener, 'witnessFailedStep'); spyOn(listener, 'markCurrentScenarioAsFailing'); - spyOn(listener, 'log'); }); it("stores the failed step result", function() { @@ -498,17 +403,16 @@ describe("Cucumber.Listener.ProgressFormatter", function() { expect(listener.markCurrentScenarioAsFailing).toHaveBeenCalled(); }); - it("logs the failed step character", function() { - listener.handleFailedStepResult(stepResult); - expect(listener.log).toHaveBeenCalledWith(Cucumber.Listener.ProgressFormatter.FAILED_STEP_CHARACTER); - }); }); describe("handleBeforeScenarioEvent", function() { - var event, callback; + var scenario, event, callback; beforeEach(function() { - event = createSpy("event"); + scenario = createSpyWithStubs("scenario", {getName: "A Scenario"}); + event = createSpyWithStubs("event", {getPayloadItem: scenario}); + + callback = createSpy("callback"); spyOn(listener, 'prepareBeforeScenario'); }); @@ -530,12 +434,6 @@ describe("Cucumber.Listener.ProgressFormatter", function() { beforeEach(function() { event = createSpy("Event"); callback = createSpy("Callback"); - spyOn(listener, "logSummary"); - }); - - it("displays a summary", function() { - listener.handleAfterFeaturesEvent(event, callback); - expect(listener.logSummary).toHaveBeenCalled(); }); it("calls back", function() { @@ -805,655 +703,6 @@ describe("Cucumber.Listener.ProgressFormatter", function() { }); }); - describe("logSummary()", function() { - var scenarioCount, passedScenarioCount, failedScenarioCount; - var stepCount, passedStepCount; - - beforeEach(function() { - spyOn(listener, 'log'); - spyOn(listener, 'witnessedAnyFailedStep'); - spyOn(listener, 'witnessedAnyUndefinedStep'); - spyOn(listener, 'logFailedStepResults'); - spyOn(listener, 'logScenariosSummary'); - spyOn(listener, 'logStepsSummary'); - spyOn(listener, 'logUndefinedStepSnippets'); - }); - - it("logs two line feeds", function() { - listener.logSummary(); - expect(listener.log).toHaveBeenCalledWith("\n\n"); - }); - - it("checks wether there are failed steps or not", function() { - listener.logSummary(); - expect(listener.witnessedAnyFailedStep).toHaveBeenCalled(); - }); - - describe("when there are failed steps", function() { - beforeEach(function() { - listener.witnessedAnyFailedStep.andReturn(true); - }); - - it("logs the failed steps", function() { - listener.logSummary(); - expect(listener.logFailedStepResults).toHaveBeenCalled(); - }); - }); - - describe("when there are no failed steps", function() { - beforeEach(function() { - listener.witnessedAnyFailedStep.andReturn(false); - }); - - it("does not log failed steps", function() { - listener.logSummary(); - expect(listener.logFailedStepResults).not.toHaveBeenCalled(); - }); - }); - - it("logs the scenarios summary", function() { - listener.logSummary(); - expect(listener.logScenariosSummary).toHaveBeenCalled(); - }); - - it("logs the steps summary", function() { - listener.logSummary(); - expect(listener.logStepsSummary).toHaveBeenCalled(); - }); - - it("checks wether there are undefined steps or not", function() { - listener.logSummary(); - expect(listener.witnessedAnyUndefinedStep).toHaveBeenCalled(); - }); - - describe("when there are undefined steps", function() { - beforeEach(function() { - listener.witnessedAnyUndefinedStep.andReturn(true); - }); - - it("logs the undefined step snippets", function() { - listener.logSummary(); - expect(listener.logUndefinedStepSnippets).toHaveBeenCalled(); - }); - }); - - describe("when there are no undefined steps", function() { - beforeEach(function() { - listener.witnessedAnyUndefinedStep.andReturn(false); - }); - - it("does not log the undefined step snippets", function() { - listener.logSummary(); - expect(listener.logUndefinedStepSnippets).not.toHaveBeenCalled(); - }); - }); - }); - - describe("logFailedStepResults()", function() { - var failedScenarioLogBuffer; - - beforeEach(function() { - failedScenarioLogBuffer = createSpy("failed scenario log buffer"); - spyOnStub(failedStepResults, 'syncForEach'); - spyOn(listener, 'log'); - spyOn(listener, 'getFailedScenarioLogBuffer').andReturn(failedScenarioLogBuffer); - }); - - it("logs a failed steps header", function() { - listener.logFailedStepResults(); - expect(listener.log).toHaveBeenCalledWith("(::) failed steps (::)\n\n"); - }); - - it("iterates synchronously over the failed step results", function() { - listener.logFailedStepResults(); - expect(failedStepResults.syncForEach).toHaveBeenCalled(); - expect(failedStepResults.syncForEach).toHaveBeenCalledWithAFunctionAsNthParameter(1); - }); - - describe("for each failed step result", function() { - var userFunction, failedStep, forEachCallback; - - beforeEach(function() { - listener.logFailedStepResults(); - userFunction = failedStepResults.syncForEach.mostRecentCall.args[0]; - failedStepResult = createSpy("failed step result"); - spyOn(listener, 'logFailedStepResult'); - }); - - it("tells the visitor to visit the feature and call back when finished", function() { - userFunction(failedStepResult); - expect(listener.logFailedStepResult).toHaveBeenCalledWith(failedStepResult); - }); - }); - - it("logs a failed scenarios header", function() { - listener.logFailedStepResults(); - expect(listener.log).toHaveBeenCalledWith("Failing scenarios:\n"); - }); - - it("gets the failed scenario details from its log buffer", function() { - listener.logFailedStepResults(); - expect(listener.getFailedScenarioLogBuffer).toHaveBeenCalled(); - }); - - it("logs the failed scenario details", function() { - listener.logFailedStepResults(); - expect(listener.log).toHaveBeenCalledWith(failedScenarioLogBuffer); - }); - - it("logs a line break", function() { - listener.logFailedStepResults(); - expect(listener.log).toHaveBeenCalledWith("\n"); - }); - }); - - describe("logFailedStepResult()", function() { - var stepResult, failureException; - - beforeEach(function() { - spyOn(listener, 'log'); - failureException = createSpy('caught exception'); - stepResult = createSpyWithStubs("failed step result", { getFailureException: failureException }); - }); - - it("gets the failure exception from the step result", function() { - listener.logFailedStepResult(stepResult); - expect(stepResult.getFailureException).toHaveBeenCalled(); - }); - - describe("when the failure exception has a stack", function() { - beforeEach(function() { - failureException.stack = createSpy('failure exception stack'); - }); - - it("logs the stack", function() { - listener.logFailedStepResult(stepResult); - expect(listener.log).toHaveBeenCalledWith(failureException.stack); - }); - }); - - describe("when the failure exception has no stack", function() { - it("logs the exception itself", function() { - listener.logFailedStepResult(stepResult); - expect(listener.log).toHaveBeenCalledWith(failureException); - }); - }); - - it("logs two line breaks", function() { - listener.logFailedStepResult(stepResult); - expect(listener.log).toHaveBeenCalledWith("\n\n"); - }); - }); - - describe("logScenariosSummary()", function() { - var scenarioCount, passedScenarioCount, pendingScenarioCount, failedScenarioCount; - - beforeEach(function() { - scenarioCount = 12; - passedScenarioCount = 9; - undefinedScenarioCount = 17; - pendingScenarioCount = 7; - failedScenarioCount = 15; - spyOn(listener, 'log'); - spyOn(listener, 'getScenarioCount').andReturn(scenarioCount); - spyOn(listener, 'getPassedScenarioCount').andReturn(passedScenarioCount); - spyOn(listener, 'getUndefinedScenarioCount').andReturn(undefinedScenarioCount); - spyOn(listener, 'getPendingScenarioCount').andReturn(pendingScenarioCount); - spyOn(listener, 'getFailedScenarioCount').andReturn(failedScenarioCount); - }); - - it("gets the number of scenarios", function() { - listener.logScenariosSummary(); - expect(listener.getScenarioCount).toHaveBeenCalled(); - }); - - describe("when there are no scenarios", function() { - beforeEach(function() { listener.getScenarioCount.andReturn(0); }); - - it("logs 0 scenarios", function() { - listener.logScenariosSummary(); - expect(listener.log).toHaveBeenCalledWithStringMatching(/0 scenarios/); - }); - - it("does not log any details", function() { - listener.logScenariosSummary(); - expect(listener.log).not.toHaveBeenCalledWithStringMatching(/\(.*\)/); - }); - }); - - describe("when there are scenarios", function() { - beforeEach(function() { listener.getScenarioCount.andReturn(12); }); - - describe("when there is one scenario", function() { - beforeEach(function() { listener.getScenarioCount.andReturn(1); }); - - it("logs one scenario", function() { - listener.logScenariosSummary(); - expect(listener.log).toHaveBeenCalledWithStringMatching(/1 scenario([^s]|$)/); - }); - }); - - describe("when there are 2 or more scenarios", function() { - beforeEach(function() { listener.getScenarioCount.andReturn(2); }); - - it("logs two or more scenarios", function() { - listener.logScenariosSummary(); - expect(listener.log).toHaveBeenCalledWithStringMatching(/2 scenarios/); - }); - }); - - it("gets the number of failed scenarios", function() { - listener.logScenariosSummary(); - expect(listener.getFailedScenarioCount).toHaveBeenCalled(); - }); - - describe("when there are no failed scenarios", function() { - beforeEach(function() { listener.getFailedScenarioCount.andReturn(0); }); - - it("does not log failed scenarios", function() { - listener.logScenariosSummary(); - expect(listener.log).not.toHaveBeenCalledWithStringMatching(/failed/); - }); - }); - - describe("when there is one failed scenario", function() { - beforeEach(function() { listener.getFailedScenarioCount.andReturn(1); }); - - it("logs a failed scenario", function() { - listener.logScenariosSummary(); - expect(listener.log).toHaveBeenCalledWithStringMatching(/1 failed/); - }); - }); - - describe("when there are two or more failed scenarios", function() { - beforeEach(function() { listener.getFailedScenarioCount.andReturn(2); }); - - it("logs the number of failed scenarios", function() { - listener.logScenariosSummary(); - expect(listener.log).toHaveBeenCalledWithStringMatching(/2 failed/); - }); - }); - - it("gets the number of undefined scenarios", function() { - listener.logScenariosSummary(); - expect(listener.getUndefinedScenarioCount).toHaveBeenCalled(); - }); - - describe("when there are no undefined scenarios", function() { - beforeEach(function() { listener.getUndefinedScenarioCount.andReturn(0); }); - - it("does not log passed scenarios", function() { - listener.logScenariosSummary(); - expect(listener.log).not.toHaveBeenCalledWithStringMatching(/undefined/); - }); - }); - - describe("when there is one undefined scenario", function() { - beforeEach(function() { listener.getUndefinedScenarioCount.andReturn(1); }); - - it("logs one undefined scenario", function() { - listener.logScenariosSummary(); - expect(listener.log).toHaveBeenCalledWithStringMatching(/1 undefined/); - }); - }); - - describe("when there are two or more undefined scenarios", function() { - beforeEach(function() { listener.getUndefinedScenarioCount.andReturn(2); }); - - it("logs the undefined scenarios", function() { - listener.logScenariosSummary(); - expect(listener.log).toHaveBeenCalledWithStringMatching(/2 undefined/); - }); - }); - - it("gets the number of pending scenarios", function() { - listener.logScenariosSummary(); - expect(listener.getPendingScenarioCount).toHaveBeenCalled(); - }); - - describe("when there are no pending scenarios", function() { - beforeEach(function() { listener.getPendingScenarioCount.andReturn(0); }); - - it("does not log passed scenarios", function() { - listener.logScenariosSummary(); - expect(listener.log).not.toHaveBeenCalledWithStringMatching(/pending/); - }); - }); - - describe("when there is one pending scenario", function() { - beforeEach(function() { listener.getPendingScenarioCount.andReturn(1); }); - - it("logs one pending scenario", function() { - listener.logScenariosSummary(); - expect(listener.log).toHaveBeenCalledWithStringMatching(/1 pending/); - }); - }); - - describe("when there are two or more pending scenarios", function() { - beforeEach(function() { listener.getPendingScenarioCount.andReturn(2); }); - - it("logs the pending scenarios", function() { - listener.logScenariosSummary(); - expect(listener.log).toHaveBeenCalledWithStringMatching(/2 pending/); - }); - }); - - it("gets the number of passed scenarios", function() { - listener.logScenariosSummary(); - expect(listener.getPassedScenarioCount).toHaveBeenCalled(); - }); - - describe("when there are no passed scenarios", function() { - beforeEach(function() { listener.getPassedScenarioCount.andReturn(0); }); - - it("does not log passed scenarios", function() { - listener.logScenariosSummary(); - expect(listener.log).not.toHaveBeenCalledWithStringMatching(/passed/); - }); - }); - - describe("when there is one passed scenario", function() { - beforeEach(function() { listener.getPassedScenarioCount.andReturn(1); }); - - it("logs 1 passed scenarios", function() { - listener.logScenariosSummary(); - expect(listener.log).toHaveBeenCalledWithStringMatching(/1 passed/); - }); - }); - - describe("when there are two or more passed scenarios", function() { - beforeEach(function() { listener.getPassedScenarioCount.andReturn(2); }); - - it("logs the number of passed scenarios", function() { - listener.logScenariosSummary(); - expect(listener.log).toHaveBeenCalledWithStringMatching(/2 passed/); - }); - }); - }); - }); - - describe("logStepsSummary()", function() { - var stepCount, passedStepCount, failedStepCount, skippedStepCount, pendingStepCount; - - beforeEach(function() { - stepCount = 34; - passedStepCount = 31; - failedStepCount = 7; - skippedStepCount = 5; - undefinedStepCount = 4; - pendingStepCount = 2; - spyOn(listener, 'log'); - spyOn(listener, 'getStepCount').andReturn(stepCount); - spyOn(listener, 'getPassedStepCount').andReturn(passedStepCount); - spyOn(listener, 'getFailedStepCount').andReturn(failedStepCount); - spyOn(listener, 'getSkippedStepCount').andReturn(skippedStepCount); - spyOn(listener, 'getUndefinedStepCount').andReturn(undefinedStepCount); - spyOn(listener, 'getPendingStepCount').andReturn(pendingStepCount); - }); - - it("gets the number of steps", function() { - listener.logStepsSummary(); - expect(listener.getStepCount).toHaveBeenCalled(); - }); - - describe("when there are no steps", function() { - beforeEach(function() { - listener.getStepCount.andReturn(0); - }); - - it("logs 0 steps", function() { - listener.logStepsSummary(); - expect(listener.log).toHaveBeenCalledWithStringMatching(/0 steps/); - }); - - it("does not log any details", function() { - listener.logStepsSummary(); - expect(listener.log).not.toHaveBeenCalledWithStringMatching(/\(.*\)/); - }); - }); - - describe("when there are steps", function() { - beforeEach(function() { listener.getStepCount.andReturn(13); }); - - describe("when there is one step", function() { - beforeEach(function() { - listener.getStepCount.andReturn(1); - }); - - it("logs 1 step", function() { - listener.logStepsSummary(); - expect(listener.log).toHaveBeenCalledWithStringMatching(/1 step/); - }); - }); - - describe("when there are two or more steps", function() { - beforeEach(function() { - listener.getStepCount.andReturn(2); - }); - - it("logs the number of steps", function() { - listener.logStepsSummary(); - expect(listener.log).toHaveBeenCalledWithStringMatching(/2 steps/); - }); - }); - - it("gets the number of failed steps", function() { - listener.logStepsSummary(); - expect(listener.getFailedStepCount).toHaveBeenCalled(); - }); - - describe("when there are no failed steps", function() { - beforeEach(function() { - listener.getFailedStepCount.andReturn(0); - }); - - it("does not log failed steps", function() { - listener.logStepsSummary(); - expect(listener.log).not.toHaveBeenCalledWithStringMatching(/failed/); - }); - }); - - describe("when there is one failed step", function() { - beforeEach(function() { - listener.getFailedStepCount.andReturn(1); - }); - - it("logs one failed step", function() { - listener.logStepsSummary(); - expect(listener.log).toHaveBeenCalledWithStringMatching(/1 failed/); - }); - }); - - describe("when there is two or more failed steps", function() { - beforeEach(function() { - listener.getFailedStepCount.andReturn(2); - }); - - it("logs the number of failed steps", function() { - listener.logStepsSummary(); - expect(listener.log).toHaveBeenCalledWithStringMatching(/2 failed/); - }); - }); - - it("gets the number of undefined steps", function() { - listener.logStepsSummary(); - expect(listener.getUndefinedStepCount).toHaveBeenCalled(); - }); - - describe("when there are no undefined steps", function() { - beforeEach(function() { - listener.getUndefinedStepCount.andReturn(0); - }); - - it("does not log undefined steps", function() { - listener.logStepsSummary(); - expect(listener.log).not.toHaveBeenCalledWithStringMatching(/undefined/); - }); - }); - - describe("when there is one undefined step", function() { - beforeEach(function() { - listener.getUndefinedStepCount.andReturn(1); - }); - - it("logs one undefined steps", function() { - listener.logStepsSummary(); - expect(listener.log).toHaveBeenCalledWithStringMatching(/1 undefined/); - }); - }); - - describe("when there are two or more undefined steps", function() { - beforeEach(function() { - listener.getUndefinedStepCount.andReturn(2); - }); - - it("logs the number of undefined steps", function() { - listener.logStepsSummary(); - expect(listener.log).toHaveBeenCalledWithStringMatching(/2 undefined/); - }); - }); - - it("gets the number of pending steps", function() { - listener.logStepsSummary(); - expect(listener.getPendingStepCount).toHaveBeenCalled(); - }); - - describe("when there are no pending steps", function() { - beforeEach(function() { - listener.getPendingStepCount.andReturn(0); - }); - - it("does not log pending steps", function() { - listener.logStepsSummary(); - expect(listener.log).not.toHaveBeenCalledWithStringMatching(/pending/); - }); - }); - - describe("when there is one pending step", function() { - beforeEach(function() { - listener.getPendingStepCount.andReturn(1); - }); - - it("logs one pending steps", function() { - listener.logStepsSummary(); - expect(listener.log).toHaveBeenCalledWithStringMatching(/1 pending/); - }); - }); - - describe("when there are two or more pending steps", function() { - beforeEach(function() { - listener.getPendingStepCount.andReturn(2); - }); - - it("logs the number of pending steps", function() { - listener.logStepsSummary(); - expect(listener.log).toHaveBeenCalledWithStringMatching(/2 pending/); - }); - }); - - it("gets the number of skipped steps", function() { - listener.logStepsSummary(); - expect(listener.getSkippedStepCount).toHaveBeenCalled(); - }); - - describe("when there are no skipped steps", function() { - beforeEach(function() { - listener.getSkippedStepCount.andReturn(0); - }); - - it("does not log skipped steps", function() { - listener.logStepsSummary(); - expect(listener.log).not.toHaveBeenCalledWithStringMatching(/skipped/); - }); - }); - - describe("when there is one skipped step", function() { - beforeEach(function() { - listener.getSkippedStepCount.andReturn(1); - }); - - it("logs one skipped steps", function() { - listener.logStepsSummary(); - expect(listener.log).toHaveBeenCalledWithStringMatching(/1 skipped/); - }); - }); - - describe("when there are two or more skipped steps", function() { - beforeEach(function() { - listener.getSkippedStepCount.andReturn(2); - }); - - it("logs the number of skipped steps", function() { - listener.logStepsSummary(); - expect(listener.log).toHaveBeenCalledWithStringMatching(/2 skipped/); - }); - }); - - it("gets the number of passed steps", function() { - listener.logStepsSummary(); - expect(listener.getPassedStepCount).toHaveBeenCalled(); - }); - - describe("when there are no passed steps", function() { - beforeEach(function() { - listener.getPassedStepCount.andReturn(0); - }); - - it("does not log passed steps", function() { - listener.logStepsSummary(); - expect(listener.log).not.toHaveBeenCalledWithStringMatching(/passed/); - }); - }); - - describe("when there is one passed step", function() { - beforeEach(function() { - listener.getPassedStepCount.andReturn(1); - }); - - it("logs one passed step", function() { - listener.logStepsSummary(); - expect(listener.log).toHaveBeenCalledWithStringMatching(/1 passed/); - }); - }); - - describe("when there is two or more passed steps", function() { - beforeEach(function() { - listener.getPassedStepCount.andReturn(2); - }); - - it("logs the number of passed steps", function() { - listener.logStepsSummary(); - expect(listener.log).toHaveBeenCalledWithStringMatching(/2 passed/); - }); - }); - }); - }); - - describe("logUndefinedStepSnippets()", function() { - var undefinedStepLogBuffer; - - beforeEach(function() { - undefinedStepLogBuffer = createSpy("undefined step log buffer"); - spyOn(listener, 'log'); - spyOn(listener, 'getUndefinedStepLogBuffer').andReturn(undefinedStepLogBuffer); - }); - - it("logs a little explanation about the snippets", function() { - listener.logUndefinedStepSnippets(); - expect(listener.log).toHaveBeenCalledWith("\nYou can implement step definitions for undefined steps with these snippets:\n\n"); - }); - - it("gets the undefined steps log buffer", function() { - listener.logUndefinedStepSnippets(); - expect(listener.getUndefinedStepLogBuffer).toHaveBeenCalled(); - }); - - it("logs the undefined steps", function() { - listener.logUndefinedStepSnippets(); - expect(listener.log).toHaveBeenCalledWith(undefinedStepLogBuffer); - }); - }); - describe("getScenarioCount()", function() { var passedScenarioCount, undefinedScenarioCount, pendingScenarioCount, failedScenarioCount; diff --git a/spec/tmp.txt b/spec/tmp.txt new file mode 100644 index 000000000..b1511927b --- /dev/null +++ b/spec/tmp.txt @@ -0,0 +1,728 @@ +{ + "name": "", + "id": "" +} +Cucumber.Listener.JsonFormatter + + constructor + creates a collection to store the failed steps + + hear() + checks wether there is a handler for the event + + when there is a handler for that event + gets the handler for that event + calls the handler with the event and the callback + does not callback + + when there are no handlers for that event + calls back + does not get the handler for the event + + hasHandlerForEvent + builds the name of the handler for that event + + when the handler exists + returns true + + when the handler does not exist + returns false + + buildHandlerNameForEvent + gets the name of the event + returns the name of the event with prefix 'handle' and suffix 'Event' + + getHandlerForEvent() + gets the name of the handler for the event + + when an event handler exists for the event + returns the event handler + + when no event handlers exist for the event + returns nothing + + handleStepResultEvent() + gets the step result from the event payload + checks wether the step was successful or not + + when the step passed + handles the successful step result + + when the step did not pass + does not handle a successful step result + checks wether the step is pending + + when the step was pending + handles the pending step result + + when the step was not pending + does not handle a pending step result + checks wether the step was skipped + + when the step was skipped + handles the skipped step result + + when the step was not skipped + does not handle a skipped step result + checks wether the step was undefined + + when the step was undefined + handles the undefined step result + + when the step was not undefined + does not handle a skipped step result + handles a failed step result + calls back + + handleSuccessfulStepResult() + witnesses a passed step + + handlePendingStepResult() + witnesses a pending step + marks the current scenario as pending + + handleSkippedStepResult() + counts one more skipped step + + handleUndefinedStepResult() + gets the step from the step result + stores the undefined step + witnesses an undefined step + marks the current scenario as undefined + + handleFailedStepResult() + stores the failed step result + witnesses a failed step + marks the current scenario as failing + + handleBeforeScenarioEvent + prepares for a new scenario + calls back + + handleAfterFeaturesEvent() + calls back + + handleAfterScenarioEvent() + checks wether the current scenario failed + + when the current scenario failed + witnesses a failed scenario + gets the scenario from the payload + stores the failed scenario + + when the current scenario did not fail + checks wether the current scenario is undefined + + when the current scenario is undefined + witnesses an undefined scenario + + when the current scenario is not undefined + checks wether the current scenario is pending + + when the current scenario is pending + witnesses a pending scenario + + when the current scenario is not pending (passed) + witnesses a passed scenario + calls back + + isCurrentScenarioFailing() + returns false when the current scenario did not fail yet + returns true when a step in the current scenario failed + + isCurrentScenarioPending() + returns false when the current scenario was not set pending yet + returns true when the current scenario was set pending + + isCurrentScenarioUndefined() + returns false when the current scenario was not set undefined yet + returns true when the current scenario was set undefined + + prepareBeforeScenario() + unmarks the current scenario as pending + unmarks the current scenario as failing + unmarks the current scenario as undefined + + storeFailedStepResult() + adds the result to the failed step result collection + + storeFailedScenario() + gets the name of the scenario + gets the line of the scenario + appends the scenario details to the failed scenario log buffer + + storeUndefinedStep() + creates a new step definition snippet builder + builds the step definition + appends the snippet to the undefined step log buffer + + getFailedScenarioLogBuffer() [appendStringToFailedScenarioLogBuffer()] + returns the logged failed scenario details + returns all logged failed scenario lines joined with a line break + + getUndefinedStepLogBuffer() [appendStringToUndefinedStepLogBuffer()] + returns the logged undefined step details + returns all logged failed scenario lines joined with a line break + + appendStringToUndefinedStepLogBuffer() [getUndefinedStepLogBuffer()] + does not log the same string twice + + getScenarioCount() + gets the number of passed scenarios + gets the number of undefined scenarios + gets the number of pending scenarios + gets the number of failed scenarios + returns the sum of passed, undefined, pending aand failed scenarios + + getStepCount() + gets the number of passed steps + gets the number of undefined steps + gets the number of skipped steps + gets the number of pending steps + gets the number of failed steps + returns the sum of passed steps and failed steps + + passed scenario counting + + witnessPassedScenario() + counts one more passed scenario + + getPassedScenarioCount() + returns 0 when no scenario passed + returns 1 when one scenario passed + returns 2 when two scenarios passed + returns 3 when three scenarios passed + + undefined scenario counting + + getUndefinedScenarioCount() + returns 0 when no scenarios undefined + returns 1 when one scenario passed + returns 2 when two scenarios passed + returns 3 when two scenarios passed + + pending scenario counting + + getPendingScenarioCount() + returns 0 when no scenarios pending + returns 1 when one scenario passed + returns 2 when two scenarios passed + returns 3 when two scenarios passed + + failed scenario counting + + getFailedScenarioCount() + returns 0 when no scenarios failed + returns 1 when one scenario passed + returns 2 when two scenarios passed + returns 3 when two scenarios passed + + passed step counting + + witnessPassedStep() + counts one more passed step + + getPassedStepCount() + returns 0 when no step passed + returns 1 when one step passed + returns 2 when two steps passed + returns 3 when three steps passed + + failed step counting + + getFailedStepCount() + returns 0 when no steps failed + returns 1 when one step passed + returns 2 when two steps passed + returns 3 when two steps passed + + skipped step counting + + getSkippedStepCount() + returns 0 when no steps skipped + returns 1 when one step passed + returns 2 when two steps passed + returns 3 when two steps passed + + undefined step counting + + getUndefinedStepCount() + returns 0 when no steps undefined + returns 1 when one step passed + returns 2 when two steps passed + returns 3 when two steps passed + + pending step counting + + getPendingStepCount() + returns 0 when no steps pending + returns 1 when one step passed + returns 2 when two steps passed + returns 3 when two steps passed + + witnessedAnyFailedStep() + returns false when no failed step were encountered + returns true when one or more steps were witnessed + + witnessedAnyUndefinedStep() + returns false when no undefined step were encountered + returns true when one or more steps were witnessed + +Cucumber.Listener.ProgressFormatter + + constructor + creates a collection to store the failed steps + + log() + records logged strings + outputs the logged string to STDOUT by default + + when asked to output to STDOUT + outputs the logged string to STDOUT + + when asked to not output to STDOUT + does not output anything to STDOUT + + when asked to output to a function + calls the function with the logged string + + getLogs() + returns the logged buffer + returns an empty string when the listener did not log anything yet + + hear() + checks wether there is a handler for the event + + when there is a handler for that event + gets the handler for that event + calls the handler with the event and the callback + does not callback + + when there are no handlers for that event + calls back + does not get the handler for the event + + hasHandlerForEvent + builds the name of the handler for that event + + when the handler exists + returns true + + when the handler does not exist + returns false + + buildHandlerNameForEvent + gets the name of the event + returns the name of the event with prefix 'handle' and suffix 'Event' + + getHandlerForEvent() + gets the name of the handler for the event + + when an event handler exists for the event + returns the event handler + + when no event handlers exist for the event + returns nothing + + handleStepResultEvent() + gets the step result from the event payload + checks wether the step was successful or not + + when the step passed + handles the successful step result + + when the step did not pass + does not handle a successful step result + checks wether the step is pending + + when the step was pending + handles the pending step result + + when the step was not pending + does not handle a pending step result + checks wether the step was skipped + + when the step was skipped + handles the skipped step result + + when the step was not skipped + does not handle a skipped step result + checks wether the step was undefined + + when the step was undefined + handles the undefined step result + + when the step was not undefined + does not handle a skipped step result + handles a failed step result + calls back + + handleSuccessfulStepResult() + witnesses a passed step + logs the passing step character + + handlePendingStepResult() + witnesses a pending step + marks the current scenario as pending + logs the pending step character + + handleSkippedStepResult() + counts one more skipped step + logs the skipped step character + + handleUndefinedStepResult() + gets the step from the step result + stores the undefined step + witnesses an undefined step + marks the current scenario as undefined + logs the undefined step character + + handleFailedStepResult() + stores the failed step result + witnesses a failed step + marks the current scenario as failing + logs the failed step character + + handleBeforeScenarioEvent + prepares for a new scenario + calls back + + handleAfterFeaturesEvent() + displays a summary + calls back + + handleAfterScenarioEvent() + checks wether the current scenario failed + + when the current scenario failed + witnesses a failed scenario + gets the scenario from the payload + stores the failed scenario + + when the current scenario did not fail + checks wether the current scenario is undefined + + when the current scenario is undefined + witnesses an undefined scenario + + when the current scenario is not undefined + checks wether the current scenario is pending + + when the current scenario is pending + witnesses a pending scenario + + when the current scenario is not pending (passed) + witnesses a passed scenario + calls back + + isCurrentScenarioFailing() + returns false when the current scenario did not fail yet + returns true when a step in the current scenario failed + + isCurrentScenarioPending() + returns false when the current scenario was not set pending yet + returns true when the current scenario was set pending + + isCurrentScenarioUndefined() + returns false when the current scenario was not set undefined yet + returns true when the current scenario was set undefined + + prepareBeforeScenario() + unmarks the current scenario as pending + unmarks the current scenario as failing + unmarks the current scenario as undefined + + storeFailedStepResult() + adds the result to the failed step result collection + + storeFailedScenario() + gets the name of the scenario + gets the line of the scenario + appends the scenario details to the failed scenario log buffer + + storeUndefinedStep() + creates a new step definition snippet builder + builds the step definition + appends the snippet to the undefined step log buffer + + getFailedScenarioLogBuffer() [appendStringToFailedScenarioLogBuffer()] + returns the logged failed scenario details + returns all logged failed scenario lines joined with a line break + + getUndefinedStepLogBuffer() [appendStringToUndefinedStepLogBuffer()] + returns the logged undefined step details + returns all logged failed scenario lines joined with a line break + + appendStringToUndefinedStepLogBuffer() [getUndefinedStepLogBuffer()] + does not log the same string twice + + logSummary() + logs two line feeds + checks wether there are failed steps or not + + when there are failed steps + logs the failed steps + + when there are no failed steps + does not log failed steps + logs the scenarios summary + logs the steps summary + checks wether there are undefined steps or not + + when there are undefined steps + logs the undefined step snippets + + when there are no undefined steps + does not log the undefined step snippets + + logFailedStepResults() + logs a failed steps header + iterates synchronously over the failed step results + + for each failed step result + tells the visitor to visit the feature and call back when finished + logs a failed scenarios header + gets the failed scenario details from its log buffer + logs the failed scenario details + logs a line break + + logFailedStepResult() + gets the failure exception from the step result + + when the failure exception has a stack + logs the stack + + when the failure exception has no stack + logs the exception itself + logs two line breaks + + logScenariosSummary() + gets the number of scenarios + + when there are no scenarios + logs 0 scenarios + does not log any details + + when there are scenarios + + when there is one scenario + logs one scenario + + when there are 2 or more scenarios + logs two or more scenarios + gets the number of failed scenarios + + when there are no failed scenarios + does not log failed scenarios + + when there is one failed scenario + logs a failed scenario + + when there are two or more failed scenarios + logs the number of failed scenarios + gets the number of undefined scenarios + + when there are no undefined scenarios + does not log passed scenarios + + when there is one undefined scenario + logs one undefined scenario + + when there are two or more undefined scenarios + logs the undefined scenarios + gets the number of pending scenarios + + when there are no pending scenarios + does not log passed scenarios + + when there is one pending scenario + logs one pending scenario + + when there are two or more pending scenarios + logs the pending scenarios + gets the number of passed scenarios + + when there are no passed scenarios + does not log passed scenarios + + when there is one passed scenario + logs 1 passed scenarios + + when there are two or more passed scenarios + logs the number of passed scenarios + + logStepsSummary() + gets the number of steps + + when there are no steps + logs 0 steps + does not log any details + + when there are steps + + when there is one step + logs 1 step + + when there are two or more steps + logs the number of steps + gets the number of failed steps + + when there are no failed steps + does not log failed steps + + when there is one failed step + logs one failed step + + when there is two or more failed steps + logs the number of failed steps + gets the number of undefined steps + + when there are no undefined steps + does not log undefined steps + + when there is one undefined step + logs one undefined steps + + when there are two or more undefined steps + logs the number of undefined steps + gets the number of pending steps + + when there are no pending steps + does not log pending steps + + when there is one pending step + logs one pending steps + + when there are two or more pending steps + logs the number of pending steps + gets the number of skipped steps + + when there are no skipped steps + does not log skipped steps + + when there is one skipped step + logs one skipped steps + + when there are two or more skipped steps + logs the number of skipped steps + gets the number of passed steps + + when there are no passed steps + does not log passed steps + + when there is one passed step + logs one passed step + + when there is two or more passed steps + logs the number of passed steps + + logUndefinedStepSnippets() + logs a little explanation about the snippets + gets the undefined steps log buffer + logs the undefined steps + + getScenarioCount() + gets the number of passed scenarios + gets the number of undefined scenarios + gets the number of pending scenarios + gets the number of failed scenarios + returns the sum of passed, undefined, pending aand failed scenarios + + getStepCount() + gets the number of passed steps + gets the number of undefined steps + gets the number of skipped steps + gets the number of pending steps + gets the number of failed steps + returns the sum of passed steps and failed steps + + passed scenario counting + + witnessPassedScenario() + counts one more passed scenario + + getPassedScenarioCount() + returns 0 when no scenario passed + returns 1 when one scenario passed + returns 2 when two scenarios passed + returns 3 when three scenarios passed + + undefined scenario counting + + getUndefinedScenarioCount() + returns 0 when no scenarios undefined + returns 1 when one scenario passed + returns 2 when two scenarios passed + returns 3 when two scenarios passed + + pending scenario counting + + getPendingScenarioCount() + returns 0 when no scenarios pending + returns 1 when one scenario passed + returns 2 when two scenarios passed + returns 3 when two scenarios passed + + failed scenario counting + + getFailedScenarioCount() + returns 0 when no scenarios failed + returns 1 when one scenario passed + returns 2 when two scenarios passed + returns 3 when two scenarios passed + + passed step counting + + witnessPassedStep() + counts one more passed step + + getPassedStepCount() + returns 0 when no step passed + returns 1 when one step passed + returns 2 when two steps passed + returns 3 when three steps passed + + failed step counting + + getFailedStepCount() + returns 0 when no steps failed + returns 1 when one step passed + returns 2 when two steps passed + returns 3 when two steps passed + + skipped step counting + + getSkippedStepCount() + returns 0 when no steps skipped + returns 1 when one step passed + returns 2 when two steps passed + returns 3 when two steps passed + + undefined step counting + + getUndefinedStepCount() + returns 0 when no steps undefined + returns 1 when one step passed + returns 2 when two steps passed + returns 3 when two steps passed + + pending step counting + + getPendingStepCount() + returns 0 when no steps pending + returns 1 when one step passed + returns 2 when two steps passed + returns 3 when two steps passed + + witnessedAnyFailedStep() + returns false when no failed step were encountered + returns true when one or more steps were witnessed + + witnessedAnyUndefinedStep() + returns false when no undefined step were encountered + returns true when one or more steps were witnessed + +Finished in 0.095 seconds +338 tests, 339 assertions, 0 failures + + From fd940fa934ddda4a4535656c0dbe3501f8099d02 Mon Sep 17 00:00:00 2001 From: Chris Young Date: Fri, 25 May 2012 17:59:33 +0100 Subject: [PATCH 06/44] Updated the output of the tests in the json_formatter feature to match what you get from Ruby Cucumber --- features/json_formatter.feature | 34 +++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/features/json_formatter.feature b/features/json_formatter.feature index 8d68ad6bb..24ddae6bc 100644 --- a/features/json_formatter.feature +++ b/features/json_formatter.feature @@ -36,4 +36,38 @@ Feature: JSON Formatter ] } """ + Scenario: output JSON for a feature with one scenario with one pending step + Given a file named "features/a.feature" with: + """ + Feature: some feature + + Scenario: I've declaired one step but not yet defined it' + Given I have not defined this step + """ + When I run `cucumber.js -f json` + Then it should pass with this json: + """ +[{"elements": +[ + {"id":"some-feature;i've-declaired-one-step-but-not-yet-defined-it'", + "line":3, + "keyword":"Scenario", + "steps":[ + {"result":{"status":"undefined"}, + "line":4,"keyword": + "Given ","match":{"location":"simple.feature:4"}, + "name":"I have not defined this step"} + ], + "description":"", + "type":"scenario", + "name":"I've declaired one step but not yet defined it'" + } +], +"id":"some-feature", +"line":1, +"uri":"simple.feature", +"keyword":"Feature", +"description":"", +"name":"some feature"}] + """ From 58cd3469a06a9b915d9314e5fe745bde70f909ba Mon Sep 17 00:00:00 2001 From: Chris Young Date: Fri, 25 May 2012 18:02:25 +0100 Subject: [PATCH 07/44] =?UTF-8?q?Command=20line=20says=20there=20are=20no?= =?UTF-8?q?=20commits=20GUI=20says=20there=20are=E2=80=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- features/json_formatter.feature | 44 ++++++++++++++++----------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/features/json_formatter.feature b/features/json_formatter.feature index 24ddae6bc..d7476b9bc 100644 --- a/features/json_formatter.feature +++ b/features/json_formatter.feature @@ -47,27 +47,27 @@ Feature: JSON Formatter When I run `cucumber.js -f json` Then it should pass with this json: """ -[{"elements": -[ - {"id":"some-feature;i've-declaired-one-step-but-not-yet-defined-it'", - "line":3, - "keyword":"Scenario", - "steps":[ - {"result":{"status":"undefined"}, - "line":4,"keyword": - "Given ","match":{"location":"simple.feature:4"}, - "name":"I have not defined this step"} - ], - "description":"", - "type":"scenario", - "name":"I've declaired one step but not yet defined it'" - } -], -"id":"some-feature", -"line":1, -"uri":"simple.feature", -"keyword":"Feature", -"description":"", -"name":"some feature"}] + [{"elements": + [ + {"id":"some-feature;i've-declaired-one-step-but-not-yet-defined-it'", + "line":3, + "keyword":"Scenario", + "steps":[ + {"result":{"status":"undefined"}, + "line":4,"keyword": + "Given ","match":{"location":"simple.feature:4"}, + "name":"I have not defined this step"} + ], + "description":"", + "type":"scenario", + "name":"I've declaired one step but not yet defined it'" + } + ], + "id":"some-feature", + "line":1, + "uri":"simple.feature", + "keyword":"Feature", + "description":"", + "name":"some feature"}] """ From e613101d22bce6d8a3bd35764036322875ba7158 Mon Sep 17 00:00:00 2001 From: Chris Young Date: Sat, 26 May 2012 12:11:00 +0100 Subject: [PATCH 08/44] Now have two passing scenarios. Have had to deprecate the uri attribute as I can't figure out where to get it from and this is holding up getting on with all the remaining good stuff. --- features/json_formatter.feature | 70 +++++++++---------------- lib/cucumber/listener/json_formatter.js | 45 +++++++++++----- 2 files changed, 56 insertions(+), 59 deletions(-) diff --git a/features/json_formatter.feature b/features/json_formatter.feature index d7476b9bc..6a361ec7a 100644 --- a/features/json_formatter.feature +++ b/features/json_formatter.feature @@ -1,3 +1,5 @@ +# TODO: Reinstate "uri":"a.feature", + Feature: JSON Formatter In order to simplify processing of Cucumber features and results Developers should be able to consume features as JSON @@ -10,10 +12,14 @@ Feature: JSON Formatter When I run `cucumber.js -f json` Then it should pass with this json: """ -{ - "name": "some feature", - "id": "" -} +[ + {"id":"some-feature", + "name":"some feature", + "description":"", + "line":1, + "keyword":"Feature" + } +] """ Scenario: output JSON for a feature with one undefined scenario @@ -26,48 +32,22 @@ Feature: JSON Formatter When I run `cucumber.js -f json` Then it should pass with this json: """ -{ - "name": "some feature", - "id": "", - "elements": [ - { - "name": "I havn't done anything yet" - } - ] -} - """ - Scenario: output JSON for a feature with one scenario with one pending step - Given a file named "features/a.feature" with: - """ - Feature: some feature - - Scenario: I've declaired one step but not yet defined it' - Given I have not defined this step - """ - When I run `cucumber.js -f json` - Then it should pass with this json: - """ - [{"elements": - [ - {"id":"some-feature;i've-declaired-one-step-but-not-yet-defined-it'", +[ + { + "id":"some-feature", + "name":"some feature", + "description":"", + "line":1, + "keyword":"Feature", + "elements":[ + {"name":"I havn't done anything yet", + "id":"some-feature;i-havn't-done-anything-yet", "line":3, "keyword":"Scenario", - "steps":[ - {"result":{"status":"undefined"}, - "line":4,"keyword": - "Given ","match":{"location":"simple.feature:4"}, - "name":"I have not defined this step"} - ], "description":"", - "type":"scenario", - "name":"I've declaired one step but not yet defined it'" + "type":"scenario" } - ], - "id":"some-feature", - "line":1, - "uri":"simple.feature", - "keyword":"Feature", - "description":"", - "name":"some feature"}] - """ - + ] + } +] + """ \ No newline at end of file diff --git a/lib/cucumber/listener/json_formatter.js b/lib/cucumber/listener/json_formatter.js index 03b8b3a03..4b891dbb6 100644 --- a/lib/cucumber/listener/json_formatter.js +++ b/lib/cucumber/listener/json_formatter.js @@ -17,8 +17,10 @@ var JsonFormatter = function(options) { var currentScenarioPending = false; var failedStepResults = Cucumber.Type.Collection(); - var output = {"name": "", - "id": ""} + var currentFeatureId = ''; + + var output = []; + output[0] = {}; var self = { @@ -51,10 +53,16 @@ var JsonFormatter = function(options) { }, handleBeforeFeatureEvent: function handleBeforeFeatureEvent(event, callback) { - output["name"] = event.getPayloadItem('feature').getName(); - var description = event.getPayloadItem('feature').getDescription(); - if (description != "") - output["description"] = description + + var feature = event.getPayloadItem('feature'); + currentFeatureId = feature.getName().replace(' ','-'); + + output[0]["id"] = currentFeatureId + output[0]["name"] = feature.getName(); + output[0]["description"] = feature.getDescription(); + output[0]["line"] = feature.getLine(); + output[0]["keyword"] = feature.getKeyword(); + callback(); }, @@ -65,13 +73,22 @@ var JsonFormatter = function(options) { var scenario = event.getPayloadItem('scenario'); - var scenarioOutput = {"name": scenario.getName()}; + var id = currentFeatureId + ';' + scenario.getName().replace(/ /g, '-').toLowerCase(); + + var scenarioOutput = {}; + scenarioOutput["name"]= scenario.getName(); + scenarioOutput["id"]= id; + scenarioOutput["line"]= scenario.getLine(); + scenarioOutput["keyword"]= scenario.getKeyword(); + scenarioOutput["description"]= scenario.getDescription(); + scenarioOutput["type"]= "scenario"; + - if (!("elements" in output)) { - output["elements"] = []; + if (!("elements" in output[0])) { + output[0]["elements"] = []; } - output["elements"].push(scenarioOutput); + output[0]["elements"].push(scenarioOutput); callback(); }, @@ -79,12 +96,12 @@ var JsonFormatter = function(options) { handleStepResultEvent: function handleStepResult(event, callback) { var stepResult = event.getPayloadItem('stepResult'); - if (!("elements" in output)) { - output["elements"] = []; //TODO: Make Dry + if (!("elements" in output[0])) { + output[0]["elements"] = []; //TODO: Make Dry } - if (!([output["elements"].length-1]["steps"])) { - [output["elements"].length-1]["steps"] = []; + if (!([output[0]["elements"].length-1]["steps"])) { + [output[0]["elements"].length-1]["steps"] = []; } if (stepResult.isSuccessful()) From a4a6b7553c9ed45e7d74315eef7e04b456133245 Mon Sep 17 00:00:00 2001 From: Chris Young Date: Sun, 27 May 2012 16:47:15 +0100 Subject: [PATCH 09/44] Now have three scenarios passing albeit with steps:match:location not yet implemented --- features/json_formatter.feature | 46 ++++++++++++++++++++++++- features/step_definitions/cli_steps.js | 17 +++++++-- lib/cucumber/listener/json_formatter.js | 25 ++++++++++++-- 3 files changed, 81 insertions(+), 7 deletions(-) diff --git a/features/json_formatter.feature b/features/json_formatter.feature index 6a361ec7a..51e34ffba 100644 --- a/features/json_formatter.feature +++ b/features/json_formatter.feature @@ -50,4 +50,48 @@ Feature: JSON Formatter ] } ] - """ \ No newline at end of file + """ + + Scenario: output JSON for a feature with one scenario with one pending step + Given a file named "features/a.feature" with: + """ + Feature: some feature + + Scenario: I've declaired one step but not yet defined it + Given I have not defined this step + """ + When I run `cucumber.js -f json` + Then it should pass with this json: + """ +[ + { + "id": "some-feature", + "name": "some feature", + "description": "", + "line": 1, + "keyword": "Feature", + "elements": [ + { + "name": "I've declaired one step but not yet defined it", + "id": "some-feature;i've-declaired-one-step-but-not-yet-defined-it", + "line": 3, + "keyword": "Scenario", + "description": "", + "type": "scenario", + "steps":[ + {"name":"I have not defined this step", + "line":4, + "keyword":"Given ", + "result": + {"status":"undefined" + }, + "match": + {"location":"TODO" + } + } + ] + } + ] + } +] + """ diff --git a/features/step_definitions/cli_steps.js b/features/step_definitions/cli_steps.js index 9a8d7690f..7c6b01b32 100644 --- a/features/step_definitions/cli_steps.js +++ b/features/step_definitions/cli_steps.js @@ -81,13 +81,24 @@ var cliSteps = function cliSteps() { var actualError = lastRun['error']; var actualStderr = lastRun['stderr']; - var actualJson = JSON.parse(actualOutput); - var expectedJson = JSON.parse(expectedOutput); + try { + var actualJson = JSON.parse(actualOutput); + } + catch(err) { + throw new Error("Error parsing actual JSON:\n" + actualOutput); + } + + try { + var expectedJson = JSON.parse(expectedOutput); + } + catch(err) { + throw new Error("Error parsing expected JSON:\n" + expectedOutput); + } var actualJsonString = JSON.stringify(actualJson, null, 2); var expectedJsonString = JSON.stringify(expectedJson, null, 2); - if (actualJsonString.indexOf(expectedJsonString) == -1) + if (actualJsonString != expectedJsonString) throw new Error("Expected output to match the following:\n'" + expectedJsonString + "'\nGot:\n'" + actualJsonString + "'.\n" + "Error:\n'" + actualError + "'.\n" + "stderr:\n'" + actualStderr +"'."); diff --git a/lib/cucumber/listener/json_formatter.js b/lib/cucumber/listener/json_formatter.js index 4b891dbb6..083a69c3c 100644 --- a/lib/cucumber/listener/json_formatter.js +++ b/lib/cucumber/listener/json_formatter.js @@ -100,20 +100,39 @@ var JsonFormatter = function(options) { output[0]["elements"] = []; //TODO: Make Dry } - if (!([output[0]["elements"].length-1]["steps"])) { - [output[0]["elements"].length-1]["steps"] = []; + var element = output[0]["elements"][output[0]["elements"].length-1] + if (!element["steps"]) { + element["steps"] = []; } + var step = stepResult.getStep(); + + var stepOutput = {}; + stepOutput["name"] = step.getName(); + stepOutput["line"] = step.getLine(); + stepOutput["keyword"] = step.getKeyword(); + + var resultStatus = "failed"; + if (stepResult.isSuccessful()) self.handleSuccessfulStepResult(); else if (stepResult.isPending()) self.handlePendingStepResult(); else if (stepResult.isSkipped()) self.handleSkippedStepResult(); - else if (stepResult.isUndefined()) + else if (stepResult.isUndefined()) { + resultStatus = "undefined"; self.handleUndefinedStepResult(stepResult); + } else self.handleFailedStepResult(stepResult); + + stepOutput["result"] = {status:resultStatus} + + stepOutput["match"] = {location:"TODO"} + + element["steps"].push(stepOutput); + callback(); }, From deb9e6b52a1e15b22a49e78e17379027a4c57f9f Mon Sep 17 00:00:00 2001 From: Chris Young Date: Mon, 28 May 2012 09:22:56 +0100 Subject: [PATCH 10/44] Four passing scenrios. Need to add error_message to EVENT though --- features/json_formatter.feature | 56 ++++++++++++++++++++++++- lib/cucumber/listener/json_formatter.js | 9 +++- 2 files changed, 62 insertions(+), 3 deletions(-) diff --git a/features/json_formatter.feature b/features/json_formatter.feature index 51e34ffba..054d7c514 100644 --- a/features/json_formatter.feature +++ b/features/json_formatter.feature @@ -52,7 +52,7 @@ Feature: JSON Formatter ] """ - Scenario: output JSON for a feature with one scenario with one pending step + Scenario: output JSON for a feature with one scenario with one undefined step Given a file named "features/a.feature" with: """ Feature: some feature @@ -95,3 +95,57 @@ Feature: JSON Formatter } ] """ + + Scenario: output JSON for a feature with one scenario with one pending step + Given a file named "features/a.feature" with: + """ + Feature: some feature + + Scenario: I've declaired one step but not yet defined it + Given This step is pending + """ + And a file named "features/step_definitions/cucumber_steps.js" with: + """ + var cucumberSteps = function() { + this.Given(/^This step is pending$/, function(callback) { callback.pending(); }); + }; + module.exports = cucumberSteps; + """ + + When I run `cucumber.js -f json` + Then it should pass with this json: + """ + [ + { + "id": "some-feature", + "name": "some feature", + "description": "", + "line": 1, + "keyword": "Feature", + "elements": [ + { + "name": "I've declaired one step but not yet defined it", + "id": "some-feature;i've-declaired-one-step-but-not-yet-defined-it", + "line": 3, + "keyword": "Scenario", + "description": "", + "type": "scenario", + "steps": [ + { + "name": "This step is pending", + "line": 4, + "keyword": "Given ", + "result": { + "error_message": "TODO", + "status": "pending" + }, + "match": { + "location": "TODO" + } + } + ] + } + ] + } + ] + """ diff --git a/lib/cucumber/listener/json_formatter.js b/lib/cucumber/listener/json_formatter.js index 083a69c3c..b50c94d6a 100644 --- a/lib/cucumber/listener/json_formatter.js +++ b/lib/cucumber/listener/json_formatter.js @@ -112,12 +112,17 @@ var JsonFormatter = function(options) { stepOutput["line"] = step.getLine(); stepOutput["keyword"] = step.getKeyword(); + stepOutput["result"] = {} + var resultStatus = "failed"; if (stepResult.isSuccessful()) self.handleSuccessfulStepResult(); - else if (stepResult.isPending()) + else if (stepResult.isPending()) { + resultStatus = "pending"; + stepOutput["result"]["error_message"] = 'TODO'; self.handlePendingStepResult(); + } else if (stepResult.isSkipped()) self.handleSkippedStepResult(); else if (stepResult.isUndefined()) { @@ -127,7 +132,7 @@ var JsonFormatter = function(options) { else self.handleFailedStepResult(stepResult); - stepOutput["result"] = {status:resultStatus} + stepOutput["result"]["status"] = resultStatus stepOutput["match"] = {location:"TODO"} From f91bea1c09f6d324b16b3a139ea80a5afa4ef87a Mon Sep 17 00:00:00 2001 From: Chris Young Date: Sat, 16 Jun 2012 07:44:30 +0100 Subject: [PATCH 11/44] Added Scenarios for failing and passing steps --- features/json_formatter.feature | 109 +++++++++++++++++++++++- lib/cucumber/listener/json_formatter.js | 6 +- 2 files changed, 110 insertions(+), 5 deletions(-) diff --git a/features/json_formatter.feature b/features/json_formatter.feature index 054d7c514..d3f06c459 100644 --- a/features/json_formatter.feature +++ b/features/json_formatter.feature @@ -101,7 +101,7 @@ Feature: JSON Formatter """ Feature: some feature - Scenario: I've declaired one step but not yet defined it + Scenario: I've declaired one step which is pending Given This step is pending """ And a file named "features/step_definitions/cucumber_steps.js" with: @@ -124,8 +124,8 @@ Feature: JSON Formatter "keyword": "Feature", "elements": [ { - "name": "I've declaired one step but not yet defined it", - "id": "some-feature;i've-declaired-one-step-but-not-yet-defined-it", + "name": "I've declaired one step which is pending", + "id": "some-feature;i've-declaired-one-step-which-is-pending", "line": 3, "keyword": "Scenario", "description": "", @@ -149,3 +149,106 @@ Feature: JSON Formatter } ] """ + Scenario: output JSON for a feature with one scenario with failing step + Given a file named "features/a.feature" with: + """ + Feature: some feature + + Scenario: I've declaired one step but it is failing + Given This step is failing + """ + And a file named "features/step_definitions/cucumber_steps.js" with: + """ + var cucumberSteps = function() { + this.Given(/^This step is failing$/, function(callback) { callback.fail(); }); + }; + module.exports = cucumberSteps; + """ + + When I run `cucumber.js -f json` + Then it should pass with this json: + """ + [ + { + "id": "some-feature", + "name": "some feature", + "description": "", + "line": 1, + "keyword": "Feature", + "elements": [ + { + "name": "I've declaired one step but it is failing", + "id": "some-feature;i've-declaired-one-step-but-it-is-failing", + "line": 3, + "keyword": "Scenario", + "description": "", + "type": "scenario", + "steps": [ + { + "name": "This step is failing", + "line": 4, + "keyword": "Given ", + "result": { + "status": "failed" + }, + "match": { + "location": "TODO" + } + } + ] + } + ] + } + ] + """ + Scenario: output JSON for a feature with one scenario with passing step + Given a file named "features/a.feature" with: + """ + Feature: some feature + + Scenario: I've declaired one step which passes + Given This step is passing + """ + And a file named "features/step_definitions/cucumber_steps.js" with: + """ + var cucumberSteps = function() { + this.Given(/^This step is passing$/, function(callback) { callback(); }); + }; + module.exports = cucumberSteps; + """ + When I run `cucumber.js -f json` + Then it should pass with this json: + """ + [ + { + "id": "some-feature", + "name": "some feature", + "description": "", + "line": 1, + "keyword": "Feature", + "elements": [ + { + "name": "I've declaired one step which passes", + "id": "some-feature;i've-declaired-one-step-which-passes", + "line": 3, + "keyword": "Scenario", + "description": "", + "type": "scenario", + "steps": [ + { + "name": "This step is passing", + "line": 4, + "keyword": "Given ", + "result": { + "status": "passed" + }, + "match": { + "location": "TODO" + } + } + ] + } + ] + } + ] + """ \ No newline at end of file diff --git a/lib/cucumber/listener/json_formatter.js b/lib/cucumber/listener/json_formatter.js index b50c94d6a..dbe85ecbc 100644 --- a/lib/cucumber/listener/json_formatter.js +++ b/lib/cucumber/listener/json_formatter.js @@ -116,8 +116,10 @@ var JsonFormatter = function(options) { var resultStatus = "failed"; - if (stepResult.isSuccessful()) - self.handleSuccessfulStepResult(); + if (stepResult.isSuccessful()) { + resultStatus = "passed"; + self.handleSuccessfulStepResult(); + } else if (stepResult.isPending()) { resultStatus = "pending"; stepOutput["result"]["error_message"] = 'TODO'; From 51c52af7d4fe14e0444444600432e9ac07d8dc87 Mon Sep 17 00:00:00 2001 From: Chris Young Date: Thu, 21 Jun 2012 15:19:41 +0100 Subject: [PATCH 12/44] Added more scenarios to the tests. Removed redundant methods copied from progress listener --- features/json_formatter.feature | 268 +++++- features/step_definitions/cli_steps.js | 2 +- lib/cucumber/listener/json_formatter.js | 255 +----- spec/cucumber/listener/json_formatter_spec.js | 797 +----------------- 4 files changed, 275 insertions(+), 1047 deletions(-) diff --git a/features/json_formatter.feature b/features/json_formatter.feature index d3f06c459..347a94771 100644 --- a/features/json_formatter.feature +++ b/features/json_formatter.feature @@ -1,5 +1,3 @@ -# TODO: Reinstate "uri":"a.feature", - Feature: JSON Formatter In order to simplify processing of Cucumber features and results Developers should be able to consume features as JSON @@ -10,14 +8,15 @@ Feature: JSON Formatter Feature: some feature """ When I run `cucumber.js -f json` - Then it should pass with this json: + Then it should output this json: """ [ {"id":"some-feature", "name":"some feature", "description":"", "line":1, - "keyword":"Feature" + "keyword":"Feature", + "uri":"TODO" } ] """ @@ -30,7 +29,7 @@ Feature: JSON Formatter Scenario: I havn't done anything yet """ When I run `cucumber.js -f json` - Then it should pass with this json: + Then it should output this json: """ [ { @@ -39,6 +38,7 @@ Feature: JSON Formatter "description":"", "line":1, "keyword":"Feature", + "uri":"TODO", "elements":[ {"name":"I havn't done anything yet", "id":"some-feature;i-havn't-done-anything-yet", @@ -61,7 +61,7 @@ Feature: JSON Formatter Given I have not defined this step """ When I run `cucumber.js -f json` - Then it should pass with this json: + Then it should output this json: """ [ { @@ -70,6 +70,7 @@ Feature: JSON Formatter "description": "", "line": 1, "keyword": "Feature", + "uri":"TODO", "elements": [ { "name": "I've declaired one step but not yet defined it", @@ -113,7 +114,7 @@ Feature: JSON Formatter """ When I run `cucumber.js -f json` - Then it should pass with this json: + Then it should output this json: """ [ { @@ -122,6 +123,7 @@ Feature: JSON Formatter "description": "", "line": 1, "keyword": "Feature", + "uri":"TODO", "elements": [ { "name": "I've declaired one step which is pending", @@ -166,7 +168,7 @@ Feature: JSON Formatter """ When I run `cucumber.js -f json` - Then it should pass with this json: + Then it should output this json: """ [ { @@ -175,6 +177,7 @@ Feature: JSON Formatter "description": "", "line": 1, "keyword": "Feature", + "uri":"TODO", "elements": [ { "name": "I've declaired one step but it is failing", @@ -217,7 +220,7 @@ Feature: JSON Formatter module.exports = cucumberSteps; """ When I run `cucumber.js -f json` - Then it should pass with this json: + Then it should output this json: """ [ { @@ -226,6 +229,7 @@ Feature: JSON Formatter "description": "", "line": 1, "keyword": "Feature", + "uri":"TODO", "elements": [ { "name": "I've declaired one step which passes", @@ -251,4 +255,248 @@ Feature: JSON Formatter ] } ] - """ \ No newline at end of file + """ + + Scenario: output JSON for a scenario with a passing step follwed by one that is pending and one that fails + Given a file named "features/a.feature" with: + """ + Feature: some feature + + Scenario: I've declaired one step which is passing, one pending and one failing. + Given This step is passing + And This step is pending + And This step fails but will be skipped + """ + And a file named "features/step_definitions/cucumber_steps.js" with: + """ + var cucumberSteps = function() { + this.Given(/^This step is passing$/, function(callback) { callback(); }); + this.Given(/^This step is pending$/, function(callback) { callback.pending(); }); + this.Given(/^This step fails but will be skipped$/, function(callback) { callback.fail(); }); + }; + module.exports = cucumberSteps; + """ + When I run `cucumber.js -f json` + Then it should output this json: + """ + [ + { + "id": "some-feature", + "name": "some feature", + "description": "", + "line": 1, + "keyword": "Feature", + "uri": "TODO", + "elements": [ + { + "name": "I've declaired one step which is passing, one pending and one failing.", + "id": "some-feature;i've-declaired-one-step-which-is-passing,-one-pending-and-one-failing.", + "line": 3, + "keyword": "Scenario", + "description": "", + "type": "scenario", + "steps": [ + { + "name": "This step is passing", + "line": 4, + "keyword": "Given ", + "result": { + "status": "passed" + }, + "match": { + "location": "TODO" + } + }, + { + "name": "This step is pending", + "line": 5, + "keyword": "And ", + "result": { + "error_message": "TODO", + "status": "pending" + }, + "match": { + "location": "TODO" + } + }, + { + "name": "This step fails but will be skipped", + "line": 6, + "keyword": "And ", + "result": { + "status": "skipped" + }, + "match": { + "location": "TODO" + } + } + ] + } + ] + } + ] + """ + + Scenario: output JSON for a scenario with a pending step follwed by one that passes and one that fails + Given a file named "features/a.feature" with: + """ + Feature: some feature + + Scenario: I've declaired one step which is passing, one pending and one failing. + Given This step is pending + And This step is passing but will be skipped + And This step fails but will be skipped + """ + And a file named "features/step_definitions/cucumber_steps.js" with: + """ + var cucumberSteps = function() { + this.Given(/^This step is pending$/, function(callback) { callback.pending(); }); + this.Given(/^This step is passing but will be skipped$/, function(callback) { callback(); }); + this.Given(/^This step fails but will be skipped$/, function(callback) { callback.fail(); }); + }; + module.exports = cucumberSteps; + """ + When I run `cucumber.js -f json` + Then it should output this json: + """ +[ + { + "id": "some-feature", + "name": "some feature", + "description": "", + "line": 1, + "keyword": "Feature", + "uri": "TODO", + "elements": [ + { + "name": "I've declaired one step which is passing, one pending and one failing.", + "id": "some-feature;i've-declaired-one-step-which-is-passing,-one-pending-and-one-failing.", + "line": 3, + "keyword": "Scenario", + "description": "", + "type": "scenario", + "steps": [ + { + "name": "This step is pending", + "line": 4, + "keyword": "Given ", + "result": { + "error_message": "TODO", + "status": "pending" + }, + "match": { + "location": "TODO" + } + }, + { + "name": "This step is passing but will be skipped", + "line": 5, + "keyword": "And ", + "result": { + "status": "skipped" + }, + "match": { + "location": "TODO" + } + }, + { + "name": "This step fails but will be skipped", + "line": 6, + "keyword": "And ", + "result": { + "status": "skipped" + }, + "match": { + "location": "TODO" + } + } + ] + } + ] + } +] + """ + +Scenario: one feature, one passing scenario, one failing scenario + Given a file named "features/a.feature" with: + """ + Feature: one passes one fails + + Scenario: This one passes + Given This step is passing + Scenario: This one fails + Given This step is failing + """ + And a file named "features/step_definitions/cucumber_steps.js" with: + """ + var cucumberSteps = function() { + this.Given(/^This step is passing$/, function(callback) { callback(); }); + this.Given(/^This step is failing$/, function(callback) { callback.fail(); }); + }; + module.exports = cucumberSteps; + """ + When I run `cucumber.js -f json` + Then it should output this json: + """ +[ + { + "id": "one-passes one fails", + "name": "one passes one fails", + "description": "", + "line": 1, + "keyword": "Feature", + "uri": "TODO", + "elements": [ + { + "name": "This one passes", + "id": "one-passes one fails;this-one-passes", + "line": 3, + "keyword": "Scenario", + "description": "", + "type": "scenario", + "steps": [ + { + "name": "This step is passing", + "line": 4, + "keyword": "Given ", + "result": { + "status": "passed" + }, + "match": { + "location": "TODO" + } + } + ] + }, + { + "name": "This one fails", + "id": "one-passes one fails;this-one-fails", + "line": 5, + "keyword": "Scenario", + "description": "", + "type": "scenario", + "steps": [ + { + "name": "This step is failing", + "line": 6, + "keyword": "Given ", + "result": { + "status": "failed" + }, + "match": { + "location": "TODO" + } + } + ] + } + ] + } +] + """ + + # Embedings? + + # DocString? + + # 'it should pass with... is a bit misleading' + diff --git a/features/step_definitions/cli_steps.js b/features/step_definitions/cli_steps.js index 7c6b01b32..ec1a4ddcd 100644 --- a/features/step_definitions/cli_steps.js +++ b/features/step_definitions/cli_steps.js @@ -75,7 +75,7 @@ var cliSteps = function cliSteps() { callback(); }); - this.Then(/^it should pass with this json:$/, function(expectedOutput, callback) { + this.Then(/^it should output this json:$/, function(expectedOutput, callback) { var actualOutput = lastRun['stdout']; var actualError = lastRun['error']; diff --git a/lib/cucumber/listener/json_formatter.js b/lib/cucumber/listener/json_formatter.js index dbe85ecbc..f8cda80dc 100644 --- a/lib/cucumber/listener/json_formatter.js +++ b/lib/cucumber/listener/json_formatter.js @@ -1,27 +1,10 @@ var JsonFormatter = function(options) { var Cucumber = require('../../cucumber'); - var failedScenarioLogBuffer = ""; - var undefinedStepLogBuffer = ""; - var passedScenarioCount = 0; - var undefinedScenarioCount = 0; - var pendingScenarioCount = 0; - var failedScenarioCount = 0; - var passedStepCount = 0; - var failedStepCount = 0; - var skippedStepCount = 0; - var undefinedStepCount = 0; - var pendingStepCount = 0; - var currentScenarioFailing = false; - var currentScenarioUndefined = false; - var currentScenarioPending = false; - var failedStepResults = Cucumber.Type.Collection(); - var currentFeatureId = ''; var output = []; - output[0] = {}; - + var self = { hear: function hear(event, callback) { @@ -57,11 +40,14 @@ var JsonFormatter = function(options) { var feature = event.getPayloadItem('feature'); currentFeatureId = feature.getName().replace(' ','-'); - output[0]["id"] = currentFeatureId - output[0]["name"] = feature.getName(); - output[0]["description"] = feature.getDescription(); - output[0]["line"] = feature.getLine(); - output[0]["keyword"] = feature.getKeyword(); + var element = { + id: currentFeatureId, + name: feature.getName(), + description: feature.getDescription(), + line: feature.getLine(), + keyword: feature.getKeyword(), + uri: 'TODO'} + output.push(element); callback(); }, @@ -69,8 +55,6 @@ var JsonFormatter = function(options) { handleBeforeScenarioEvent: function handleBeforeScenarioEvent(event, callback) { - self.prepareBeforeScenario(); // Can probably get rid of this - var scenario = event.getPayloadItem('scenario'); var id = currentFeatureId + ';' + scenario.getName().replace(/ /g, '-').toLowerCase(); @@ -83,7 +67,6 @@ var JsonFormatter = function(options) { scenarioOutput["description"]= scenario.getDescription(); scenarioOutput["type"]= "scenario"; - if (!("elements" in output[0])) { output[0]["elements"] = []; } @@ -118,246 +101,34 @@ var JsonFormatter = function(options) { if (stepResult.isSuccessful()) { resultStatus = "passed"; - self.handleSuccessfulStepResult(); } else if (stepResult.isPending()) { resultStatus = "pending"; stepOutput["result"]["error_message"] = 'TODO'; - self.handlePendingStepResult(); } - else if (stepResult.isSkipped()) - self.handleSkippedStepResult(); + else if (stepResult.isSkipped()) { + resultStatus = "skipped"; + } else if (stepResult.isUndefined()) { resultStatus = "undefined"; - self.handleUndefinedStepResult(stepResult); - } - else - self.handleFailedStepResult(stepResult); + } stepOutput["result"]["status"] = resultStatus - stepOutput["match"] = {location:"TODO"} - element["steps"].push(stepOutput); callback(); }, - handleSuccessfulStepResult: function handleSuccessfulStepResult() { - self.witnessPassedStep(); - }, - - handlePendingStepResult: function handlePendingStepResult() { - self.witnessPendingStep(); - self.markCurrentScenarioAsPending(); - }, - - handleSkippedStepResult: function handleSkippedStepResult() { - self.witnessSkippedStep(); - }, - - handleUndefinedStepResult: function handleUndefinedStepResult(stepResult) { - var step = stepResult.getStep(); - self.storeUndefinedStep(step); - self.witnessUndefinedStep(); - self.markCurrentScenarioAsUndefined(); - }, - - handleFailedStepResult: function handleFailedStepResult(stepResult) { - self.storeFailedStepResult(stepResult); - self.witnessFailedStep(); - self.markCurrentScenarioAsFailing(); - }, - handleAfterFeaturesEvent: function handleAfterFeaturesEvent(event, callback) { process.stdout.write(JSON.stringify(output, null, 2)); callback(); }, handleAfterScenarioEvent: function handleAfterScenarioEvent(event, callback) { - if (self.isCurrentScenarioFailing()) { - var scenario = event.getPayloadItem('scenario'); - self.storeFailedScenario(scenario); - self.witnessFailedScenario(); - } else if (self.isCurrentScenarioUndefined()) { - self.witnessUndefinedScenario(); - } else if (self.isCurrentScenarioPending()) { - self.witnessPendingScenario(); - } else { - self.witnessPassedScenario(); - } callback(); }, - prepareBeforeScenario: function prepareBeforeScenario() { - currentScenarioFailing = false; - currentScenarioPending = false; - currentScenarioUndefined = false; - }, - - markCurrentScenarioAsFailing: function markCurrentScenarioAsFailing() { - currentScenarioFailing = true; - }, - - markCurrentScenarioAsUndefined: function markCurrentScenarioAsUndefined() { - currentScenarioUndefined = true; - }, - - markCurrentScenarioAsPending: function markCurrentScenarioAsPending() { - currentScenarioPending = true; - }, - - isCurrentScenarioFailing: function isCurrentScenarioFailing() { - return currentScenarioFailing; - }, - - isCurrentScenarioUndefined: function isCurrentScenarioUndefined() { - return currentScenarioUndefined; - }, - - isCurrentScenarioPending: function isCurrentScenarioPending() { - return currentScenarioPending; - }, - - storeFailedStepResult: function storeFailedStepResult(failedStepResult) { - failedStepResults.add(failedStepResult); - }, - - storeFailedScenario: function storeFailedScenario(failedScenario) { - var name = failedScenario.getName(); - var line = failedScenario.getLine(); - self.appendStringToFailedScenarioLogBuffer(":" + line + " # Scenario: " + name); - }, - - storeUndefinedStep: function storeUndefinedStep(step) { - var snippetBuilder = Cucumber.SupportCode.StepDefinitionSnippetBuilder(step); - var snippet = snippetBuilder.buildSnippet(); - self.appendStringToUndefinedStepLogBuffer(snippet); - }, - - appendStringToFailedScenarioLogBuffer: function appendStringToFailedScenarioLogBuffer(string) { - failedScenarioLogBuffer += string + "\n"; - }, - - appendStringToUndefinedStepLogBuffer: function appendStringToUndefinedStepLogBuffer(string) { - if (undefinedStepLogBuffer.indexOf(string) == -1) - undefinedStepLogBuffer += string + "\n"; - }, - - getFailedScenarioLogBuffer: function getFailedScenarioLogBuffer() { - return failedScenarioLogBuffer; - }, - - getUndefinedStepLogBuffer: function getUndefinedStepLogBuffer() { - return undefinedStepLogBuffer; - }, - -/* - logUndefinedStepSnippets: function logUndefinedStepSnippets() { - var undefinedStepLogBuffer = self.getUndefinedStepLogBuffer(); - self.log("\nYou can implement step definitions for undefined steps with these snippets:\n\n"); - self.log(undefinedStepLogBuffer); - }, -*/ - - witnessPassedScenario: function witnessPassedScenario() { - passedScenarioCount++; - }, - - witnessUndefinedScenario: function witnessUndefinedScenario() { - undefinedScenarioCount++; - }, - - witnessPendingScenario: function witnessPendingScenario() { - pendingScenarioCount++; - }, - - witnessFailedScenario: function witnessFailedScenario() { - failedScenarioCount++; - }, - - witnessPassedStep: function witnessPassedStep() { - passedStepCount++; - }, - - witnessUndefinedStep: function witnessUndefinedStep() { - undefinedStepCount++; - }, - - witnessPendingStep: function witnessPendingStep() { - pendingStepCount++; - }, - - witnessFailedStep: function witnessFailedStep() { - failedStepCount++; - }, - - witnessSkippedStep: function witnessSkippedStep() { - skippedStepCount++; - }, - - getScenarioCount: function getScenarioCount() { - var scenarioCount = - self.getPassedScenarioCount() + - self.getUndefinedScenarioCount() + - self.getPendingScenarioCount() + - self.getFailedScenarioCount(); - return scenarioCount; - }, - - getPassedScenarioCount: function getPassedScenarioCount() { - return passedScenarioCount; - }, - - getUndefinedScenarioCount: function getUndefinedScenarioCount() { - return undefinedScenarioCount; - }, - - getPendingScenarioCount: function getPendingScenarioCount() { - return pendingScenarioCount; - }, - - getFailedScenarioCount: function getFailedScenarioCount() { - return failedScenarioCount; - }, - - getStepCount: function getStepCount() { - var stepCount = - self.getPassedStepCount() + - self.getUndefinedStepCount() + - self.getSkippedStepCount() + - self.getPendingStepCount() + - self.getFailedStepCount(); - return stepCount; - }, - - getPassedStepCount: function getPassedStepCount() { - return passedStepCount; - }, - - getPendingStepCount: function getPendingStepCount() { - return pendingStepCount; - }, - - getFailedStepCount: function getFailedStepCount() { - return failedStepCount; - }, - - getSkippedStepCount: function getSkippedStepCount() { - return skippedStepCount; - }, - - getUndefinedStepCount: function getUndefinedStepCount() { - return undefinedStepCount; - }, - - witnessedAnyFailedStep: function witnessedAnyFailedStep() { - return failedStepCount > 0; - }, - - witnessedAnyUndefinedStep: function witnessedAnyUndefinedStep() { - return undefinedStepCount > 0; - } }; return self; }; diff --git a/spec/cucumber/listener/json_formatter_spec.js b/spec/cucumber/listener/json_formatter_spec.js index cfa395512..4d5ca9337 100644 --- a/spec/cucumber/listener/json_formatter_spec.js +++ b/spec/cucumber/listener/json_formatter_spec.js @@ -5,15 +5,10 @@ describe("Cucumber.Listener.JsonFormatter", function() { var listener, failedStepResults; beforeEach(function() { - failedStepResults = createSpy("Failed steps"); - spyOn(Cucumber.Type, 'Collection').andReturn(failedStepResults); listener = Cucumber.Listener.JsonFormatter(); }); describe("constructor", function() { - it("creates a collection to store the failed steps", function() { - expect(Cucumber.Type.Collection).toHaveBeenCalled(); - }); }); describe("hear()", function() { @@ -159,138 +154,81 @@ describe("Cucumber.Listener.JsonFormatter", function() { var event, callback, stepResult; beforeEach(function() { - stepResult = createSpyWithStubs("step result", { - isSuccessful: undefined, - isPending: undefined, - isFailed: undefined, - isSkipped: undefined, - isUndefined: undefined - }); event = createSpyWithStubs("event", {getPayloadItem: stepResult}); callback = createSpy("Callback"); - spyOn(listener, 'handleFailedStepResult'); }); it("gets the step result from the event payload", function() { - listener.handleStepResultEvent(event, callback); - expect(event.getPayloadItem).toHaveBeenCalledWith('stepResult'); }); it("checks wether the step was successful or not", function() { - listener.handleStepResultEvent(event, callback); - expect(stepResult.isSuccessful).toHaveBeenCalled(); }); describe("when the step passed", function() { - beforeEach(function() { - stepResult.isSuccessful.andReturn(true); - spyOn(listener, 'handleSuccessfulStepResult'); - }); - - it("handles the successful step result", function() { - listener.handleStepResultEvent(event, callback); - expect(listener.handleSuccessfulStepResult).toHaveBeenCalled(); - }); }); describe("when the step did not pass", function() { beforeEach(function() { - stepResult.isSuccessful.andReturn(false); - spyOn(listener, 'handleSuccessfulStepResult'); }); it("does not handle a successful step result", function() { - listener.handleStepResultEvent(event, callback); - expect(listener.handleSuccessfulStepResult).not.toHaveBeenCalled(); }); it("checks wether the step is pending", function() { - listener.handleStepResultEvent(event, callback); - expect(stepResult.isPending).toHaveBeenCalled(); }); describe("when the step was pending", function() { beforeEach(function() { - stepResult.isPending.andReturn(true); - spyOn(listener, 'handlePendingStepResult'); }); it("handles the pending step result", function() { - listener.handleStepResultEvent(event, callback); - expect(listener.handlePendingStepResult).toHaveBeenCalled(); }); }); describe("when the step was not pending", function() { beforeEach(function() { - stepResult.isPending.andReturn(false); - spyOn(listener, 'handlePendingStepResult'); }); it("does not handle a pending step result", function() { - listener.handleStepResultEvent(event, callback); - expect(listener.handlePendingStepResult).not.toHaveBeenCalled(); }); it("checks wether the step was skipped", function() { - listener.handleStepResultEvent(event, callback); - expect(stepResult.isSkipped).toHaveBeenCalled(); }); describe("when the step was skipped", function() { beforeEach(function() { - stepResult.isSkipped.andReturn(true); - spyOn(listener, 'handleSkippedStepResult'); }); it("handles the skipped step result", function() { - listener.handleStepResultEvent(event, callback); - expect(listener.handleSkippedStepResult).toHaveBeenCalled(); }); }); describe("when the step was not skipped", function() { beforeEach(function() { - stepResult.isSkipped.andReturn(false); - spyOn(listener, 'handleSkippedStepResult'); }); it("does not handle a skipped step result", function() { - listener.handleStepResultEvent(event, callback); - expect(listener.handleSkippedStepResult).not.toHaveBeenCalled(); }); it("checks wether the step was undefined", function() { - listener.handleStepResultEvent(event, callback); - expect(stepResult.isUndefined).toHaveBeenCalled(); }); describe("when the step was undefined", function() { beforeEach(function() { - stepResult.isUndefined.andReturn(true); - spyOn(listener, 'handleUndefinedStepResult'); }); it("handles the undefined step result", function() { - listener.handleStepResultEvent(event, callback); - expect(listener.handleUndefinedStepResult).toHaveBeenCalledWith(stepResult); }); }); describe("when the step was not undefined", function() { beforeEach(function() { - stepResult.isUndefined.andReturn(false); - spyOn(listener, 'handleUndefinedStepResult'); }); it("does not handle a skipped step result", function() { - listener.handleStepResultEvent(event, callback); - expect(listener.handleSkippedStepResult).not.toHaveBeenCalled(); }); it("handles a failed step result", function() { - listener.handleStepResultEvent(event, callback); - expect(listener.handleFailedStepResult).toHaveBeenCalledWith(stepResult); }); }); }); @@ -298,111 +236,7 @@ describe("Cucumber.Listener.JsonFormatter", function() { }); it("calls back", function() { - listener.handleStepResultEvent(event, callback); - expect(callback).toHaveBeenCalled(); - }); - }); - - describe("handleSuccessfulStepResult()", function() { - beforeEach(function() { - spyOn(listener, 'witnessPassedStep'); - }); - - it("witnesses a passed step", function() { - listener.handleSuccessfulStepResult(); - expect(listener.witnessPassedStep).toHaveBeenCalled(); - }); - - }); - - describe("handlePendingStepResult()", function() { - beforeEach(function() { - spyOn(listener, 'witnessPendingStep'); - spyOn(listener, 'markCurrentScenarioAsPending'); - }); - - it("witnesses a pending step", function() { - listener.handlePendingStepResult(); - expect(listener.witnessPendingStep).toHaveBeenCalled(); }); - - it("marks the current scenario as pending", function() { - listener.handlePendingStepResult(); - expect(listener.markCurrentScenarioAsPending).toHaveBeenCalled(); - }); - - }); - - describe("handleSkippedStepResult()", function() { - beforeEach(function() { - spyOn(listener, 'witnessSkippedStep'); - }); - - it("counts one more skipped step", function() { - listener.handleSkippedStepResult(); - expect(listener.witnessSkippedStep).toHaveBeenCalled(); - }); - - }); - - describe("handleUndefinedStepResult()", function() { - var stepResult, step; - - beforeEach(function() { - step = createSpy("step"); - stepResult = createSpyWithStubs("step result", {getStep: step}); - spyOn(listener, 'storeUndefinedStep'); - spyOn(listener, 'witnessUndefinedStep'); - spyOn(listener, 'markCurrentScenarioAsUndefined'); - }); - - it("gets the step from the step result", function() { - listener.handleUndefinedStepResult(stepResult); - expect(stepResult.getStep).toHaveBeenCalled(); - }); - - it("stores the undefined step", function() { - listener.handleUndefinedStepResult(stepResult); - expect(listener.storeUndefinedStep).toHaveBeenCalledWith(step); - }); - - it("witnesses an undefined step", function() { - listener.handleUndefinedStepResult(stepResult); - expect(listener.witnessUndefinedStep).toHaveBeenCalled(); - }); - - it("marks the current scenario as undefined", function() { - listener.handleUndefinedStepResult(stepResult); - expect(listener.markCurrentScenarioAsUndefined).toHaveBeenCalled(); - }); - - }); - - describe("handleFailedStepResult()", function() { - var stepResult; - - beforeEach(function() { - stepResult = createSpy("failed step result"); - spyOn(listener, 'storeFailedStepResult'); - spyOn(listener, 'witnessFailedStep'); - spyOn(listener, 'markCurrentScenarioAsFailing'); - }); - - it("stores the failed step result", function() { - listener.handleFailedStepResult(stepResult); - expect(listener.storeFailedStepResult).toHaveBeenCalledWith(stepResult); - }); - - it("witnesses a failed step", function() { - listener.handleFailedStepResult(stepResult); - expect(listener.witnessFailedStep).toHaveBeenCalled(); - }); - - it("marks the current scenario as failing", function() { - listener.handleFailedStepResult(stepResult); - expect(listener.markCurrentScenarioAsFailing).toHaveBeenCalled(); - }); - }); describe("handleBeforeScenarioEvent", function() { @@ -417,15 +251,8 @@ describe("Cucumber.Listener.JsonFormatter", function() { spyOn(listener, 'prepareBeforeScenario'); }); - it("prepares for a new scenario", function() { - listener.handleBeforeScenarioEvent(event, callback); - expect(listener.prepareBeforeScenario).toHaveBeenCalled(); - }); + // TODO: Add replacement tests - it("calls back", function() { - listener.handleBeforeScenarioEvent(event, callback); - expect(callback).toHaveBeenCalled(); - }); }); describe("handleAfterFeaturesEvent()", function() { @@ -444,625 +271,7 @@ describe("Cucumber.Listener.JsonFormatter", function() { describe("handleAfterScenarioEvent()", function() { var event, callback; - - beforeEach(function() { - event = createSpy("event"); - callback = createSpy("callback"); - spyOn(listener, 'isCurrentScenarioFailing'); - spyOn(listener, 'witnessPassedScenario'); - spyOn(listener, 'witnessUndefinedScenario'); - spyOn(listener, 'witnessPendingScenario'); - spyOn(listener, 'witnessFailedScenario'); - }); - - it("checks wether the current scenario failed", function() { - listener.handleAfterScenarioEvent(event, callback); - expect(listener.isCurrentScenarioFailing).toHaveBeenCalled(); - }); - - describe("when the current scenario failed", function() { - var scenario; - - beforeEach(function() { - scenario = createSpy("scenario"); - listener.isCurrentScenarioFailing.andReturn(true); - spyOn(listener, 'storeFailedScenario'); - spyOnStub(event, 'getPayloadItem').andReturn(scenario); - }); - - it("witnesses a failed scenario", function() { - listener.handleAfterScenarioEvent(event, callback); - expect(listener.witnessFailedScenario).toHaveBeenCalled(); - }); - - it("gets the scenario from the payload", function() { - listener.handleAfterScenarioEvent(event, callback); - expect(event.getPayloadItem).toHaveBeenCalledWith('scenario'); - }); - - it("stores the failed scenario", function() { - listener.handleAfterScenarioEvent(event, callback); - expect(listener.storeFailedScenario).toHaveBeenCalledWith(scenario); - }); - }); - - describe("when the current scenario did not fail", function() { - beforeEach(function() { - listener.isCurrentScenarioFailing.andReturn(false); - spyOn(listener, 'isCurrentScenarioUndefined'); - }); - - it("checks wether the current scenario is undefined", function() { - listener.handleAfterScenarioEvent(event, callback); - expect(listener.isCurrentScenarioUndefined).toHaveBeenCalled(); - }); - - describe("when the current scenario is undefined", function() { - beforeEach(function() { - listener.isCurrentScenarioUndefined.andReturn(true); - }); - - it("witnesses an undefined scenario", function() { - listener.handleAfterScenarioEvent(event, callback); - expect(listener.witnessUndefinedScenario).toHaveBeenCalled(); - }); - }); - - describe("when the current scenario is not undefined", function() { - beforeEach(function() { - listener.isCurrentScenarioUndefined.andReturn(false); - spyOn(listener, 'isCurrentScenarioPending'); - }); - - it("checks wether the current scenario is pending", function() { - listener.handleAfterScenarioEvent(event, callback); - expect(listener.isCurrentScenarioPending).toHaveBeenCalled(); - }); - - describe("when the current scenario is pending", function() { - beforeEach(function() { - listener.isCurrentScenarioPending.andReturn(true); - }); - - it("witnesses a pending scenario", function() { - listener.handleAfterScenarioEvent(event, callback); - expect(listener.witnessPendingScenario).toHaveBeenCalled(); - }); - }); - - describe("when the current scenario is not pending (passed)", function() { - beforeEach(function() { - listener.isCurrentScenarioPending.andReturn(false); - }); - - it("witnesses a passed scenario", function() { - listener.handleAfterScenarioEvent(event, callback); - expect(listener.witnessPassedScenario).toHaveBeenCalled(); - }); - }); - }); - }); - it("calls back", function() { - listener.handleAfterScenarioEvent(event, callback); - expect(callback).toHaveBeenCalled(); - }); - }); - - describe("isCurrentScenarioFailing()", function() { - it("returns false when the current scenario did not fail yet", function() { - expect(listener.isCurrentScenarioFailing()).toBeFalsy(); - }); - - it("returns true when a step in the current scenario failed", function() { - listener.markCurrentScenarioAsFailing(); - expect(listener.isCurrentScenarioFailing()).toBeTruthy(); - }); - }); - - describe("isCurrentScenarioPending()", function() { - it("returns false when the current scenario was not set pending yet", function() { - expect(listener.isCurrentScenarioPending()).toBeFalsy(); - }); - - it("returns true when the current scenario was set pending", function() { - listener.markCurrentScenarioAsPending(); - expect(listener.isCurrentScenarioPending()).toBeTruthy(); - }); - }); - - describe("isCurrentScenarioUndefined()", function() { - it("returns false when the current scenario was not set undefined yet", function() { - expect(listener.isCurrentScenarioUndefined()).toBeFalsy(); - }); - - it("returns true when the current scenario was set undefined", function() { - listener.markCurrentScenarioAsUndefined(); - expect(listener.isCurrentScenarioUndefined()).toBeTruthy(); - }); - }); - - describe("prepareBeforeScenario()", function() { - it("unmarks the current scenario as pending", function() { - listener.markCurrentScenarioAsPending(); - listener.prepareBeforeScenario(); - expect(listener.isCurrentScenarioPending()).toBeFalsy(); - }); - - it("unmarks the current scenario as failing", function() { - listener.markCurrentScenarioAsFailing(); - listener.prepareBeforeScenario(); - expect(listener.isCurrentScenarioFailing()).toBeFalsy(); - }); - - it("unmarks the current scenario as undefined", function() { - listener.markCurrentScenarioAsUndefined(); - listener.prepareBeforeScenario(); - expect(listener.isCurrentScenarioUndefined()).toBeFalsy(); - }); - }); - - describe("storeFailedStepResult()", function() { - var failedStepResult; - - beforeEach(function() { - failedStepResult = createSpy("failed step result"); - spyOnStub(failedStepResults, 'add'); - }); - - it("adds the result to the failed step result collection", function() { - listener.storeFailedStepResult(failedStepResult); - expect(failedStepResults.add).toHaveBeenCalledWith(failedStepResult); - }); - }); - - describe("storeFailedScenario()", function() { - var failedScenario, name, line; - - beforeEach(function() { - name = "some failed scenario"; - line = "123"; - string = ":" + line + " # Scenario: " + name; - failedScenario = createSpyWithStubs("failedScenario", {getName: name, getLine: line}); - spyOn(listener, 'appendStringToFailedScenarioLogBuffer'); - }); - - it("gets the name of the scenario", function() { - listener.storeFailedScenario(failedScenario); - expect(failedScenario.getName).toHaveBeenCalled(); - }); - - it("gets the line of the scenario", function() { - listener.storeFailedScenario(failedScenario); - expect(failedScenario.getLine).toHaveBeenCalled(); - }); - - it("appends the scenario details to the failed scenario log buffer", function() { - listener.storeFailedScenario(failedScenario); - expect(listener.appendStringToFailedScenarioLogBuffer).toHaveBeenCalledWith(string); - }); - }); - - describe("storeUndefinedStep()", function() { - var snippetBuilder, snippet, step; - - beforeEach(function() { - stpe = createSpy("step"); - snippet = createSpy("step definition snippet"); - snippetBuilder = createSpyWithStubs("snippet builder", {buildSnippet: snippet}); - spyOn(Cucumber.SupportCode, 'StepDefinitionSnippetBuilder').andReturn(snippetBuilder); - spyOn(listener, 'appendStringToUndefinedStepLogBuffer'); - }); - - it("creates a new step definition snippet builder", function() { - listener.storeUndefinedStep(step); - expect(Cucumber.SupportCode.StepDefinitionSnippetBuilder).toHaveBeenCalledWith(step); - }); - - it("builds the step definition", function() { - listener.storeUndefinedStep(step); - expect(snippetBuilder.buildSnippet).toHaveBeenCalled(); - }); - - it("appends the snippet to the undefined step log buffer", function() { - listener.storeUndefinedStep(step); - expect(listener.appendStringToUndefinedStepLogBuffer).toHaveBeenCalledWith(snippet); - }); - }); - - describe("getFailedScenarioLogBuffer() [appendStringToFailedScenarioLogBuffer()]", function() { - it("returns the logged failed scenario details", function() { - listener.appendStringToFailedScenarioLogBuffer("abc"); - expect(listener.getFailedScenarioLogBuffer()).toBe("abc\n"); - }); - - it("returns all logged failed scenario lines joined with a line break", function() { - listener.appendStringToFailedScenarioLogBuffer("abc"); - listener.appendStringToFailedScenarioLogBuffer("def"); - expect(listener.getFailedScenarioLogBuffer()).toBe("abc\ndef\n"); - }); - }); - - describe("getUndefinedStepLogBuffer() [appendStringToUndefinedStepLogBuffer()]", function() { - it("returns the logged undefined step details", function() { - listener.appendStringToUndefinedStepLogBuffer("abc"); - expect(listener.getUndefinedStepLogBuffer()).toBe("abc\n"); - }); - - it("returns all logged failed scenario lines joined with a line break", function() { - listener.appendStringToUndefinedStepLogBuffer("abc"); - listener.appendStringToUndefinedStepLogBuffer("def"); - expect(listener.getUndefinedStepLogBuffer()).toBe("abc\ndef\n"); - }); - }); - - describe("appendStringToUndefinedStepLogBuffer() [getUndefinedStepLogBuffer()]", function() { - it("does not log the same string twice", function() { - listener.appendStringToUndefinedStepLogBuffer("abcdef"); - listener.appendStringToUndefinedStepLogBuffer("abcdef"); - expect(listener.getUndefinedStepLogBuffer()).toBe("abcdef\n"); - }); - }); - - describe("getScenarioCount()", function() { - var passedScenarioCount, undefinedScenarioCount, pendingScenarioCount, failedScenarioCount; - - beforeEach(function() { - passedScenarioCount = Math.floor(Math.random()*11) + 1; - undefinedScenarioCount = Math.floor(Math.random()*11) + 1; - pendingScenarioCount = Math.floor(Math.random()*11) + 1; - failedScenarioCount = Math.floor(Math.random()*11) + 1; - spyOn(listener, 'getPassedScenarioCount').andReturn(passedScenarioCount); - spyOn(listener, 'getUndefinedScenarioCount').andReturn(undefinedScenarioCount); - spyOn(listener, 'getPendingScenarioCount').andReturn(pendingScenarioCount); - spyOn(listener, 'getFailedScenarioCount').andReturn(failedScenarioCount); - }); - - it("gets the number of passed scenarios", function() { - listener.getScenarioCount(); - expect(listener.getPassedScenarioCount).toHaveBeenCalled(); - }); - - it("gets the number of undefined scenarios", function() { - listener.getScenarioCount(); - expect(listener.getUndefinedScenarioCount).toHaveBeenCalled(); - }); - - it("gets the number of pending scenarios", function() { - listener.getScenarioCount(); - expect(listener.getPendingScenarioCount).toHaveBeenCalled(); - }); - - it("gets the number of failed scenarios", function() { - listener.getScenarioCount(); - expect(listener.getFailedScenarioCount).toHaveBeenCalled(); - }); - - it("returns the sum of passed, undefined, pending aand failed scenarios", function() { - expect(listener.getScenarioCount()).toBe(passedScenarioCount + undefinedScenarioCount + pendingScenarioCount + failedScenarioCount); - }); - }); - - describe("getStepCount()", function() { - var passedStepCount, undefinedStepCount, skippedStepCount, pendingStepCount, failedStepCount, stepCount; - - beforeEach(function() { - passedStepCount = Math.floor(Math.random()*11) + 1; - undefinedStepCount = Math.floor(Math.random()*11) + 1; - skippedStepCount = Math.floor(Math.random()*11) + 1; - pendingStepCount = Math.floor(Math.random()*11) + 1; - failedStepCount = Math.floor(Math.random()*11) + 1; - stepCount = - undefinedStepCount + - passedStepCount + - skippedStepCount + - pendingStepCount + - failedStepCount; - spyOn(listener, 'getPassedStepCount').andReturn(passedStepCount); - spyOn(listener, 'getUndefinedStepCount').andReturn(undefinedStepCount); - spyOn(listener, 'getSkippedStepCount').andReturn(skippedStepCount); - spyOn(listener, 'getPendingStepCount').andReturn(pendingStepCount); - spyOn(listener, 'getFailedStepCount').andReturn(failedStepCount); - }); - - it("gets the number of passed steps", function() { - listener.getStepCount(); - expect(listener.getPassedStepCount).toHaveBeenCalled(); - }); - - it("gets the number of undefined steps", function() { - listener.getStepCount(); - expect(listener.getUndefinedStepCount).toHaveBeenCalled(); - }); - - it("gets the number of skipped steps", function() { - listener.getStepCount(); - expect(listener.getSkippedStepCount).toHaveBeenCalled(); - }); - - it("gets the number of pending steps", function() { - listener.getStepCount(); - expect(listener.getPendingStepCount).toHaveBeenCalled(); - }); - - it("gets the number of failed steps", function() { - listener.getStepCount(); - expect(listener.getFailedStepCount).toHaveBeenCalled(); - }); - - it("returns the sum of passed steps and failed steps", function() { - expect(listener.getStepCount()).toBe(stepCount); - }); - }); - - describe("passed scenario counting", function() { - describe("witnessPassedScenario()", function() { - it("counts one more passed scenario", function() { - var beforeCountOne = listener.getPassedScenarioCount(); - listener.witnessPassedScenario(); - expect(listener.getPassedScenarioCount()).toBe(beforeCountOne + 1); - }); - }); - - describe("getPassedScenarioCount()", function() { - it("returns 0 when no scenario passed", function() { - expect(listener.getPassedScenarioCount()).toBe(0); - }); - - it("returns 1 when one scenario passed", function() { - listener.witnessPassedScenario(); - expect(listener.getPassedScenarioCount()).toBe(1); - }); - - it("returns 2 when two scenarios passed", function() { - listener.witnessPassedScenario(); - listener.witnessPassedScenario(); - expect(listener.getPassedScenarioCount()).toBe(2); - }); - - it("returns 3 when three scenarios passed", function() { - listener.witnessPassedScenario(); - listener.witnessPassedScenario(); - listener.witnessPassedScenario(); - expect(listener.getPassedScenarioCount()).toBe(3); - }); - }); - }); - - describe("undefined scenario counting", function() { - describe("getUndefinedScenarioCount()", function() { - it("returns 0 when no scenarios undefined", function() { - expect(listener.getUndefinedScenarioCount()).toBe(0); - }); - - it("returns 1 when one scenario passed", function() { - listener.witnessUndefinedScenario(); - expect(listener.getUndefinedScenarioCount()).toBe(1); - }); - - it("returns 2 when two scenarios passed", function() { - listener.witnessUndefinedScenario(); - listener.witnessUndefinedScenario(); - expect(listener.getUndefinedScenarioCount()).toBe(2); - }); - - it("returns 3 when two scenarios passed", function() { - listener.witnessUndefinedScenario(); - listener.witnessUndefinedScenario(); - listener.witnessUndefinedScenario(); - expect(listener.getUndefinedScenarioCount()).toBe(3); - }); - }); - }); - - describe("pending scenario counting", function() { - describe("getPendingScenarioCount()", function() { - it("returns 0 when no scenarios pending", function() { - expect(listener.getPendingScenarioCount()).toBe(0); - }); - - it("returns 1 when one scenario passed", function() { - listener.witnessPendingScenario(); - expect(listener.getPendingScenarioCount()).toBe(1); - }); - - it("returns 2 when two scenarios passed", function() { - listener.witnessPendingScenario(); - listener.witnessPendingScenario(); - expect(listener.getPendingScenarioCount()).toBe(2); - }); - - it("returns 3 when two scenarios passed", function() { - listener.witnessPendingScenario(); - listener.witnessPendingScenario(); - listener.witnessPendingScenario(); - expect(listener.getPendingScenarioCount()).toBe(3); - }); - }); - }); - - describe("failed scenario counting", function() { - describe("getFailedScenarioCount()", function() { - it("returns 0 when no scenarios failed", function() { - expect(listener.getFailedScenarioCount()).toBe(0); - }); - - it("returns 1 when one scenario passed", function() { - listener.witnessFailedScenario(); - expect(listener.getFailedScenarioCount()).toBe(1); - }); - - it("returns 2 when two scenarios passed", function() { - listener.witnessFailedScenario(); - listener.witnessFailedScenario(); - expect(listener.getFailedScenarioCount()).toBe(2); - }); - - it("returns 3 when two scenarios passed", function() { - listener.witnessFailedScenario(); - listener.witnessFailedScenario(); - listener.witnessFailedScenario(); - expect(listener.getFailedScenarioCount()).toBe(3); - }); - }); - }); - - describe("passed step counting", function() { - describe("witnessPassedStep()", function() { - it("counts one more passed step", function() { - var beforeCountOne = listener.getPassedStepCount(); - listener.witnessPassedStep(); - expect(listener.getPassedStepCount()).toBe(beforeCountOne + 1); - }); - }); - - describe("getPassedStepCount()", function() { - it("returns 0 when no step passed", function() { - expect(listener.getPassedStepCount()).toBe(0); - }); - - it("returns 1 when one step passed", function() { - listener.witnessPassedStep(); - expect(listener.getPassedStepCount()).toBe(1); - }); - - it("returns 2 when two steps passed", function() { - listener.witnessPassedStep(); - listener.witnessPassedStep(); - expect(listener.getPassedStepCount()).toBe(2); - }); - - it("returns 3 when three steps passed", function() { - listener.witnessPassedStep(); - listener.witnessPassedStep(); - listener.witnessPassedStep(); - expect(listener.getPassedStepCount()).toBe(3); - }); - }); - }); - - describe("failed step counting", function() { - describe("getFailedStepCount()", function() { - it("returns 0 when no steps failed", function() { - expect(listener.getFailedStepCount()).toBe(0); - }); - - it("returns 1 when one step passed", function() { - listener.witnessFailedStep(); - expect(listener.getFailedStepCount()).toBe(1); - }); - - it("returns 2 when two steps passed", function() { - listener.witnessFailedStep(); - listener.witnessFailedStep(); - expect(listener.getFailedStepCount()).toBe(2); - }); - - it("returns 3 when two steps passed", function() { - listener.witnessFailedStep(); - listener.witnessFailedStep(); - listener.witnessFailedStep(); - expect(listener.getFailedStepCount()).toBe(3); - }); - }); - }); - - describe("skipped step counting", function() { - describe("getSkippedStepCount()", function() { - it("returns 0 when no steps skipped", function() { - expect(listener.getSkippedStepCount()).toBe(0); - }); - - it("returns 1 when one step passed", function() { - listener.witnessSkippedStep(); - expect(listener.getSkippedStepCount()).toBe(1); - }); - - it("returns 2 when two steps passed", function() { - listener.witnessSkippedStep(); - listener.witnessSkippedStep(); - expect(listener.getSkippedStepCount()).toBe(2); - }); - - it("returns 3 when two steps passed", function() { - listener.witnessSkippedStep(); - listener.witnessSkippedStep(); - listener.witnessSkippedStep(); - expect(listener.getSkippedStepCount()).toBe(3); - }); - }); - }); - - describe("undefined step counting", function() { - describe("getUndefinedStepCount()", function() { - it("returns 0 when no steps undefined", function() { - expect(listener.getUndefinedStepCount()).toBe(0); - }); - - it("returns 1 when one step passed", function() { - listener.witnessUndefinedStep(); - expect(listener.getUndefinedStepCount()).toBe(1); - }); - - it("returns 2 when two steps passed", function() { - listener.witnessUndefinedStep(); - listener.witnessUndefinedStep(); - expect(listener.getUndefinedStepCount()).toBe(2); - }); - - it("returns 3 when two steps passed", function() { - listener.witnessUndefinedStep(); - listener.witnessUndefinedStep(); - listener.witnessUndefinedStep(); - expect(listener.getUndefinedStepCount()).toBe(3); - }); - }); - }); - - describe("pending step counting", function() { - describe("getPendingStepCount()", function() { - it("returns 0 when no steps pending", function() { - expect(listener.getPendingStepCount()).toBe(0); - }); - - it("returns 1 when one step passed", function() { - listener.witnessPendingStep(); - expect(listener.getPendingStepCount()).toBe(1); - }); - - it("returns 2 when two steps passed", function() { - listener.witnessPendingStep(); - listener.witnessPendingStep(); - expect(listener.getPendingStepCount()).toBe(2); - }); - - it("returns 3 when two steps passed", function() { - listener.witnessPendingStep(); - listener.witnessPendingStep(); - listener.witnessPendingStep(); - expect(listener.getPendingStepCount()).toBe(3); - }); - }); - }); - - describe("witnessedAnyFailedStep()", function() { - it("returns false when no failed step were encountered", function() { - expect(listener.witnessedAnyFailedStep()).toBeFalsy(); - }); - - it("returns true when one or more steps were witnessed", function() { - listener.witnessFailedStep(); - expect(listener.witnessedAnyFailedStep()).toBeTruthy(); - }); - }); - - describe("witnessedAnyUndefinedStep()", function() { - it("returns false when no undefined step were encountered", function() { - expect(listener.witnessedAnyUndefinedStep()).toBeFalsy(); - }); - - it("returns true when one or more steps were witnessed", function() { - listener.witnessUndefinedStep(); - expect(listener.witnessedAnyUndefinedStep()).toBeTruthy(); - }); + // DO WE EVEN NEED THIS? }); }); + From b3dc4ad020d0ed47037b8fddad5730d9cc5f6bfc Mon Sep 17 00:00:00 2001 From: Chris Young Date: Thu, 21 Jun 2012 15:21:11 +0100 Subject: [PATCH 13/44] Removing redundant dir --- features/err | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 features/err diff --git a/features/err b/features/err deleted file mode 100644 index e69de29bb..000000000 From e042dae7ed24c06680836cdbbf1505e9efaaab8c Mon Sep 17 00:00:00 2001 From: Chris Young Date: Thu, 21 Jun 2012 17:45:14 +0100 Subject: [PATCH 14/44] Added more tests. Pushed the placeholder URI down into the event object. Don't know how it should be populated. --- features/json_formatter.feature | 5 ++++- features/step_definitions/cli_steps.js | 18 ++++++++++++++++++ lib/cucumber/ast/feature.js | 11 ++++++++++- lib/cucumber/listener/json_formatter.js | 7 ++++++- lib/cucumber/parser.js | 4 ++-- spec/cucumber/ast/feature_spec.js | 9 ++++++++- 6 files changed, 48 insertions(+), 6 deletions(-) diff --git a/features/json_formatter.feature b/features/json_formatter.feature index 347a94771..43d749208 100644 --- a/features/json_formatter.feature +++ b/features/json_formatter.feature @@ -166,7 +166,7 @@ Feature: JSON Formatter }; module.exports = cucumberSteps; """ - + And CUCUMBER_JS_HOME environment variable has been set to the cucumber-js install dir When I run `cucumber.js -f json` Then it should output this json: """ @@ -192,6 +192,7 @@ Feature: JSON Formatter "line": 4, "keyword": "Given ", "result": { + "error_message": "Error: Step failure\n at Function.fail ($CUCUMBER_JS_HOME/lib/cucumber/support_code/step_definition.js:52:49)\n at World. ($CUCUMBER_JS_HOME/tmp/cucumber-js-sandbox/features/step_definitions/cucumber_steps.js:2:70)\n at Object.invoke ($CUCUMBER_JS_HOME/lib/cucumber/support_code/step_definition.js:59:14)\n at Object.execute ($CUCUMBER_JS_HOME/lib/cucumber/ast/step.js:153:22)\n at Object.acceptVisitor ($CUCUMBER_JS_HOME/lib/cucumber/ast/step.js:145:12)\n at Object.executeStep ($CUCUMBER_JS_HOME/lib/cucumber/runtime/ast_tree_walker.js:170:12)\n at Object.processStep ($CUCUMBER_JS_HOME/lib/cucumber/runtime/ast_tree_walker.js:165:14)\n at $CUCUMBER_JS_HOME/lib/cucumber/runtime/ast_tree_walker.js:67:16\n at callUserFunctionAndBroadcastAfterEvent ($CUCUMBER_JS_HOME/lib/cucumber/runtime/ast_tree_walker.js:91:9)\n at iterate ($CUCUMBER_JS_HOME/lib/cucumber/type/collection.js:14:11)", "status": "failed" }, "match": { @@ -435,6 +436,7 @@ Scenario: one feature, one passing scenario, one failing scenario }; module.exports = cucumberSteps; """ + And CUCUMBER_JS_HOME environment variable has been set to the cucumber-js install dir When I run `cucumber.js -f json` Then it should output this json: """ @@ -481,6 +483,7 @@ Scenario: one feature, one passing scenario, one failing scenario "line": 6, "keyword": "Given ", "result": { + "error_message": "Error: Step failure\n at Function.fail ($CUCUMBER_JS_HOME/lib/cucumber/support_code/step_definition.js:52:49)\n at World. ($CUCUMBER_JS_HOME/tmp/cucumber-js-sandbox/features/step_definitions/cucumber_steps.js:3:70)\n at Object.invoke ($CUCUMBER_JS_HOME/lib/cucumber/support_code/step_definition.js:59:14)\n at Object.execute ($CUCUMBER_JS_HOME/lib/cucumber/ast/step.js:153:22)\n at Object.acceptVisitor ($CUCUMBER_JS_HOME/lib/cucumber/ast/step.js:145:12)\n at Object.executeStep ($CUCUMBER_JS_HOME/lib/cucumber/runtime/ast_tree_walker.js:170:12)\n at Object.processStep ($CUCUMBER_JS_HOME/lib/cucumber/runtime/ast_tree_walker.js:165:14)\n at $CUCUMBER_JS_HOME/lib/cucumber/runtime/ast_tree_walker.js:67:16\n at callUserFunctionAndBroadcastAfterEvent ($CUCUMBER_JS_HOME/lib/cucumber/runtime/ast_tree_walker.js:91:9)\n at iterate ($CUCUMBER_JS_HOME/lib/cucumber/type/collection.js:14:11)", "status": "failed" }, "match": { diff --git a/features/step_definitions/cli_steps.js b/features/step_definitions/cli_steps.js index ec1a4ddcd..9eb979c23 100644 --- a/features/step_definitions/cli_steps.js +++ b/features/step_definitions/cli_steps.js @@ -75,6 +75,16 @@ var cliSteps = function cliSteps() { callback(); }); + this.Given(/^CUCUMBER_JS_HOME environment variable has been set to the cucumber\-js install dir$/, function(callback) { + // This is needed to allow us to check the error_message produced for a failed steps which includes paths which + // contain the location where cucumber-js is installed. + if (process.env.CUCUMBER_JS_HOME) { + callback(); + } else { + callback.fail(new Error("CUCUMBER_JS_HOME has not been set.")); + } + }); + this.Then(/^it should output this json:$/, function(expectedOutput, callback) { var actualOutput = lastRun['stdout']; @@ -88,6 +98,14 @@ var cliSteps = function cliSteps() { throw new Error("Error parsing actual JSON:\n" + actualOutput); } + if (expectedOutput.indexOf('$CUCUMBER_JS_HOME') != -1) { + if (!process.env.CUCUMBER_JS_HOME) { + callback.fail(new Error("CUCUMBER_JS_HOME has not been set.")); + } else { + expectedOutput = expectedOutput.replace(/\$CUCUMBER_JS_HOME/g, process.env.CUCUMBER_JS_HOME); + } + } + try { var expectedJson = JSON.parse(expectedOutput); } diff --git a/lib/cucumber/ast/feature.js b/lib/cucumber/ast/feature.js index a9ebec68d..fb1377c46 100644 --- a/lib/cucumber/ast/feature.js +++ b/lib/cucumber/ast/feature.js @@ -1,4 +1,4 @@ -var Feature = function(keyword, name, description, line) { +var Feature = function(keyword, name, description, line, uri) { var Cucumber = require('../../cucumber'); var background; @@ -22,6 +22,15 @@ var Feature = function(keyword, name, description, line) { return line; }, + getUri: function getUri() { + //TODO: Add uri to feature + if (!uri) { + return 'TODO'; + } else { + return uri; + } + }, + addBackground: function addBackground(newBackground) { background = newBackground; }, diff --git a/lib/cucumber/listener/json_formatter.js b/lib/cucumber/listener/json_formatter.js index f8cda80dc..2ed0dfab6 100644 --- a/lib/cucumber/listener/json_formatter.js +++ b/lib/cucumber/listener/json_formatter.js @@ -46,7 +46,8 @@ var JsonFormatter = function(options) { description: feature.getDescription(), line: feature.getLine(), keyword: feature.getKeyword(), - uri: 'TODO'} + uri: feature.getUri() + } output.push(element); callback(); @@ -112,6 +113,10 @@ var JsonFormatter = function(options) { else if (stepResult.isUndefined()) { resultStatus = "undefined"; } + else { + var failureMessage = stepResult.getFailureException(); + stepOutput["result"]["error_message"] = (failureMessage.stack || failureMessage); + } stepOutput["result"]["status"] = resultStatus stepOutput["match"] = {location:"TODO"} diff --git a/lib/cucumber/parser.js b/lib/cucumber/parser.js index 52ecd9b36..1c8570d8d 100644 --- a/lib/cucumber/parser.js +++ b/lib/cucumber/parser.js @@ -49,8 +49,8 @@ var Parser = function(featureSources, astFilter) { handleEof: function handleEof() {}, - handleFeature: function handleFeature(keyword, name, description, line) { - var feature = Cucumber.Ast.Feature(keyword, name, description, line); + handleFeature: function handleFeature(keyword, name, description, line, uri) { + var feature = Cucumber.Ast.Feature(keyword, name, description, line, uri); astAssembler.insertFeature(feature); }, diff --git a/spec/cucumber/ast/feature_spec.js b/spec/cucumber/ast/feature_spec.js index 7e755c2cc..99bba94b3 100644 --- a/spec/cucumber/ast/feature_spec.js +++ b/spec/cucumber/ast/feature_spec.js @@ -16,7 +16,8 @@ describe("Cucumber.Ast.Feature", function() { name = createSpy("Feature name"); description = createSpy("Feature description"); line = createSpy("Feature line number"); - feature = Cucumber.Ast.Feature(keyword, name, description, line); + uri = createSpy("Feaure uri"); + feature = Cucumber.Ast.Feature(keyword, name, description, line, uri); }); describe("constructor", function() { @@ -49,6 +50,12 @@ describe("Cucumber.Ast.Feature", function() { }); }); + describe("getUri()", function() { + it("returns a uniform resource identifier for the feature consisting of the filename containing the feature", function() { + expect(feature.getUri()).toBe(uri); + }); + }); + describe("getBackground() [addBackground()]", function() { describe("when a background was previously added", function() { var background; From 7035ea0c1cda288ba6d56f2d81adcee97406a7eb Mon Sep 17 00:00:00 2001 From: Chris Young Date: Fri, 22 Jun 2012 15:29:44 +0100 Subject: [PATCH 15/44] Added tests for multiple features. Fixed functionality to populate correct feature/scenario with its child steps. Added more Jasmine tests --- README.md | 3 +- features/foo.feature | 22 + features/json_formatter.feature | 730 ++++++++++++++---- lib/cucumber/listener/json_formatter.js | 46 +- spec/cucumber/listener/json_formatter_spec.js | 162 +++- 5 files changed, 766 insertions(+), 197 deletions(-) create mode 100644 features/foo.feature diff --git a/README.md b/README.md index 5cb98be55..2582e09a8 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,7 @@ Cucumber.js is still a work in progress. Here is its current status. | [Failing steps](/~https://github.com/cucumber/cucumber-tck/blob/master/failing_steps.feature) | Done | | [Hooks](/~https://github.com/cucumber/cucumber-tck/blob/master/hooks.feature) | Done | | [I18n](/~https://github.com/cucumber/cucumber-tck/blob/master/i18n.feature) | To do | -| [JSON formatter](/~https://github.com/cucumber/cucumber-tck/blob/master/json_formatter.feature) | Hacking away... | +| [JSON formatter](/~https://github.com/cucumber/cucumber-tck/blob/master/json_formatter.feature) | In Progress4 | | [Pretty formatter](/~https://github.com/cucumber/cucumber-tck/blob/master/pretty_formatter.feature) | To do2 | | [Scenario outlines and examples](/~https://github.com/cucumber/cucumber-tck/blob/master/scenario_outlines_and_examples.feature) | To do | | [Stats collector](/~https://github.com/cucumber/cucumber-tck/blob/master/stats_collector.feature) | To do | @@ -38,6 +38,7 @@ Cucumber.js is still a work in progress. Here is its current status. 1. Not certified by [Cucumber TCK](/~https://github.com/cucumber/cucumber-tck) yet. 2. Considered for removal from [Cucumber TCK](/~https://github.com/cucumber/cucumber-tck). 3. Simple *Around*, *Before* and *After* hooks are available. +4. Missing 'uri' and 'matches' attributes. ### Cucumber.js-specific features diff --git a/features/foo.feature b/features/foo.feature new file mode 100644 index 000000000..f31504472 --- /dev/null +++ b/features/foo.feature @@ -0,0 +1,22 @@ +Feature: Foo + Scenario: output JSON for a feature with one scenario with failing step + Given a file named "features/a.feature" with: + """ + Feature: some feature + + Scenario: I've declaired one step but it is failing + Given This step is failing + """ + And a file named "features/step_definitions/cucumber_steps.js" with: + """ + var cucumberSteps = function() { + this.Given(/^This step is failing$/, function(callback) { callback.fail(); }); + }; + module.exports = cucumberSteps; + """ + And CUCUMBER_JS_HOME environment variable has been set to the cucumber-js install dir + When I run `cucumber.js -f json` + Then it should output this json: + """ + [] + """ diff --git a/features/json_formatter.feature b/features/json_formatter.feature index 43d749208..9efb538ae 100644 --- a/features/json_formatter.feature +++ b/features/json_formatter.feature @@ -10,15 +10,15 @@ Feature: JSON Formatter When I run `cucumber.js -f json` Then it should output this json: """ -[ - {"id":"some-feature", - "name":"some feature", - "description":"", - "line":1, - "keyword":"Feature", - "uri":"TODO" - } -] + [ + {"id":"some-feature", + "name":"some feature", + "description":"", + "line":1, + "keyword":"Feature", + "uri":"TODO" + } + ] """ Scenario: output JSON for a feature with one undefined scenario @@ -31,25 +31,25 @@ Feature: JSON Formatter When I run `cucumber.js -f json` Then it should output this json: """ -[ - { - "id":"some-feature", - "name":"some feature", - "description":"", - "line":1, - "keyword":"Feature", - "uri":"TODO", - "elements":[ - {"name":"I havn't done anything yet", - "id":"some-feature;i-havn't-done-anything-yet", - "line":3, - "keyword":"Scenario", - "description":"", - "type":"scenario" - } - ] - } -] + [ + { + "id":"some-feature", + "name":"some feature", + "description":"", + "line":1, + "keyword":"Feature", + "uri":"TODO", + "elements":[ + {"name":"I havn't done anything yet", + "id":"some-feature;i-havn't-done-anything-yet", + "line":3, + "keyword":"Scenario", + "description":"", + "type":"scenario" + } + ] + } + ] """ Scenario: output JSON for a feature with one scenario with one undefined step @@ -63,38 +63,38 @@ Feature: JSON Formatter When I run `cucumber.js -f json` Then it should output this json: """ -[ - { - "id": "some-feature", - "name": "some feature", - "description": "", - "line": 1, - "keyword": "Feature", - "uri":"TODO", - "elements": [ - { - "name": "I've declaired one step but not yet defined it", - "id": "some-feature;i've-declaired-one-step-but-not-yet-defined-it", - "line": 3, - "keyword": "Scenario", - "description": "", - "type": "scenario", - "steps":[ - {"name":"I have not defined this step", - "line":4, - "keyword":"Given ", - "result": - {"status":"undefined" - }, - "match": - {"location":"TODO" + [ + { + "id": "some-feature", + "name": "some feature", + "description": "", + "line": 1, + "keyword": "Feature", + "uri":"TODO", + "elements": [ + { + "name": "I've declaired one step but not yet defined it", + "id": "some-feature;i've-declaired-one-step-but-not-yet-defined-it", + "line": 3, + "keyword": "Scenario", + "description": "", + "type": "scenario", + "steps":[ + {"name":"I have not defined this step", + "line":4, + "keyword":"Given ", + "result": + {"status":"undefined" + }, + "match": + {"location":"TODO" + } + } + ] } - } ] - } - ] - } -] + } + ] """ Scenario: output JSON for a feature with one scenario with one pending step @@ -360,62 +360,62 @@ Feature: JSON Formatter When I run `cucumber.js -f json` Then it should output this json: """ -[ - { - "id": "some-feature", - "name": "some feature", - "description": "", - "line": 1, - "keyword": "Feature", - "uri": "TODO", - "elements": [ - { - "name": "I've declaired one step which is passing, one pending and one failing.", - "id": "some-feature;i've-declaired-one-step-which-is-passing,-one-pending-and-one-failing.", - "line": 3, - "keyword": "Scenario", - "description": "", - "type": "scenario", - "steps": [ - { - "name": "This step is pending", - "line": 4, - "keyword": "Given ", - "result": { - "error_message": "TODO", - "status": "pending" - }, - "match": { - "location": "TODO" - } - }, - { - "name": "This step is passing but will be skipped", - "line": 5, - "keyword": "And ", - "result": { - "status": "skipped" - }, - "match": { - "location": "TODO" - } - }, - { - "name": "This step fails but will be skipped", - "line": 6, - "keyword": "And ", - "result": { - "status": "skipped" - }, - "match": { - "location": "TODO" + [ + { + "id": "some-feature", + "name": "some feature", + "description": "", + "line": 1, + "keyword": "Feature", + "uri": "TODO", + "elements": [ + { + "name": "I've declaired one step which is passing, one pending and one failing.", + "id": "some-feature;i've-declaired-one-step-which-is-passing,-one-pending-and-one-failing.", + "line": 3, + "keyword": "Scenario", + "description": "", + "type": "scenario", + "steps": [ + { + "name": "This step is pending", + "line": 4, + "keyword": "Given ", + "result": { + "error_message": "TODO", + "status": "pending" + }, + "match": { + "location": "TODO" + } + }, + { + "name": "This step is passing but will be skipped", + "line": 5, + "keyword": "And ", + "result": { + "status": "skipped" + }, + "match": { + "location": "TODO" + } + }, + { + "name": "This step fails but will be skipped", + "line": 6, + "keyword": "And ", + "result": { + "status": "skipped" + }, + "match": { + "location": "TODO" + } + } + ] } - } - ] - } - ] - } -] + ] + } + ] """ Scenario: one feature, one passing scenario, one failing scenario @@ -440,66 +440,464 @@ Scenario: one feature, one passing scenario, one failing scenario When I run `cucumber.js -f json` Then it should output this json: """ -[ - { - "id": "one-passes one fails", - "name": "one passes one fails", - "description": "", - "line": 1, - "keyword": "Feature", - "uri": "TODO", - "elements": [ - { - "name": "This one passes", - "id": "one-passes one fails;this-one-passes", - "line": 3, - "keyword": "Scenario", - "description": "", - "type": "scenario", - "steps": [ - { - "name": "This step is passing", - "line": 4, - "keyword": "Given ", - "result": { - "status": "passed" + [ + { + "id": "one-passes one fails", + "name": "one passes one fails", + "description": "", + "line": 1, + "keyword": "Feature", + "uri": "TODO", + "elements": [ + { + "name": "This one passes", + "id": "one-passes one fails;this-one-passes", + "line": 3, + "keyword": "Scenario", + "description": "", + "type": "scenario", + "steps": [ + { + "name": "This step is passing", + "line": 4, + "keyword": "Given ", + "result": { + "status": "passed" + }, + "match": { + "location": "TODO" + } + } + ] }, - "match": { - "location": "TODO" + { + "name": "This one fails", + "id": "one-passes one fails;this-one-fails", + "line": 5, + "keyword": "Scenario", + "description": "", + "type": "scenario", + "steps": [ + { + "name": "This step is failing", + "line": 6, + "keyword": "Given ", + "result": { + "error_message": "Error: Step failure\n at Function.fail ($CUCUMBER_JS_HOME/lib/cucumber/support_code/step_definition.js:52:49)\n at World. ($CUCUMBER_JS_HOME/tmp/cucumber-js-sandbox/features/step_definitions/cucumber_steps.js:3:70)\n at Object.invoke ($CUCUMBER_JS_HOME/lib/cucumber/support_code/step_definition.js:59:14)\n at Object.execute ($CUCUMBER_JS_HOME/lib/cucumber/ast/step.js:153:22)\n at Object.acceptVisitor ($CUCUMBER_JS_HOME/lib/cucumber/ast/step.js:145:12)\n at Object.executeStep ($CUCUMBER_JS_HOME/lib/cucumber/runtime/ast_tree_walker.js:170:12)\n at Object.processStep ($CUCUMBER_JS_HOME/lib/cucumber/runtime/ast_tree_walker.js:165:14)\n at $CUCUMBER_JS_HOME/lib/cucumber/runtime/ast_tree_walker.js:67:16\n at callUserFunctionAndBroadcastAfterEvent ($CUCUMBER_JS_HOME/lib/cucumber/runtime/ast_tree_walker.js:91:9)\n at iterate ($CUCUMBER_JS_HOME/lib/cucumber/type/collection.js:14:11)", + "status": "failed" + }, + "match": { + "location": "TODO" + } + } + ] } - } - ] - }, - { - "name": "This one fails", - "id": "one-passes one fails;this-one-fails", - "line": 5, - "keyword": "Scenario", - "description": "", - "type": "scenario", - "steps": [ - { - "name": "This step is failing", - "line": 6, - "keyword": "Given ", - "result": { - "error_message": "Error: Step failure\n at Function.fail ($CUCUMBER_JS_HOME/lib/cucumber/support_code/step_definition.js:52:49)\n at World. ($CUCUMBER_JS_HOME/tmp/cucumber-js-sandbox/features/step_definitions/cucumber_steps.js:3:70)\n at Object.invoke ($CUCUMBER_JS_HOME/lib/cucumber/support_code/step_definition.js:59:14)\n at Object.execute ($CUCUMBER_JS_HOME/lib/cucumber/ast/step.js:153:22)\n at Object.acceptVisitor ($CUCUMBER_JS_HOME/lib/cucumber/ast/step.js:145:12)\n at Object.executeStep ($CUCUMBER_JS_HOME/lib/cucumber/runtime/ast_tree_walker.js:170:12)\n at Object.processStep ($CUCUMBER_JS_HOME/lib/cucumber/runtime/ast_tree_walker.js:165:14)\n at $CUCUMBER_JS_HOME/lib/cucumber/runtime/ast_tree_walker.js:67:16\n at callUserFunctionAndBroadcastAfterEvent ($CUCUMBER_JS_HOME/lib/cucumber/runtime/ast_tree_walker.js:91:9)\n at iterate ($CUCUMBER_JS_HOME/lib/cucumber/type/collection.js:14:11)", - "status": "failed" + ] + } + ] + """ + Scenario: output JSON for multiple features + Given a file named "features/a.feature" with: + """ + Feature: feature a + + Scenario: This is the first feature + Given This step is passing + """ + And a file named "features/b.feature" with: + """ + Feature: feature b + + Scenario: This is the second feature + Given This step is passing + """ + And a file named "features/c.feature" with: + """ + Feature: feature c + + Scenario: This is the third feature + Given This step is passing + """ + And a file named "features/step_definitions/cucumber_steps.js" with: + """ + var cucumberSteps = function() { + this.Given(/^This step is passing$/, function(callback) { callback(); }); + }; + module.exports = cucumberSteps; + """ + When I run `cucumber.js -f json` + Then it should output this json: + """ + [ + { + "id": "feature-a", + "name": "feature a", + "description": "", + "line": 1, + "keyword": "Feature", + "uri": "TODO", + "elements": [ + { + "name": "This is the first feature", + "id": "feature-a;this-is-the-first-feature", + "line": 3, + "keyword": "Scenario", + "description": "", + "type": "scenario", + "steps": [ + { + "name": "This step is passing", + "line": 4, + "keyword": "Given ", + "result": { + "status": "passed" + }, + "match": { + "location": "TODO" + } + } + ] + } + ] + }, + { + "id": "feature-b", + "name": "feature b", + "description": "", + "line": 1, + "keyword": "Feature", + "uri": "TODO", + "elements": [ + { + "name": "This is the second feature", + "id": "feature-b;this-is-the-second-feature", + "line": 3, + "keyword": "Scenario", + "description": "", + "type": "scenario", + "steps": [ + { + "name": "This step is passing", + "line": 4, + "keyword": "Given ", + "result": { + "status": "passed" + }, + "match": { + "location": "TODO" + } + } + ] + } + ] + }, + { + "id": "feature-c", + "name": "feature c", + "description": "", + "line": 1, + "keyword": "Feature", + "uri": "TODO", + "elements": [ + { + "name": "This is the third feature", + "id": "feature-c;this-is-the-third-feature", + "line": 3, + "keyword": "Scenario", + "description": "", + "type": "scenario", + "steps": [ + { + "name": "This step is passing", + "line": 4, + "keyword": "Given ", + "result": { + "status": "passed" + }, + "match": { + "location": "TODO" + } + } + ] + } + ] + } + ] + """ + Scenario: output JSON for multiple features each with multiple scenarios + Given a file named "features/a.feature" with: + """ + Feature: feature a + + Scenario: This is the feature a scenario one + Given This step is passing + + Scenario: This is the feature a scenario two + Given This step is passing + + Scenario: This is the feature a scenario three + Given This step is passing + """ + And a file named "features/b.feature" with: + """ + Feature: feature b + + Scenario: This is the feature b scenario one + Given This step is passing + + Scenario: This is the feature b scenario two + Given This step is passing + + Scenario: This is the feature b scenario three + Given This step is passing + """ + And a file named "features/c.feature" with: + """ + Feature: feature c + + Scenario: This is the feature c scenario one + Given This step is passing + + Scenario: This is the feature c scenario two + Given This step is passing + + Scenario: This is the feature c scenario three + Given This step is passing + """ + And a file named "features/step_definitions/cucumber_steps.js" with: + """ + var cucumberSteps = function() { + this.Given(/^This step is passing$/, function(callback) { callback(); }); + }; + module.exports = cucumberSteps; + """ + When I run `cucumber.js -f json` + Then it should output this json: + """ + [ + { + "id": "feature-a", + "name": "feature a", + "description": "", + "line": 1, + "keyword": "Feature", + "uri": "TODO", + "elements": [ + { + "name": "This is the feature a scenario one", + "id": "feature-a;this-is-the-feature-a-scenario-one", + "line": 3, + "keyword": "Scenario", + "description": "", + "type": "scenario", + "steps": [ + { + "name": "This step is passing", + "line": 4, + "keyword": "Given ", + "result": { + "status": "passed" + }, + "match": { + "location": "TODO" + } + } + ] + }, + { + "name": "This is the feature a scenario two", + "id": "feature-a;this-is-the-feature-a-scenario-two", + "line": 6, + "keyword": "Scenario", + "description": "", + "type": "scenario", + "steps": [ + { + "name": "This step is passing", + "line": 7, + "keyword": "Given ", + "result": { + "status": "passed" + }, + "match": { + "location": "TODO" + } + } + ] }, - "match": { - "location": "TODO" + { + "name": "This is the feature a scenario three", + "id": "feature-a;this-is-the-feature-a-scenario-three", + "line": 9, + "keyword": "Scenario", + "description": "", + "type": "scenario", + "steps": [ + { + "name": "This step is passing", + "line": 10, + "keyword": "Given ", + "result": { + "status": "passed" + }, + "match": { + "location": "TODO" + } + } + ] } - } - ] - } - ] - } -] + ] + }, + { + "id": "feature-b", + "name": "feature b", + "description": "", + "line": 1, + "keyword": "Feature", + "uri": "TODO", + "elements": [ + { + "name": "This is the feature b scenario one", + "id": "feature-b;this-is-the-feature-b-scenario-one", + "line": 3, + "keyword": "Scenario", + "description": "", + "type": "scenario", + "steps": [ + { + "name": "This step is passing", + "line": 4, + "keyword": "Given ", + "result": { + "status": "passed" + }, + "match": { + "location": "TODO" + } + } + ] + }, + { + "name": "This is the feature b scenario two", + "id": "feature-b;this-is-the-feature-b-scenario-two", + "line": 6, + "keyword": "Scenario", + "description": "", + "type": "scenario", + "steps": [ + { + "name": "This step is passing", + "line": 7, + "keyword": "Given ", + "result": { + "status": "passed" + }, + "match": { + "location": "TODO" + } + } + ] + }, + { + "name": "This is the feature b scenario three", + "id": "feature-b;this-is-the-feature-b-scenario-three", + "line": 9, + "keyword": "Scenario", + "description": "", + "type": "scenario", + "steps": [ + { + "name": "This step is passing", + "line": 10, + "keyword": "Given ", + "result": { + "status": "passed" + }, + "match": { + "location": "TODO" + } + } + ] + } + ] + }, + { + "id": "feature-c", + "name": "feature c", + "description": "", + "line": 1, + "keyword": "Feature", + "uri": "TODO", + "elements": [ + { + "name": "This is the feature c scenario one", + "id": "feature-c;this-is-the-feature-c-scenario-one", + "line": 3, + "keyword": "Scenario", + "description": "", + "type": "scenario", + "steps": [ + { + "name": "This step is passing", + "line": 4, + "keyword": "Given ", + "result": { + "status": "passed" + }, + "match": { + "location": "TODO" + } + } + ] + }, + { + "name": "This is the feature c scenario two", + "id": "feature-c;this-is-the-feature-c-scenario-two", + "line": 6, + "keyword": "Scenario", + "description": "", + "type": "scenario", + "steps": [ + { + "name": "This step is passing", + "line": 7, + "keyword": "Given ", + "result": { + "status": "passed" + }, + "match": { + "location": "TODO" + } + } + ] + }, + { + "name": "This is the feature c scenario three", + "id": "feature-c;this-is-the-feature-c-scenario-three", + "line": 9, + "keyword": "Scenario", + "description": "", + "type": "scenario", + "steps": [ + { + "name": "This step is passing", + "line": 10, + "keyword": "Given ", + "result": { + "status": "passed" + }, + "match": { + "location": "TODO" + } + } + ] + } + ] + } + ] """ # Embedings? # DocString? - # 'it should pass with... is a bit misleading' - diff --git a/lib/cucumber/listener/json_formatter.js b/lib/cucumber/listener/json_formatter.js index 2ed0dfab6..3a3a78a45 100644 --- a/lib/cucumber/listener/json_formatter.js +++ b/lib/cucumber/listener/json_formatter.js @@ -2,11 +2,20 @@ var JsonFormatter = function(options) { var Cucumber = require('../../cucumber'); var currentFeatureId = ''; + var currentFeatureIndex = null; var output = []; var self = { + getOutput: function getOutput() { + return output; + }, + + getCurrentFeatureIndex: function getCurrentFeatureIndex() { + return currentFeatureIndex; + }, + hear: function hear(event, callback) { if (self.hasHandlerForEvent(event)) { @@ -39,6 +48,11 @@ var JsonFormatter = function(options) { var feature = event.getPayloadItem('feature'); currentFeatureId = feature.getName().replace(' ','-'); + if (currentFeatureIndex == null) { + currentFeatureIndex = 0; + } else { + currentFeatureIndex+=1; + } var element = { id: currentFeatureId, @@ -68,11 +82,11 @@ var JsonFormatter = function(options) { scenarioOutput["description"]= scenario.getDescription(); scenarioOutput["type"]= "scenario"; - if (!("elements" in output[0])) { - output[0]["elements"] = []; + if (!("elements" in output[currentFeatureIndex])) { + output[currentFeatureIndex]["elements"] = []; } - output[0]["elements"].push(scenarioOutput); + output[currentFeatureIndex]["elements"].push(scenarioOutput); callback(); }, @@ -80,11 +94,24 @@ var JsonFormatter = function(options) { handleStepResultEvent: function handleStepResult(event, callback) { var stepResult = event.getPayloadItem('stepResult'); - if (!("elements" in output[0])) { - output[0]["elements"] = []; //TODO: Make Dry + if (output.length == 0) { + // There is no parent scenario so create an empty element to contain the step + var parent_element = {}; + output.push(parent_element); + } + + if (currentFeatureIndex == null) { + currentFeatureIndex = 0; + } + + if (!("elements" in output[currentFeatureIndex])) { + var element = {steps: []}; + output[currentFeatureIndex]["elements"] = []; //TODO: Make Dry + output[currentFeatureIndex]["elements"].push(element); } - var element = output[0]["elements"][output[0]["elements"].length-1] + var element = output[currentFeatureIndex]["elements"][output[currentFeatureIndex]["elements"].length-1] + if (!element["steps"]) { element["steps"] = []; } @@ -126,13 +153,10 @@ var JsonFormatter = function(options) { }, handleAfterFeaturesEvent: function handleAfterFeaturesEvent(event, callback) { + // TODO: Factor this out and get rid of magic number process.stdout.write(JSON.stringify(output, null, 2)); callback(); - }, - - handleAfterScenarioEvent: function handleAfterScenarioEvent(event, callback) { - callback(); - }, + } }; return self; diff --git a/spec/cucumber/listener/json_formatter_spec.js b/spec/cucumber/listener/json_formatter_spec.js index 4d5ca9337..3419f25d1 100644 --- a/spec/cucumber/listener/json_formatter_spec.js +++ b/spec/cucumber/listener/json_formatter_spec.js @@ -11,6 +11,8 @@ describe("Cucumber.Listener.JsonFormatter", function() { describe("constructor", function() { }); + // Get Event + describe("hear()", function() { var event, callback; var eventHandler; @@ -150,14 +152,147 @@ describe("Cucumber.Listener.JsonFormatter", function() { }); }); + // Handle Feature + + describe("handleBeforeFeatureEvent()", function() { + var scenario, event, callback; + + beforeEach(function() { + feature = createSpyWithStubs("feature", + {getKeyword: 'Feature', + getName: 'A Name', + getDescription: 'A Description', + getLine: 3, + getUri: 'TODO'}); + + event = createSpyWithStubs("event", {getPayloadItem: feature}); + + callback = createSpy("callback"); + }); + + it("adds the feature attributes to the output", function() { + listener.handleBeforeFeatureEvent(event, callback); + var output = listener.getOutput(); + var expectedOutput = '[ { "id": "A-Name", \ + "name": "A Name", \ + "description": "A Description", \ + "line": 3, \ + "keyword": "Feature", \ + "uri": "TODO" } ]'; + + var expectedJson = JSON.parse(expectedOutput); + var expectedJsonString = JSON.stringify(expectedJson, null, 2); + var actualJsonString = JSON.stringify(output, null, 2); + + expect(actualJsonString).toEqual(expectedJsonString); + + }); + + it("it keeps track of the current feature so we can add scenarios and steps to it", function() { + listener.handleBeforeFeatureEvent(event, callback); + expect(listener.getCurrentFeatureIndex()).toEqual(0); + listener.handleBeforeFeatureEvent(event, callback); + expect(listener.getCurrentFeatureIndex()).toEqual(1); + listener.handleBeforeFeatureEvent(event, callback); + expect(listener.getCurrentFeatureIndex()).toEqual(2); + }); + + }); + + // Handle Scenario + + describe("handleBeforeScenarioEvent()", function() { + + }); + + // Handle Step Results + describe("handleStepResultEvent()", function() { var event, callback, stepResult; beforeEach(function() { + + step = createSpyWithStubs("step", { + getName: 'Step', + getLine: 3, + getKeyword: 'Step' + }); + + stepResult = createSpyWithStubs("step result", { + isSuccessful: undefined, + isPending: undefined, + isFailed: undefined, + isSkipped: undefined, + isUndefined: undefined, + getStep: step + }); + event = createSpyWithStubs("event", {getPayloadItem: stepResult}); callback = createSpy("Callback"); }); + + it("adds the step result to the parent scenario in the output", function(){ + stepResult.isSuccessful.andReturn(true); + listener.handleStepResultEvent(event, callback); + output = listener.getOutput(); + + var expectedOutput = '[ \ + { \ + "elements": [ \ + { \ + "steps": [ \ + { \ + "name": "Step", \ + "line": 3, \ + "keyword": "Step", \ + "result": { \ + "status": "passed" \ + }, \ + "match": { \ + "location": "TODO" \ + } \ + } \ + ] \ + } \ + ] \ + } \ + ]'; + + var expectedJson = JSON.parse(expectedOutput); + var actualJsonString = JSON.stringify(output, null, 2); + var expectedJsonString = JSON.stringify(expectedJson, null, 2); + + expect(actualJsonString).toEqual(expectedJsonString); + + }); + + it("adds the step result to parent when there are multiple features in the output", function(){ + + stepResult.isSuccessful.andReturn(true); + + feature = createSpyWithStubs("feature", + {getKeyword: 'Feature', + getName: 'A Name', + getDescription: 'A Description', + getLine: 3, + getUri: 'TODO'}); + + feature_event = createSpyWithStubs("event", {getPayloadItem: feature}); + + listener.handleBeforeFeatureEvent(feature_event, callback); + listener.handleStepResultEvent(event, callback); + + listener.handleBeforeFeatureEvent(feature_event, callback); + listener.handleStepResultEvent(event, callback); + + listener.handleBeforeFeatureEvent(feature_event, callback); + listener.handleStepResultEvent(event, callback); + + // console.log(listener.getOutput()); + }); + + it("gets the step result from the event payload", function() { }); @@ -239,21 +374,7 @@ describe("Cucumber.Listener.JsonFormatter", function() { }); }); - describe("handleBeforeScenarioEvent", function() { - var scenario, event, callback; - - beforeEach(function() { - scenario = createSpyWithStubs("scenario", {getName: "A Scenario"}); - event = createSpyWithStubs("event", {getPayloadItem: scenario}); - - - callback = createSpy("callback"); - spyOn(listener, 'prepareBeforeScenario'); - }); - - // TODO: Add replacement tests - - }); + // We're all done. Output the JSON. describe("handleAfterFeaturesEvent()", function() { var features, callback; @@ -261,17 +382,20 @@ describe("Cucumber.Listener.JsonFormatter", function() { beforeEach(function() { event = createSpy("Event"); callback = createSpy("Callback"); + spyOn(process.stdout, 'write'); // prevent actual output during spec execution + }); + + it("writes to stdout", function() { + listener.handleAfterFeaturesEvent(event, callback); + expect(process.stdout.write).toHaveBeenCalled(); //TODO: With anything? }); it("calls back", function() { listener.handleAfterFeaturesEvent(event, callback); expect(callback).toHaveBeenCalled(); }); - }); - describe("handleAfterScenarioEvent()", function() { - var event, callback; - // DO WE EVEN NEED THIS? }); + }); From 3a03a9174bc24c1c38f556b90def693a9e5a10b2 Mon Sep 17 00:00:00 2001 From: Chris Young Date: Fri, 22 Jun 2012 15:33:28 +0100 Subject: [PATCH 16/44] Added note to readme to clarify current status of JSON formatter --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 2582e09a8..2a62ae8fd 100644 --- a/README.md +++ b/README.md @@ -38,7 +38,7 @@ Cucumber.js is still a work in progress. Here is its current status. 1. Not certified by [Cucumber TCK](/~https://github.com/cucumber/cucumber-tck) yet. 2. Considered for removal from [Cucumber TCK](/~https://github.com/cucumber/cucumber-tck). 3. Simple *Around*, *Before* and *After* hooks are available. -4. Missing 'uri' and 'matches' attributes. +4. Missing 'uri' and 'matches' attributes. Needs more Jasmine tests and refactoring before submitting 'pull' request. ### Cucumber.js-specific features From 516c5b954eaecbdd984f5c7ec7b9f430323fb860 Mon Sep 17 00:00:00 2001 From: Chris Young Date: Fri, 22 Jun 2012 17:45:32 +0100 Subject: [PATCH 17/44] Removed redunant require of Cucumber-js --- lib/cucumber/listener/json_formatter.js | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/cucumber/listener/json_formatter.js b/lib/cucumber/listener/json_formatter.js index 3a3a78a45..ef2dc9c19 100644 --- a/lib/cucumber/listener/json_formatter.js +++ b/lib/cucumber/listener/json_formatter.js @@ -1,5 +1,4 @@ var JsonFormatter = function(options) { - var Cucumber = require('../../cucumber'); var currentFeatureId = ''; var currentFeatureIndex = null; From 109d47dd2919b97a1c4662c78ddafd06f798deca Mon Sep 17 00:00:00 2001 From: Chris Young Date: Mon, 25 Jun 2012 17:19:46 +0100 Subject: [PATCH 18/44] Updated README to show that we are now using the new Gherkin provided json_formatter --- README.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 47e648c18..211f33e19 100644 --- a/README.md +++ b/README.md @@ -25,13 +25,8 @@ Cucumber.js is still a work in progress. Here is its current status. | [Failing steps](/~https://github.com/cucumber/cucumber-tck/blob/master/failing_steps.feature) | Done | | [Hooks](/~https://github.com/cucumber/cucumber-tck/blob/master/hooks.feature) | Done | | [I18n](/~https://github.com/cucumber/cucumber-tck/blob/master/i18n.feature) | To do | -<<<<<<< HEAD -| [JSON formatter](/~https://github.com/cucumber/cucumber-tck/blob/master/json_formatter.feature) | In Progress4 | -| [Pretty formatter](/~https://github.com/cucumber/cucumber-tck/blob/master/pretty_formatter.feature) | To do2 | -======= -| [JSON formatter](/~https://github.com/cucumber/cucumber-tck/blob/master/json_formatter.feature) | To do | +| [JSON formatter](/~https://github.com/cucumber/cucumber-tck/blob/master/json_formatter.feature) | WIP4 | | [Pretty formatter](/~https://github.com/cucumber/cucumber-tck/blob/master/pretty_formatter.feature) | WIP2 | ->>>>>>> upstream/master | [Scenario outlines and examples](/~https://github.com/cucumber/cucumber-tck/blob/master/scenario_outlines_and_examples.feature) | To do | | [Stats collector](/~https://github.com/cucumber/cucumber-tck/blob/master/stats_collector.feature) | To do | | [Step argument transforms](/~https://github.com/cucumber/cucumber-tck/blob/master/step_argument_transforms.feature) | To do | @@ -43,7 +38,12 @@ Cucumber.js is still a work in progress. Here is its current status. 1. Not certified by [Cucumber TCK](/~https://github.com/cucumber/cucumber-tck) yet. 2. Considered for removal from [Cucumber TCK](/~https://github.com/cucumber/cucumber-tck). 3. Simple *Around*, *Before* and *After* hooks are available. -4. Missing 'matches' attributes. Needs more Jasmine tests and refactoring before submitting 'pull' request. +4. Missing 'matches' attributes. Simple wrapper for Gherkin JsonFormatter pending porting of: + +* /~https://github.com/cucumber/gherkin/blob/master/lib/gherkin/listener/formatter_listener.rb +* /~https://github.com/cucumber/gherkin/blob/master/lib/gherkin/formatter/filter_formatter.rb + +In Gherkin itself ### Cucumber.js-specific features From a7d9747f7a4a762d8d07a9823cdd41059984f6f6 Mon Sep 17 00:00:00 2001 From: Chris Young Date: Mon, 25 Jun 2012 23:20:15 +0100 Subject: [PATCH 19/44] Refactored to use the newly available Gherking json_formatter. Got one feature passing now. Ten to go... --- features/json_formatter.feature | 1804 ++++++++--------- lib/cucumber/cli.js | 4 +- lib/cucumber/cli/argument_parser.js | 10 +- lib/cucumber/cli/configuration.js | 22 +- lib/cucumber/listener.js | 2 +- lib/cucumber/listener/json_formatter.js | 168 -- spec/cucumber/listener/json_formatter_spec.js | 401 ---- 7 files changed, 911 insertions(+), 1500 deletions(-) delete mode 100644 lib/cucumber/listener/json_formatter.js delete mode 100644 spec/cucumber/listener/json_formatter_spec.js diff --git a/features/json_formatter.feature b/features/json_formatter.feature index 03e2f5175..ae1482926 100644 --- a/features/json_formatter.feature +++ b/features/json_formatter.feature @@ -1,904 +1,904 @@ -Feature: JSON Formatter - In order to simplify processing of Cucumber features and results - Developers should be able to consume features as JSON - - Background: - Given CUCUMBER_JS_HOME environment variable has been set to the cucumber-js install dir - - Scenario: output JSON for a feature with no scenarios - Given a file named "features/a.feature" with: - """ - Feature: some feature - """ - When I run `cucumber.js -f json` - Then it should output this json: - """ - [ - {"id":"some-feature", - "name":"some feature", - "description":"", - "line":1, - "keyword":"Feature", - "uri":"$CUCUMBER_JS_HOME/tmp/cucumber-js-sandbox/features/a.feature" - } - ] - """ - - Scenario: output JSON for a feature with one undefined scenario - Given a file named "features/a.feature" with: - """ - Feature: some feature - - Scenario: I havn't done anything yet - """ - When I run `cucumber.js -f json` - Then it should output this json: - """ - [ - { - "id":"some-feature", - "name":"some feature", - "description":"", - "line":1, - "keyword":"Feature", - "uri":"$CUCUMBER_JS_HOME/tmp/cucumber-js-sandbox/features/a.feature", - "elements":[ - {"name":"I havn't done anything yet", - "id":"some-feature;i-havn't-done-anything-yet", - "line":3, - "keyword":"Scenario", - "description":"", - "type":"scenario" - } - ] - } - ] - """ - - Scenario: output JSON for a feature with one scenario with one undefined step - Given a file named "features/a.feature" with: - """ - Feature: some feature - - Scenario: I've declaired one step but not yet defined it - Given I have not defined this step - """ - When I run `cucumber.js -f json` - Then it should output this json: - """ - [ - { - "id": "some-feature", - "name": "some feature", - "description": "", - "line": 1, - "keyword": "Feature", - "uri":"$CUCUMBER_JS_HOME/tmp/cucumber-js-sandbox/features/a.feature", - "elements": [ - { - "name": "I've declaired one step but not yet defined it", - "id": "some-feature;i've-declaired-one-step-but-not-yet-defined-it", - "line": 3, - "keyword": "Scenario", - "description": "", - "type": "scenario", - "steps":[ - {"name":"I have not defined this step", - "line":4, - "keyword":"Given ", - "result": - {"status":"undefined" - }, - "match": - {"location":"TODO" - } - } - ] - } - ] - } - ] - """ - - Scenario: output JSON for a feature with one scenario with one pending step - Given a file named "features/a.feature" with: - """ - Feature: some feature - - Scenario: I've declaired one step which is pending - Given This step is pending - """ - And a file named "features/step_definitions/cucumber_steps.js" with: - """ - var cucumberSteps = function() { - this.Given(/^This step is pending$/, function(callback) { callback.pending(); }); - }; - module.exports = cucumberSteps; - """ - - When I run `cucumber.js -f json` - Then it should output this json: - """ - [ - { - "id": "some-feature", - "name": "some feature", - "description": "", - "line": 1, - "keyword": "Feature", - "uri":"$CUCUMBER_JS_HOME/tmp/cucumber-js-sandbox/features/a.feature", - "elements": [ - { - "name": "I've declaired one step which is pending", - "id": "some-feature;i've-declaired-one-step-which-is-pending", - "line": 3, - "keyword": "Scenario", - "description": "", - "type": "scenario", - "steps": [ - { - "name": "This step is pending", - "line": 4, - "keyword": "Given ", - "result": { - "error_message": "TODO", - "status": "pending" - }, - "match": { - "location": "TODO" - } - } - ] - } - ] + Feature: JSON Formatter + In order to simplify processing of Cucumber features and results + Developers should be able to consume features as JSON + + Background: + Given CUCUMBER_JS_HOME environment variable has been set to the cucumber-js install dir + + Scenario: output JSON for a feature with no scenarios + Given a file named "features/a.feature" with: + """ + Feature: some feature + """ + When I run `cucumber.js -f json` + Then it should output this json: + """ + [ + {"id":"some-feature", + "name":"some feature", + "description":"", + "line":1, + "keyword":"Feature", + "uri":"$CUCUMBER_JS_HOME/tmp/cucumber-js-sandbox/features/a.feature" } - ] - """ - Scenario: output JSON for a feature with one scenario with failing step - Given a file named "features/a.feature" with: - """ - Feature: some feature - - Scenario: I've declaired one step but it is failing - Given This step is failing - """ - And a file named "features/step_definitions/cucumber_steps.js" with: - """ - var cucumberSteps = function() { - this.Given(/^This step is failing$/, function(callback) { callback.fail(); }); - }; - module.exports = cucumberSteps; - """ - When I run `cucumber.js -f json` - Then it should output this json: - """ - [ - { - "id": "some-feature", - "name": "some feature", - "description": "", - "line": 1, - "keyword": "Feature", - "uri":"$CUCUMBER_JS_HOME/tmp/cucumber-js-sandbox/features/a.feature", - "elements": [ - { - "name": "I've declaired one step but it is failing", - "id": "some-feature;i've-declaired-one-step-but-it-is-failing", - "line": 3, - "keyword": "Scenario", - "description": "", - "type": "scenario", - "steps": [ - { - "name": "This step is failing", - "line": 4, - "keyword": "Given ", - "result": { - "error_message": "Error: Step failure\n at Function.fail ($CUCUMBER_JS_HOME/lib/cucumber/support_code/step_definition.js:41:49)\n at World. ($CUCUMBER_JS_HOME/tmp/cucumber-js-sandbox/features/step_definitions/cucumber_steps.js:2:70)\n at Object.invoke ($CUCUMBER_JS_HOME/lib/cucumber/support_code/step_definition.js:52:14)\n at Object.execute ($CUCUMBER_JS_HOME/lib/cucumber/ast/step.js:157:22)\n at Object.acceptVisitor ($CUCUMBER_JS_HOME/lib/cucumber/ast/step.js:149:12)\n at Object.executeStep ($CUCUMBER_JS_HOME/lib/cucumber/runtime/ast_tree_walker.js:170:12)\n at Object.processStep ($CUCUMBER_JS_HOME/lib/cucumber/runtime/ast_tree_walker.js:165:14)\n at $CUCUMBER_JS_HOME/lib/cucumber/runtime/ast_tree_walker.js:67:16\n at callUserFunctionAndBroadcastAfterEvent ($CUCUMBER_JS_HOME/lib/cucumber/runtime/ast_tree_walker.js:91:9)\n at iterate ($CUCUMBER_JS_HOME/lib/cucumber/type/collection.js:14:11)", - "status": "failed" - }, - "match": { - "location": "TODO" - } - } - ] - } - ] - } - ] - """ - Scenario: output JSON for a feature with one scenario with passing step - Given a file named "features/a.feature" with: - """ - Feature: some feature - - Scenario: I've declaired one step which passes - Given This step is passing - """ - And a file named "features/step_definitions/cucumber_steps.js" with: - """ - var cucumberSteps = function() { - this.Given(/^This step is passing$/, function(callback) { callback(); }); - }; - module.exports = cucumberSteps; - """ - When I run `cucumber.js -f json` - Then it should output this json: - """ - [ - { - "id": "some-feature", - "name": "some feature", - "description": "", - "line": 1, - "keyword": "Feature", - "uri":"$CUCUMBER_JS_HOME/tmp/cucumber-js-sandbox/features/a.feature", - "elements": [ - { - "name": "I've declaired one step which passes", - "id": "some-feature;i've-declaired-one-step-which-passes", - "line": 3, - "keyword": "Scenario", - "description": "", - "type": "scenario", - "steps": [ - { - "name": "This step is passing", - "line": 4, - "keyword": "Given ", - "result": { - "status": "passed" - }, - "match": { - "location": "TODO" - } - } - ] - } - ] - } - ] - """ - - Scenario: output JSON for a scenario with a passing step follwed by one that is pending and one that fails - Given a file named "features/a.feature" with: - """ - Feature: some feature - - Scenario: I've declaired one step which is passing, one pending and one failing. - Given This step is passing - And This step is pending - And This step fails but will be skipped - """ - And a file named "features/step_definitions/cucumber_steps.js" with: - """ - var cucumberSteps = function() { - this.Given(/^This step is passing$/, function(callback) { callback(); }); - this.Given(/^This step is pending$/, function(callback) { callback.pending(); }); - this.Given(/^This step fails but will be skipped$/, function(callback) { callback.fail(); }); - }; - module.exports = cucumberSteps; - """ - When I run `cucumber.js -f json` - Then it should output this json: - """ - [ - { - "id": "some-feature", - "name": "some feature", - "description": "", - "line": 1, - "keyword": "Feature", - "uri": "$CUCUMBER_JS_HOME/tmp/cucumber-js-sandbox/features/a.feature", - "elements": [ - { - "name": "I've declaired one step which is passing, one pending and one failing.", - "id": "some-feature;i've-declaired-one-step-which-is-passing,-one-pending-and-one-failing.", - "line": 3, - "keyword": "Scenario", - "description": "", - "type": "scenario", - "steps": [ - { - "name": "This step is passing", - "line": 4, - "keyword": "Given ", - "result": { - "status": "passed" - }, - "match": { - "location": "TODO" - } - }, - { - "name": "This step is pending", - "line": 5, - "keyword": "And ", - "result": { - "error_message": "TODO", - "status": "pending" - }, - "match": { - "location": "TODO" - } - }, - { - "name": "This step fails but will be skipped", - "line": 6, - "keyword": "And ", - "result": { - "status": "skipped" - }, - "match": { - "location": "TODO" - } - } - ] - } - ] - } - ] - """ - - Scenario: output JSON for a scenario with a pending step follwed by one that passes and one that fails - Given a file named "features/a.feature" with: - """ - Feature: some feature - - Scenario: I've declaired one step which is passing, one pending and one failing. - Given This step is pending - And This step is passing but will be skipped - And This step fails but will be skipped - """ - And a file named "features/step_definitions/cucumber_steps.js" with: - """ - var cucumberSteps = function() { - this.Given(/^This step is pending$/, function(callback) { callback.pending(); }); - this.Given(/^This step is passing but will be skipped$/, function(callback) { callback(); }); - this.Given(/^This step fails but will be skipped$/, function(callback) { callback.fail(); }); - }; - module.exports = cucumberSteps; - """ - When I run `cucumber.js -f json` - Then it should output this json: - """ - [ - { - "id": "some-feature", - "name": "some feature", - "description": "", - "line": 1, - "keyword": "Feature", - "uri": "$CUCUMBER_JS_HOME/tmp/cucumber-js-sandbox/features/a.feature", - "elements": [ - { - "name": "I've declaired one step which is passing, one pending and one failing.", - "id": "some-feature;i've-declaired-one-step-which-is-passing,-one-pending-and-one-failing.", - "line": 3, - "keyword": "Scenario", - "description": "", - "type": "scenario", - "steps": [ - { - "name": "This step is pending", - "line": 4, - "keyword": "Given ", - "result": { - "error_message": "TODO", - "status": "pending" - }, - "match": { - "location": "TODO" - } - }, - { - "name": "This step is passing but will be skipped", - "line": 5, - "keyword": "And ", - "result": { - "status": "skipped" - }, - "match": { - "location": "TODO" - } - }, - { - "name": "This step fails but will be skipped", - "line": 6, - "keyword": "And ", - "result": { - "status": "skipped" - }, - "match": { - "location": "TODO" - } - } - ] - } - ] - } - ] - """ - -Scenario: one feature, one passing scenario, one failing scenario - Given a file named "features/a.feature" with: - """ - Feature: one passes one fails - - Scenario: This one passes - Given This step is passing - Scenario: This one fails - Given This step is failing - """ - And a file named "features/step_definitions/cucumber_steps.js" with: - """ - var cucumberSteps = function() { - this.Given(/^This step is passing$/, function(callback) { callback(); }); - this.Given(/^This step is failing$/, function(callback) { callback.fail(); }); - }; - module.exports = cucumberSteps; - """ - When I run `cucumber.js -f json` - Then it should output this json: - """ - [ - { - "id": "one-passes one fails", - "name": "one passes one fails", - "description": "", - "line": 1, - "keyword": "Feature", - "uri": "$CUCUMBER_JS_HOME/tmp/cucumber-js-sandbox/features/a.feature", - "elements": [ - { - "name": "This one passes", - "id": "one-passes one fails;this-one-passes", - "line": 3, - "keyword": "Scenario", - "description": "", - "type": "scenario", - "steps": [ - { - "name": "This step is passing", - "line": 4, - "keyword": "Given ", - "result": { - "status": "passed" - }, - "match": { - "location": "TODO" - } - } - ] - }, - { - "name": "This one fails", - "id": "one-passes one fails;this-one-fails", - "line": 5, - "keyword": "Scenario", - "description": "", - "type": "scenario", - "steps": [ - { - "name": "This step is failing", - "line": 6, - "keyword": "Given ", - "result": { - "error_message": "Error: Step failure\n at Function.fail ($CUCUMBER_JS_HOME/lib/cucumber/support_code/step_definition.js:41:49)\n at World. ($CUCUMBER_JS_HOME/tmp/cucumber-js-sandbox/features/step_definitions/cucumber_steps.js:3:70)\n at Object.invoke ($CUCUMBER_JS_HOME/lib/cucumber/support_code/step_definition.js:52:14)\n at Object.execute ($CUCUMBER_JS_HOME/lib/cucumber/ast/step.js:157:22)\n at Object.acceptVisitor ($CUCUMBER_JS_HOME/lib/cucumber/ast/step.js:149:12)\n at Object.executeStep ($CUCUMBER_JS_HOME/lib/cucumber/runtime/ast_tree_walker.js:170:12)\n at Object.processStep ($CUCUMBER_JS_HOME/lib/cucumber/runtime/ast_tree_walker.js:165:14)\n at $CUCUMBER_JS_HOME/lib/cucumber/runtime/ast_tree_walker.js:67:16\n at callUserFunctionAndBroadcastAfterEvent ($CUCUMBER_JS_HOME/lib/cucumber/runtime/ast_tree_walker.js:91:9)\n at iterate ($CUCUMBER_JS_HOME/lib/cucumber/type/collection.js:14:11)", - "status": "failed" - }, - "match": { - "location": "TODO" - } - } - ] - } - ] - } - ] - """ - Scenario: output JSON for multiple features - Given a file named "features/a.feature" with: - """ - Feature: feature a - - Scenario: This is the first feature - Given This step is passing - """ - And a file named "features/b.feature" with: - """ - Feature: feature b - - Scenario: This is the second feature - Given This step is passing - """ - And a file named "features/c.feature" with: - """ - Feature: feature c - - Scenario: This is the third feature - Given This step is passing - """ - And a file named "features/step_definitions/cucumber_steps.js" with: - """ - var cucumberSteps = function() { - this.Given(/^This step is passing$/, function(callback) { callback(); }); - }; - module.exports = cucumberSteps; - """ - When I run `cucumber.js -f json` - Then it should output this json: - """ - [ - { - "id": "feature-a", - "name": "feature a", - "description": "", - "line": 1, - "keyword": "Feature", - "uri": "$CUCUMBER_JS_HOME/tmp/cucumber-js-sandbox/features/a.feature", - "elements": [ - { - "name": "This is the first feature", - "id": "feature-a;this-is-the-first-feature", - "line": 3, - "keyword": "Scenario", - "description": "", - "type": "scenario", - "steps": [ - { - "name": "This step is passing", - "line": 4, - "keyword": "Given ", - "result": { - "status": "passed" - }, - "match": { - "location": "TODO" - } - } - ] - } - ] - }, - { - "id": "feature-b", - "name": "feature b", - "description": "", - "line": 1, - "keyword": "Feature", - "uri": "$CUCUMBER_JS_HOME/tmp/cucumber-js-sandbox/features/b.feature", - "elements": [ - { - "name": "This is the second feature", - "id": "feature-b;this-is-the-second-feature", - "line": 3, - "keyword": "Scenario", - "description": "", - "type": "scenario", - "steps": [ - { - "name": "This step is passing", - "line": 4, - "keyword": "Given ", - "result": { - "status": "passed" - }, - "match": { - "location": "TODO" - } - } - ] - } - ] - }, - { - "id": "feature-c", - "name": "feature c", - "description": "", - "line": 1, - "keyword": "Feature", - "uri": "$CUCUMBER_JS_HOME/tmp/cucumber-js-sandbox/features/c.feature", - "elements": [ - { - "name": "This is the third feature", - "id": "feature-c;this-is-the-third-feature", - "line": 3, - "keyword": "Scenario", - "description": "", - "type": "scenario", - "steps": [ - { - "name": "This step is passing", - "line": 4, - "keyword": "Given ", - "result": { - "status": "passed" - }, - "match": { - "location": "TODO" - } - } - ] - } - ] - } - ] - """ - Scenario: output JSON for multiple features each with multiple scenarios - Given a file named "features/a.feature" with: - """ - Feature: feature a - - Scenario: This is the feature a scenario one - Given This step is passing - - Scenario: This is the feature a scenario two - Given This step is passing - - Scenario: This is the feature a scenario three - Given This step is passing - """ - And a file named "features/b.feature" with: - """ - Feature: feature b - - Scenario: This is the feature b scenario one - Given This step is passing - - Scenario: This is the feature b scenario two - Given This step is passing - - Scenario: This is the feature b scenario three - Given This step is passing - """ - And a file named "features/c.feature" with: - """ - Feature: feature c - - Scenario: This is the feature c scenario one - Given This step is passing - - Scenario: This is the feature c scenario two - Given This step is passing - - Scenario: This is the feature c scenario three - Given This step is passing - """ - And a file named "features/step_definitions/cucumber_steps.js" with: - """ - var cucumberSteps = function() { - this.Given(/^This step is passing$/, function(callback) { callback(); }); - }; - module.exports = cucumberSteps; - """ - When I run `cucumber.js -f json` - Then it should output this json: - """ - [ - { - "id": "feature-a", - "name": "feature a", - "description": "", - "line": 1, - "keyword": "Feature", - "uri": "$CUCUMBER_JS_HOME/tmp/cucumber-js-sandbox/features/a.feature", - "elements": [ - { - "name": "This is the feature a scenario one", - "id": "feature-a;this-is-the-feature-a-scenario-one", - "line": 3, - "keyword": "Scenario", - "description": "", - "type": "scenario", - "steps": [ - { - "name": "This step is passing", - "line": 4, - "keyword": "Given ", - "result": { - "status": "passed" - }, - "match": { - "location": "TODO" - } - } - ] - }, - { - "name": "This is the feature a scenario two", - "id": "feature-a;this-is-the-feature-a-scenario-two", - "line": 6, - "keyword": "Scenario", - "description": "", - "type": "scenario", - "steps": [ - { - "name": "This step is passing", - "line": 7, - "keyword": "Given ", - "result": { - "status": "passed" - }, - "match": { - "location": "TODO" - } - } - ] - }, - { - "name": "This is the feature a scenario three", - "id": "feature-a;this-is-the-feature-a-scenario-three", - "line": 9, - "keyword": "Scenario", - "description": "", - "type": "scenario", - "steps": [ - { - "name": "This step is passing", - "line": 10, - "keyword": "Given ", - "result": { - "status": "passed" - }, - "match": { - "location": "TODO" - } - } - ] - } - ] - }, - { - "id": "feature-b", - "name": "feature b", - "description": "", - "line": 1, - "keyword": "Feature", - "uri": "$CUCUMBER_JS_HOME/tmp/cucumber-js-sandbox/features/b.feature", - "elements": [ - { - "name": "This is the feature b scenario one", - "id": "feature-b;this-is-the-feature-b-scenario-one", - "line": 3, - "keyword": "Scenario", - "description": "", - "type": "scenario", - "steps": [ - { - "name": "This step is passing", - "line": 4, - "keyword": "Given ", - "result": { - "status": "passed" - }, - "match": { - "location": "TODO" - } - } - ] - }, - { - "name": "This is the feature b scenario two", - "id": "feature-b;this-is-the-feature-b-scenario-two", - "line": 6, - "keyword": "Scenario", - "description": "", - "type": "scenario", - "steps": [ - { - "name": "This step is passing", - "line": 7, - "keyword": "Given ", - "result": { - "status": "passed" - }, - "match": { - "location": "TODO" - } - } - ] - }, - { - "name": "This is the feature b scenario three", - "id": "feature-b;this-is-the-feature-b-scenario-three", - "line": 9, - "keyword": "Scenario", - "description": "", - "type": "scenario", - "steps": [ - { - "name": "This step is passing", - "line": 10, - "keyword": "Given ", - "result": { - "status": "passed" - }, - "match": { - "location": "TODO" - } - } - ] - } - ] - }, - { - "id": "feature-c", - "name": "feature c", - "description": "", - "line": 1, - "keyword": "Feature", - "uri": "$CUCUMBER_JS_HOME/tmp/cucumber-js-sandbox/features/c.feature", - "elements": [ - { - "name": "This is the feature c scenario one", - "id": "feature-c;this-is-the-feature-c-scenario-one", - "line": 3, - "keyword": "Scenario", - "description": "", - "type": "scenario", - "steps": [ - { - "name": "This step is passing", - "line": 4, - "keyword": "Given ", - "result": { - "status": "passed" - }, - "match": { - "location": "TODO" - } - } - ] - }, - { - "name": "This is the feature c scenario two", - "id": "feature-c;this-is-the-feature-c-scenario-two", - "line": 6, - "keyword": "Scenario", - "description": "", - "type": "scenario", - "steps": [ - { - "name": "This step is passing", - "line": 7, - "keyword": "Given ", - "result": { - "status": "passed" - }, - "match": { - "location": "TODO" - } - } - ] - }, - { - "name": "This is the feature c scenario three", - "id": "feature-c;this-is-the-feature-c-scenario-three", - "line": 9, - "keyword": "Scenario", - "description": "", - "type": "scenario", - "steps": [ - { - "name": "This step is passing", - "line": 10, - "keyword": "Given ", - "result": { - "status": "passed" - }, - "match": { - "location": "TODO" - } - } - ] - } - ] - } - ] - """ - - # Embedings? - - # DocString? + ] + """ + +# Scenario: output JSON for a feature with one undefined scenario +# Given a file named "features/a.feature" with: +# """ +# Feature: some feature + +# Scenario: I havn't done anything yet +# """ +# When I run `cucumber.js -f json` +# Then it should output this json: +# """ +# [ +# { +# "id":"some-feature", +# "name":"some feature", +# "description":"", +# "line":1, +# "keyword":"Feature", +# "uri":"$CUCUMBER_JS_HOME/tmp/cucumber-js-sandbox/features/a.feature", +# "elements":[ +# {"name":"I havn't done anything yet", +# "id":"some-feature;i-havn't-done-anything-yet", +# "line":3, +# "keyword":"Scenario", +# "description":"", +# "type":"scenario" +# } +# ] +# } +# ] +# """ + +# Scenario: output JSON for a feature with one scenario with one undefined step +# Given a file named "features/a.feature" with: +# """ +# Feature: some feature + +# Scenario: I've declaired one step but not yet defined it +# Given I have not defined this step +# """ +# When I run `cucumber.js -f json` +# Then it should output this json: +# """ +# [ +# { +# "id": "some-feature", +# "name": "some feature", +# "description": "", +# "line": 1, +# "keyword": "Feature", +# "uri":"$CUCUMBER_JS_HOME/tmp/cucumber-js-sandbox/features/a.feature", +# "elements": [ +# { +# "name": "I've declaired one step but not yet defined it", +# "id": "some-feature;i've-declaired-one-step-but-not-yet-defined-it", +# "line": 3, +# "keyword": "Scenario", +# "description": "", +# "type": "scenario", +# "steps":[ +# {"name":"I have not defined this step", +# "line":4, +# "keyword":"Given ", +# "result": +# {"status":"undefined" +# }, +# "match": +# {"location":"TODO" +# } +# } +# ] +# } +# ] +# } +# ] +# """ + +# Scenario: output JSON for a feature with one scenario with one pending step +# Given a file named "features/a.feature" with: +# """ +# Feature: some feature + +# Scenario: I've declaired one step which is pending +# Given This step is pending +# """ +# And a file named "features/step_definitions/cucumber_steps.js" with: +# """ +# var cucumberSteps = function() { +# this.Given(/^This step is pending$/, function(callback) { callback.pending(); }); +# }; +# module.exports = cucumberSteps; +# """ + +# When I run `cucumber.js -f json` +# Then it should output this json: +# """ +# [ +# { +# "id": "some-feature", +# "name": "some feature", +# "description": "", +# "line": 1, +# "keyword": "Feature", +# "uri":"$CUCUMBER_JS_HOME/tmp/cucumber-js-sandbox/features/a.feature", +# "elements": [ +# { +# "name": "I've declaired one step which is pending", +# "id": "some-feature;i've-declaired-one-step-which-is-pending", +# "line": 3, +# "keyword": "Scenario", +# "description": "", +# "type": "scenario", +# "steps": [ +# { +# "name": "This step is pending", +# "line": 4, +# "keyword": "Given ", +# "result": { +# "error_message": "TODO", +# "status": "pending" +# }, +# "match": { +# "location": "TODO" +# } +# } +# ] +# } +# ] +# } +# ] +# """ +# Scenario: output JSON for a feature with one scenario with failing step +# Given a file named "features/a.feature" with: +# """ +# Feature: some feature + +# Scenario: I've declaired one step but it is failing +# Given This step is failing +# """ +# And a file named "features/step_definitions/cucumber_steps.js" with: +# """ +# var cucumberSteps = function() { +# this.Given(/^This step is failing$/, function(callback) { callback.fail(); }); +# }; +# module.exports = cucumberSteps; +# """ +# When I run `cucumber.js -f json` +# Then it should output this json: +# """ +# [ +# { +# "id": "some-feature", +# "name": "some feature", +# "description": "", +# "line": 1, +# "keyword": "Feature", +# "uri":"$CUCUMBER_JS_HOME/tmp/cucumber-js-sandbox/features/a.feature", +# "elements": [ +# { +# "name": "I've declaired one step but it is failing", +# "id": "some-feature;i've-declaired-one-step-but-it-is-failing", +# "line": 3, +# "keyword": "Scenario", +# "description": "", +# "type": "scenario", +# "steps": [ +# { +# "name": "This step is failing", +# "line": 4, +# "keyword": "Given ", +# "result": { +# "error_message": "Error: Step failure\n at Function.fail ($CUCUMBER_JS_HOME/lib/cucumber/support_code/step_definition.js:41:49)\n at World. ($CUCUMBER_JS_HOME/tmp/cucumber-js-sandbox/features/step_definitions/cucumber_steps.js:2:70)\n at Object.invoke ($CUCUMBER_JS_HOME/lib/cucumber/support_code/step_definition.js:52:14)\n at Object.execute ($CUCUMBER_JS_HOME/lib/cucumber/ast/step.js:157:22)\n at Object.acceptVisitor ($CUCUMBER_JS_HOME/lib/cucumber/ast/step.js:149:12)\n at Object.executeStep ($CUCUMBER_JS_HOME/lib/cucumber/runtime/ast_tree_walker.js:170:12)\n at Object.processStep ($CUCUMBER_JS_HOME/lib/cucumber/runtime/ast_tree_walker.js:165:14)\n at $CUCUMBER_JS_HOME/lib/cucumber/runtime/ast_tree_walker.js:67:16\n at callUserFunctionAndBroadcastAfterEvent ($CUCUMBER_JS_HOME/lib/cucumber/runtime/ast_tree_walker.js:91:9)\n at iterate ($CUCUMBER_JS_HOME/lib/cucumber/type/collection.js:14:11)", +# "status": "failed" +# }, +# "match": { +# "location": "TODO" +# } +# } +# ] +# } +# ] +# } +# ] +# """ +# Scenario: output JSON for a feature with one scenario with passing step +# Given a file named "features/a.feature" with: +# """ +# Feature: some feature + +# Scenario: I've declaired one step which passes +# Given This step is passing +# """ +# And a file named "features/step_definitions/cucumber_steps.js" with: +# """ +# var cucumberSteps = function() { +# this.Given(/^This step is passing$/, function(callback) { callback(); }); +# }; +# module.exports = cucumberSteps; +# """ +# When I run `cucumber.js -f json` +# Then it should output this json: +# """ +# [ +# { +# "id": "some-feature", +# "name": "some feature", +# "description": "", +# "line": 1, +# "keyword": "Feature", +# "uri":"$CUCUMBER_JS_HOME/tmp/cucumber-js-sandbox/features/a.feature", +# "elements": [ +# { +# "name": "I've declaired one step which passes", +# "id": "some-feature;i've-declaired-one-step-which-passes", +# "line": 3, +# "keyword": "Scenario", +# "description": "", +# "type": "scenario", +# "steps": [ +# { +# "name": "This step is passing", +# "line": 4, +# "keyword": "Given ", +# "result": { +# "status": "passed" +# }, +# "match": { +# "location": "TODO" +# } +# } +# ] +# } +# ] +# } +# ] +# """ + +# Scenario: output JSON for a scenario with a passing step follwed by one that is pending and one that fails +# Given a file named "features/a.feature" with: +# """ +# Feature: some feature + +# Scenario: I've declaired one step which is passing, one pending and one failing. +# Given This step is passing +# And This step is pending +# And This step fails but will be skipped +# """ +# And a file named "features/step_definitions/cucumber_steps.js" with: +# """ +# var cucumberSteps = function() { +# this.Given(/^This step is passing$/, function(callback) { callback(); }); +# this.Given(/^This step is pending$/, function(callback) { callback.pending(); }); +# this.Given(/^This step fails but will be skipped$/, function(callback) { callback.fail(); }); +# }; +# module.exports = cucumberSteps; +# """ +# When I run `cucumber.js -f json` +# Then it should output this json: +# """ +# [ +# { +# "id": "some-feature", +# "name": "some feature", +# "description": "", +# "line": 1, +# "keyword": "Feature", +# "uri": "$CUCUMBER_JS_HOME/tmp/cucumber-js-sandbox/features/a.feature", +# "elements": [ +# { +# "name": "I've declaired one step which is passing, one pending and one failing.", +# "id": "some-feature;i've-declaired-one-step-which-is-passing,-one-pending-and-one-failing.", +# "line": 3, +# "keyword": "Scenario", +# "description": "", +# "type": "scenario", +# "steps": [ +# { +# "name": "This step is passing", +# "line": 4, +# "keyword": "Given ", +# "result": { +# "status": "passed" +# }, +# "match": { +# "location": "TODO" +# } +# }, +# { +# "name": "This step is pending", +# "line": 5, +# "keyword": "And ", +# "result": { +# "error_message": "TODO", +# "status": "pending" +# }, +# "match": { +# "location": "TODO" +# } +# }, +# { +# "name": "This step fails but will be skipped", +# "line": 6, +# "keyword": "And ", +# "result": { +# "status": "skipped" +# }, +# "match": { +# "location": "TODO" +# } +# } +# ] +# } +# ] +# } +# ] +# """ + +# Scenario: output JSON for a scenario with a pending step follwed by one that passes and one that fails +# Given a file named "features/a.feature" with: +# """ +# Feature: some feature + +# Scenario: I've declaired one step which is passing, one pending and one failing. +# Given This step is pending +# And This step is passing but will be skipped +# And This step fails but will be skipped +# """ +# And a file named "features/step_definitions/cucumber_steps.js" with: +# """ +# var cucumberSteps = function() { +# this.Given(/^This step is pending$/, function(callback) { callback.pending(); }); +# this.Given(/^This step is passing but will be skipped$/, function(callback) { callback(); }); +# this.Given(/^This step fails but will be skipped$/, function(callback) { callback.fail(); }); +# }; +# module.exports = cucumberSteps; +# """ +# When I run `cucumber.js -f json` +# Then it should output this json: +# """ +# [ +# { +# "id": "some-feature", +# "name": "some feature", +# "description": "", +# "line": 1, +# "keyword": "Feature", +# "uri": "$CUCUMBER_JS_HOME/tmp/cucumber-js-sandbox/features/a.feature", +# "elements": [ +# { +# "name": "I've declaired one step which is passing, one pending and one failing.", +# "id": "some-feature;i've-declaired-one-step-which-is-passing,-one-pending-and-one-failing.", +# "line": 3, +# "keyword": "Scenario", +# "description": "", +# "type": "scenario", +# "steps": [ +# { +# "name": "This step is pending", +# "line": 4, +# "keyword": "Given ", +# "result": { +# "error_message": "TODO", +# "status": "pending" +# }, +# "match": { +# "location": "TODO" +# } +# }, +# { +# "name": "This step is passing but will be skipped", +# "line": 5, +# "keyword": "And ", +# "result": { +# "status": "skipped" +# }, +# "match": { +# "location": "TODO" +# } +# }, +# { +# "name": "This step fails but will be skipped", +# "line": 6, +# "keyword": "And ", +# "result": { +# "status": "skipped" +# }, +# "match": { +# "location": "TODO" +# } +# } +# ] +# } +# ] +# } +# ] +# """ + +# Scenario: one feature, one passing scenario, one failing scenario +# Given a file named "features/a.feature" with: +# """ +# Feature: one passes one fails + +# Scenario: This one passes +# Given This step is passing +# Scenario: This one fails +# Given This step is failing +# """ +# And a file named "features/step_definitions/cucumber_steps.js" with: +# """ +# var cucumberSteps = function() { +# this.Given(/^This step is passing$/, function(callback) { callback(); }); +# this.Given(/^This step is failing$/, function(callback) { callback.fail(); }); +# }; +# module.exports = cucumberSteps; +# """ +# When I run `cucumber.js -f json` +# Then it should output this json: +# """ +# [ +# { +# "id": "one-passes one fails", +# "name": "one passes one fails", +# "description": "", +# "line": 1, +# "keyword": "Feature", +# "uri": "$CUCUMBER_JS_HOME/tmp/cucumber-js-sandbox/features/a.feature", +# "elements": [ +# { +# "name": "This one passes", +# "id": "one-passes one fails;this-one-passes", +# "line": 3, +# "keyword": "Scenario", +# "description": "", +# "type": "scenario", +# "steps": [ +# { +# "name": "This step is passing", +# "line": 4, +# "keyword": "Given ", +# "result": { +# "status": "passed" +# }, +# "match": { +# "location": "TODO" +# } +# } +# ] +# }, +# { +# "name": "This one fails", +# "id": "one-passes one fails;this-one-fails", +# "line": 5, +# "keyword": "Scenario", +# "description": "", +# "type": "scenario", +# "steps": [ +# { +# "name": "This step is failing", +# "line": 6, +# "keyword": "Given ", +# "result": { +# "error_message": "Error: Step failure\n at Function.fail ($CUCUMBER_JS_HOME/lib/cucumber/support_code/step_definition.js:41:49)\n at World. ($CUCUMBER_JS_HOME/tmp/cucumber-js-sandbox/features/step_definitions/cucumber_steps.js:3:70)\n at Object.invoke ($CUCUMBER_JS_HOME/lib/cucumber/support_code/step_definition.js:52:14)\n at Object.execute ($CUCUMBER_JS_HOME/lib/cucumber/ast/step.js:157:22)\n at Object.acceptVisitor ($CUCUMBER_JS_HOME/lib/cucumber/ast/step.js:149:12)\n at Object.executeStep ($CUCUMBER_JS_HOME/lib/cucumber/runtime/ast_tree_walker.js:170:12)\n at Object.processStep ($CUCUMBER_JS_HOME/lib/cucumber/runtime/ast_tree_walker.js:165:14)\n at $CUCUMBER_JS_HOME/lib/cucumber/runtime/ast_tree_walker.js:67:16\n at callUserFunctionAndBroadcastAfterEvent ($CUCUMBER_JS_HOME/lib/cucumber/runtime/ast_tree_walker.js:91:9)\n at iterate ($CUCUMBER_JS_HOME/lib/cucumber/type/collection.js:14:11)", +# "status": "failed" +# }, +# "match": { +# "location": "TODO" +# } +# } +# ] +# } +# ] +# } +# ] +# """ +# Scenario: output JSON for multiple features +# Given a file named "features/a.feature" with: +# """ +# Feature: feature a + +# Scenario: This is the first feature +# Given This step is passing +# """ +# And a file named "features/b.feature" with: +# """ +# Feature: feature b + +# Scenario: This is the second feature +# Given This step is passing +# """ +# And a file named "features/c.feature" with: +# """ +# Feature: feature c + +# Scenario: This is the third feature +# Given This step is passing +# """ +# And a file named "features/step_definitions/cucumber_steps.js" with: +# """ +# var cucumberSteps = function() { +# this.Given(/^This step is passing$/, function(callback) { callback(); }); +# }; +# module.exports = cucumberSteps; +# """ +# When I run `cucumber.js -f json` +# Then it should output this json: +# """ +# [ +# { +# "id": "feature-a", +# "name": "feature a", +# "description": "", +# "line": 1, +# "keyword": "Feature", +# "uri": "$CUCUMBER_JS_HOME/tmp/cucumber-js-sandbox/features/a.feature", +# "elements": [ +# { +# "name": "This is the first feature", +# "id": "feature-a;this-is-the-first-feature", +# "line": 3, +# "keyword": "Scenario", +# "description": "", +# "type": "scenario", +# "steps": [ +# { +# "name": "This step is passing", +# "line": 4, +# "keyword": "Given ", +# "result": { +# "status": "passed" +# }, +# "match": { +# "location": "TODO" +# } +# } +# ] +# } +# ] +# }, +# { +# "id": "feature-b", +# "name": "feature b", +# "description": "", +# "line": 1, +# "keyword": "Feature", +# "uri": "$CUCUMBER_JS_HOME/tmp/cucumber-js-sandbox/features/b.feature", +# "elements": [ +# { +# "name": "This is the second feature", +# "id": "feature-b;this-is-the-second-feature", +# "line": 3, +# "keyword": "Scenario", +# "description": "", +# "type": "scenario", +# "steps": [ +# { +# "name": "This step is passing", +# "line": 4, +# "keyword": "Given ", +# "result": { +# "status": "passed" +# }, +# "match": { +# "location": "TODO" +# } +# } +# ] +# } +# ] +# }, +# { +# "id": "feature-c", +# "name": "feature c", +# "description": "", +# "line": 1, +# "keyword": "Feature", +# "uri": "$CUCUMBER_JS_HOME/tmp/cucumber-js-sandbox/features/c.feature", +# "elements": [ +# { +# "name": "This is the third feature", +# "id": "feature-c;this-is-the-third-feature", +# "line": 3, +# "keyword": "Scenario", +# "description": "", +# "type": "scenario", +# "steps": [ +# { +# "name": "This step is passing", +# "line": 4, +# "keyword": "Given ", +# "result": { +# "status": "passed" +# }, +# "match": { +# "location": "TODO" +# } +# } +# ] +# } +# ] +# } +# ] +# """ +# Scenario: output JSON for multiple features each with multiple scenarios +# Given a file named "features/a.feature" with: +# """ +# Feature: feature a + +# Scenario: This is the feature a scenario one +# Given This step is passing + +# Scenario: This is the feature a scenario two +# Given This step is passing + +# Scenario: This is the feature a scenario three +# Given This step is passing +# """ +# And a file named "features/b.feature" with: +# """ +# Feature: feature b + +# Scenario: This is the feature b scenario one +# Given This step is passing + +# Scenario: This is the feature b scenario two +# Given This step is passing + +# Scenario: This is the feature b scenario three +# Given This step is passing +# """ +# And a file named "features/c.feature" with: +# """ +# Feature: feature c + +# Scenario: This is the feature c scenario one +# Given This step is passing + +# Scenario: This is the feature c scenario two +# Given This step is passing + +# Scenario: This is the feature c scenario three +# Given This step is passing +# """ +# And a file named "features/step_definitions/cucumber_steps.js" with: +# """ +# var cucumberSteps = function() { +# this.Given(/^This step is passing$/, function(callback) { callback(); }); +# }; +# module.exports = cucumberSteps; +# """ +# When I run `cucumber.js -f json` +# Then it should output this json: +# """ +# [ +# { +# "id": "feature-a", +# "name": "feature a", +# "description": "", +# "line": 1, +# "keyword": "Feature", +# "uri": "$CUCUMBER_JS_HOME/tmp/cucumber-js-sandbox/features/a.feature", +# "elements": [ +# { +# "name": "This is the feature a scenario one", +# "id": "feature-a;this-is-the-feature-a-scenario-one", +# "line": 3, +# "keyword": "Scenario", +# "description": "", +# "type": "scenario", +# "steps": [ +# { +# "name": "This step is passing", +# "line": 4, +# "keyword": "Given ", +# "result": { +# "status": "passed" +# }, +# "match": { +# "location": "TODO" +# } +# } +# ] +# }, +# { +# "name": "This is the feature a scenario two", +# "id": "feature-a;this-is-the-feature-a-scenario-two", +# "line": 6, +# "keyword": "Scenario", +# "description": "", +# "type": "scenario", +# "steps": [ +# { +# "name": "This step is passing", +# "line": 7, +# "keyword": "Given ", +# "result": { +# "status": "passed" +# }, +# "match": { +# "location": "TODO" +# } +# } +# ] +# }, +# { +# "name": "This is the feature a scenario three", +# "id": "feature-a;this-is-the-feature-a-scenario-three", +# "line": 9, +# "keyword": "Scenario", +# "description": "", +# "type": "scenario", +# "steps": [ +# { +# "name": "This step is passing", +# "line": 10, +# "keyword": "Given ", +# "result": { +# "status": "passed" +# }, +# "match": { +# "location": "TODO" +# } +# } +# ] +# } +# ] +# }, +# { +# "id": "feature-b", +# "name": "feature b", +# "description": "", +# "line": 1, +# "keyword": "Feature", +# "uri": "$CUCUMBER_JS_HOME/tmp/cucumber-js-sandbox/features/b.feature", +# "elements": [ +# { +# "name": "This is the feature b scenario one", +# "id": "feature-b;this-is-the-feature-b-scenario-one", +# "line": 3, +# "keyword": "Scenario", +# "description": "", +# "type": "scenario", +# "steps": [ +# { +# "name": "This step is passing", +# "line": 4, +# "keyword": "Given ", +# "result": { +# "status": "passed" +# }, +# "match": { +# "location": "TODO" +# } +# } +# ] +# }, +# { +# "name": "This is the feature b scenario two", +# "id": "feature-b;this-is-the-feature-b-scenario-two", +# "line": 6, +# "keyword": "Scenario", +# "description": "", +# "type": "scenario", +# "steps": [ +# { +# "name": "This step is passing", +# "line": 7, +# "keyword": "Given ", +# "result": { +# "status": "passed" +# }, +# "match": { +# "location": "TODO" +# } +# } +# ] +# }, +# { +# "name": "This is the feature b scenario three", +# "id": "feature-b;this-is-the-feature-b-scenario-three", +# "line": 9, +# "keyword": "Scenario", +# "description": "", +# "type": "scenario", +# "steps": [ +# { +# "name": "This step is passing", +# "line": 10, +# "keyword": "Given ", +# "result": { +# "status": "passed" +# }, +# "match": { +# "location": "TODO" +# } +# } +# ] +# } +# ] +# }, +# { +# "id": "feature-c", +# "name": "feature c", +# "description": "", +# "line": 1, +# "keyword": "Feature", +# "uri": "$CUCUMBER_JS_HOME/tmp/cucumber-js-sandbox/features/c.feature", +# "elements": [ +# { +# "name": "This is the feature c scenario one", +# "id": "feature-c;this-is-the-feature-c-scenario-one", +# "line": 3, +# "keyword": "Scenario", +# "description": "", +# "type": "scenario", +# "steps": [ +# { +# "name": "This step is passing", +# "line": 4, +# "keyword": "Given ", +# "result": { +# "status": "passed" +# }, +# "match": { +# "location": "TODO" +# } +# } +# ] +# }, +# { +# "name": "This is the feature c scenario two", +# "id": "feature-c;this-is-the-feature-c-scenario-two", +# "line": 6, +# "keyword": "Scenario", +# "description": "", +# "type": "scenario", +# "steps": [ +# { +# "name": "This step is passing", +# "line": 7, +# "keyword": "Given ", +# "result": { +# "status": "passed" +# }, +# "match": { +# "location": "TODO" +# } +# } +# ] +# }, +# { +# "name": "This is the feature c scenario three", +# "id": "feature-c;this-is-the-feature-c-scenario-three", +# "line": 9, +# "keyword": "Scenario", +# "description": "", +# "type": "scenario", +# "steps": [ +# { +# "name": "This step is passing", +# "line": 10, +# "keyword": "Given ", +# "result": { +# "status": "passed" +# }, +# "match": { +# "location": "TODO" +# } +# } +# ] +# } +# ] +# } +# ] +# """ + +# # Embedings? + +# # DocString? diff --git a/lib/cucumber/cli.js b/lib/cucumber/cli.js index 482154850..4799a703f 100644 --- a/lib/cucumber/cli.js +++ b/lib/cucumber/cli.js @@ -32,9 +32,6 @@ var Cli = function(argv) { Files under directories named \"support\" are always\n\ loaded first.\n\ \n\ --f, --format FORMAT How to format features (Default: json). Available formats:\n\ - json : Prints the feature as JSON\n\ -\n\ -t, --tags TAG_EXPRESSION Only execute the features or scenarios with tags\n\ matching TAG_EXPRESSION. Scenarios inherit tags\n\ declared on the Feature level. The simplest\n\ @@ -64,6 +61,7 @@ var Cli = function(argv) { Available formats:\n\ pretty : prints the feature as is\n\ progress : prints one character per scenario\n\ + json : Prints the feature as JSON\n\ \n\ -v, --version Display Cucumber.js's version.\n\ \n\ diff --git a/lib/cucumber/cli/argument_parser.js b/lib/cucumber/cli/argument_parser.js index d0c32c6a8..e9c283b79 100644 --- a/lib/cucumber/cli/argument_parser.js +++ b/lib/cucumber/cli/argument_parser.js @@ -69,7 +69,6 @@ var ArgumentParser = function(argv) { definitions[ArgumentParser.FORMAT_OPTION_NAME] = String; definitions[ArgumentParser.HELP_FLAG_NAME] = Boolean; definitions[ArgumentParser.VERSION_FLAG_NAME] = Boolean; - definitions[ArgumentParser.FORMAT_OPTION_NAME] = String; return definitions; }, @@ -99,11 +98,6 @@ var ArgumentParser = function(argv) { return options; }, - getFormat: function getFormat() { - var format = self.getOptionOrDefault(ArgumentParser.FORMAT_OPTION_NAME, "progress"); // TODO: Remove magic word - return format; - }, - getOptionOrDefault: function getOptionOrDefault(optionName, defaultValue) { var options = self.getOptions(); var optionValue = options[optionName]; @@ -120,11 +114,9 @@ ArgumentParser.REQUIRE_OPTION_NAME = "require"; ArgumentParser.REQUIRE_OPTION_SHORT_NAME = "r"; ArgumentParser.FORMAT_OPTION_NAME = "format"; ArgumentParser.FORMAT_OPTION_SHORT_NAME = "f"; +ArgumentParser.DEFAULT_FORMAT_VALUE = "progress"; ArgumentParser.TAGS_OPTION_NAME = "tags"; ArgumentParser.TAGS_OPTION_SHORT_NAME = "t"; -ArgumentParser.FORMAT_OPTION_NAME = "format"; -ArgumentParser.FORMAT_OPTION_SHORT_NAME = "f"; -ArgumentParser.DEFAULT_FORMAT_VALUE = "progress"; ArgumentParser.HELP_FLAG_NAME = "help"; ArgumentParser.HELP_FLAG_SHORT_NAME = "h"; ArgumentParser.DEFAULT_HELP_FLAG_VALUE = false; diff --git a/lib/cucumber/cli/configuration.js b/lib/cucumber/cli/configuration.js index 1a08473f3..5a797dbf2 100644 --- a/lib/cucumber/cli/configuration.js +++ b/lib/cucumber/cli/configuration.js @@ -15,6 +15,9 @@ var Configuration = function(argv) { case Configuration.PRETTY_FORMAT_NAME: formatter = Cucumber.Listener.PrettyFormatter(); break; + case Configuration.JSON_FORMAT_NAME: + formatter = Cucumber.Listener.JsonFormatterWrapper(); + break; default: throw new Error("Unknown formatter name \"" + format + "\"."); } @@ -59,26 +62,13 @@ var Configuration = function(argv) { isVersionRequested: function isVersionRequested() { var isVersionRequested = argumentParser.isVersionRequested(); return isVersionRequested; - }, - - getFormatter: function getFormatter() { - var formatter, - formatterOption = argumentParser.getFormat(); - - switch(formatterOption) { - case "json": - formatter = Cucumber.Listener.JsonFormatter(); - break; - case "progress": - default: - formatter = Cucumber.Listener.ProgressFormatter(); - } - return formatter; - }, + } }; return self; }; Configuration.PRETTY_FORMAT_NAME = "pretty"; Configuration.PROGRESS_FORMAT_NAME = "progress"; +Configuration.JSON_FORMAT_NAME = "json"; + module.exports = Configuration; diff --git a/lib/cucumber/listener.js b/lib/cucumber/listener.js index a0872ae4a..76f36b9df 100644 --- a/lib/cucumber/listener.js +++ b/lib/cucumber/listener.js @@ -36,7 +36,7 @@ Listener.EVENT_HANDLER_NAME_SUFFIX = 'Event'; Listener.Formatter = require('./listener/formatter'); Listener.PrettyFormatter = require('./listener/pretty_formatter'); Listener.ProgressFormatter = require('./listener/progress_formatter'); -Listener.JsonFormatter = require('./listener/json_formatter'); +Listener.JsonFormatterWrapper = require('./listener/json_formatter_wrapper'); Listener.StatsJournal = require('./listener/stats_journal'); Listener.Summarizer = require('./listener/summarizer'); module.exports = Listener; diff --git a/lib/cucumber/listener/json_formatter.js b/lib/cucumber/listener/json_formatter.js deleted file mode 100644 index ef2dc9c19..000000000 --- a/lib/cucumber/listener/json_formatter.js +++ /dev/null @@ -1,168 +0,0 @@ -var JsonFormatter = function(options) { - - var currentFeatureId = ''; - var currentFeatureIndex = null; - - var output = []; - - var self = { - - getOutput: function getOutput() { - return output; - }, - - getCurrentFeatureIndex: function getCurrentFeatureIndex() { - return currentFeatureIndex; - }, - - hear: function hear(event, callback) { - - if (self.hasHandlerForEvent(event)) { - var handler = self.getHandlerForEvent(event); - handler(event, callback); - } else { - callback(); - } - }, - - hasHandlerForEvent: function hasHandlerForEvent(event) { - var handlerName = self.buildHandlerNameForEvent(event); - return self[handlerName] != undefined; - }, - - buildHandlerNameForEvent: function buildHandlerNameForEvent(event) { - var handlerName = - JsonFormatter.EVENT_HANDLER_NAME_PREFIX + - event.getName() + - JsonFormatter.EVENT_HANDLER_NAME_SUFFIX; - return handlerName; - }, - - getHandlerForEvent: function getHandlerForEvent(event) { - var eventHandlerName = self.buildHandlerNameForEvent(event); - return self[eventHandlerName]; - }, - - handleBeforeFeatureEvent: function handleBeforeFeatureEvent(event, callback) { - - var feature = event.getPayloadItem('feature'); - currentFeatureId = feature.getName().replace(' ','-'); - if (currentFeatureIndex == null) { - currentFeatureIndex = 0; - } else { - currentFeatureIndex+=1; - } - - var element = { - id: currentFeatureId, - name: feature.getName(), - description: feature.getDescription(), - line: feature.getLine(), - keyword: feature.getKeyword(), - uri: feature.getUri() - } - output.push(element); - - callback(); - }, - - - handleBeforeScenarioEvent: function handleBeforeScenarioEvent(event, callback) { - - var scenario = event.getPayloadItem('scenario'); - - var id = currentFeatureId + ';' + scenario.getName().replace(/ /g, '-').toLowerCase(); - - var scenarioOutput = {}; - scenarioOutput["name"]= scenario.getName(); - scenarioOutput["id"]= id; - scenarioOutput["line"]= scenario.getLine(); - scenarioOutput["keyword"]= scenario.getKeyword(); - scenarioOutput["description"]= scenario.getDescription(); - scenarioOutput["type"]= "scenario"; - - if (!("elements" in output[currentFeatureIndex])) { - output[currentFeatureIndex]["elements"] = []; - } - - output[currentFeatureIndex]["elements"].push(scenarioOutput); - - callback(); - }, - - handleStepResultEvent: function handleStepResult(event, callback) { - var stepResult = event.getPayloadItem('stepResult'); - - if (output.length == 0) { - // There is no parent scenario so create an empty element to contain the step - var parent_element = {}; - output.push(parent_element); - } - - if (currentFeatureIndex == null) { - currentFeatureIndex = 0; - } - - if (!("elements" in output[currentFeatureIndex])) { - var element = {steps: []}; - output[currentFeatureIndex]["elements"] = []; //TODO: Make Dry - output[currentFeatureIndex]["elements"].push(element); - } - - var element = output[currentFeatureIndex]["elements"][output[currentFeatureIndex]["elements"].length-1] - - if (!element["steps"]) { - element["steps"] = []; - } - - var step = stepResult.getStep(); - - var stepOutput = {}; - stepOutput["name"] = step.getName(); - stepOutput["line"] = step.getLine(); - stepOutput["keyword"] = step.getKeyword(); - - stepOutput["result"] = {} - - var resultStatus = "failed"; - - if (stepResult.isSuccessful()) { - resultStatus = "passed"; - } - else if (stepResult.isPending()) { - resultStatus = "pending"; - stepOutput["result"]["error_message"] = 'TODO'; - } - else if (stepResult.isSkipped()) { - resultStatus = "skipped"; - } - else if (stepResult.isUndefined()) { - resultStatus = "undefined"; - } - else { - var failureMessage = stepResult.getFailureException(); - stepOutput["result"]["error_message"] = (failureMessage.stack || failureMessage); - } - - stepOutput["result"]["status"] = resultStatus - stepOutput["match"] = {location:"TODO"} - element["steps"].push(stepOutput); - - callback(); - }, - - handleAfterFeaturesEvent: function handleAfterFeaturesEvent(event, callback) { - // TODO: Factor this out and get rid of magic number - process.stdout.write(JSON.stringify(output, null, 2)); - callback(); - } - - }; - return self; -}; - -// TODO: Factor out to make common to all handlers -JsonFormatter.EVENT_HANDLER_NAME_PREFIX = 'handle'; -JsonFormatter.EVENT_HANDLER_NAME_SUFFIX = 'Event'; - -module.exports = JsonFormatter; diff --git a/spec/cucumber/listener/json_formatter_spec.js b/spec/cucumber/listener/json_formatter_spec.js deleted file mode 100644 index 3419f25d1..000000000 --- a/spec/cucumber/listener/json_formatter_spec.js +++ /dev/null @@ -1,401 +0,0 @@ -require('../../support/spec_helper'); - -describe("Cucumber.Listener.JsonFormatter", function() { - var Cucumber = requireLib('cucumber'); - var listener, failedStepResults; - - beforeEach(function() { - listener = Cucumber.Listener.JsonFormatter(); - }); - - describe("constructor", function() { - }); - - // Get Event - - describe("hear()", function() { - var event, callback; - var eventHandler; - - beforeEach(function() { - event = createSpy("Event"); - callback = createSpy("Callback"); - spyOn(listener, 'hasHandlerForEvent'); - spyOn(listener, 'getHandlerForEvent'); - }); - - it("checks wether there is a handler for the event", function() { - listener.hear(event, callback); - expect(listener.hasHandlerForEvent).toHaveBeenCalledWith(event); - }); - - describe("when there is a handler for that event", function() { - beforeEach(function() { - eventHandler = createSpy("Event handler (function)"); - listener.hasHandlerForEvent.andReturn(true); - listener.getHandlerForEvent.andReturn(eventHandler); - }); - - it("gets the handler for that event", function() { - listener.hear(event, callback); - expect(listener.getHandlerForEvent).toHaveBeenCalledWith(event); - }); - - it("calls the handler with the event and the callback", function() { - listener.hear(event, callback); - expect(eventHandler).toHaveBeenCalledWith(event, callback); - }); - - it("does not callback", function() { - listener.hear(event, callback); - expect(callback).not.toHaveBeenCalled(); - }); - }); - - describe("when there are no handlers for that event", function() { - beforeEach(function() { - listener.hasHandlerForEvent.andReturn(false); - }); - - it("calls back", function() { - listener.hear(event, callback); - expect(callback).toHaveBeenCalled(); - }); - - it("does not get the handler for the event", function() { - listener.hear(event, callback); - expect(listener.getHandlerForEvent).not.toHaveBeenCalled(); - }); - }); - }); - - describe("hasHandlerForEvent", function() { - var event, eventHandlerName, eventHandler; - - beforeEach(function() { - event = createSpy("Event"); - eventHandlerName = createSpy("event handler name"); - spyOn(listener, 'buildHandlerNameForEvent').andReturn(eventHandlerName); - }); - - it("builds the name of the handler for that event", function() { - listener.hasHandlerForEvent(event); - expect(listener.buildHandlerNameForEvent).toHaveBeenCalledWith(event); - }); - - describe("when the handler exists", function() { - beforeEach(function() { - eventHandler = createSpy("event handler"); - listener[eventHandlerName] = eventHandler; - }); - - it("returns true", function() { - expect(listener.hasHandlerForEvent(event)).toBeTruthy(); - }); - }); - - describe("when the handler does not exist", function() { - it("returns false", function() { - expect(listener.hasHandlerForEvent(event)).toBeFalsy(); - }); - }); - }); - - describe("buildHandlerNameForEvent", function() { - var event, eventName; - - beforeEach(function() { - eventName = "SomeEventName"; - event = createSpyWithStubs("Event", {getName: eventName}); - }); - - it("gets the name of the event", function() { - listener.buildHandlerNameForEvent(event); - expect(event.getName).toHaveBeenCalled(); - }); - - it("returns the name of the event with prefix 'handle' and suffix 'Event'", function() { - expect(listener.buildHandlerNameForEvent(event)).toBe("handle" + eventName + "Event"); - }); - }); - - describe("getHandlerForEvent()", function() { - var event; - var eventHandlerName, eventHandler; - - beforeEach(function() { - event = createSpy("event"); - eventHandlerName = 'handleSomeEvent'; - eventHandler = createSpy("event handler"); - spyOn(listener, 'buildHandlerNameForEvent').andReturn(eventHandlerName); - }); - - it("gets the name of the handler for the event", function() { - listener.getHandlerForEvent(event); - expect(listener.buildHandlerNameForEvent).toHaveBeenCalledWith(event); - }); - - describe("when an event handler exists for the event", function() { - beforeEach(function() { - listener[eventHandlerName] = eventHandler; - }); - - it("returns the event handler", function() { - expect(listener.getHandlerForEvent(event)).toBe(eventHandler); - }); - }); - - describe("when no event handlers exist for the event", function() { - it("returns nothing", function() { - expect(listener.getHandlerForEvent(event)).toBeUndefined(); - }); - }); - }); - - // Handle Feature - - describe("handleBeforeFeatureEvent()", function() { - var scenario, event, callback; - - beforeEach(function() { - feature = createSpyWithStubs("feature", - {getKeyword: 'Feature', - getName: 'A Name', - getDescription: 'A Description', - getLine: 3, - getUri: 'TODO'}); - - event = createSpyWithStubs("event", {getPayloadItem: feature}); - - callback = createSpy("callback"); - }); - - it("adds the feature attributes to the output", function() { - listener.handleBeforeFeatureEvent(event, callback); - var output = listener.getOutput(); - var expectedOutput = '[ { "id": "A-Name", \ - "name": "A Name", \ - "description": "A Description", \ - "line": 3, \ - "keyword": "Feature", \ - "uri": "TODO" } ]'; - - var expectedJson = JSON.parse(expectedOutput); - var expectedJsonString = JSON.stringify(expectedJson, null, 2); - var actualJsonString = JSON.stringify(output, null, 2); - - expect(actualJsonString).toEqual(expectedJsonString); - - }); - - it("it keeps track of the current feature so we can add scenarios and steps to it", function() { - listener.handleBeforeFeatureEvent(event, callback); - expect(listener.getCurrentFeatureIndex()).toEqual(0); - listener.handleBeforeFeatureEvent(event, callback); - expect(listener.getCurrentFeatureIndex()).toEqual(1); - listener.handleBeforeFeatureEvent(event, callback); - expect(listener.getCurrentFeatureIndex()).toEqual(2); - }); - - }); - - // Handle Scenario - - describe("handleBeforeScenarioEvent()", function() { - - }); - - // Handle Step Results - - describe("handleStepResultEvent()", function() { - var event, callback, stepResult; - - beforeEach(function() { - - step = createSpyWithStubs("step", { - getName: 'Step', - getLine: 3, - getKeyword: 'Step' - }); - - stepResult = createSpyWithStubs("step result", { - isSuccessful: undefined, - isPending: undefined, - isFailed: undefined, - isSkipped: undefined, - isUndefined: undefined, - getStep: step - }); - - event = createSpyWithStubs("event", {getPayloadItem: stepResult}); - callback = createSpy("Callback"); - }); - - - it("adds the step result to the parent scenario in the output", function(){ - stepResult.isSuccessful.andReturn(true); - listener.handleStepResultEvent(event, callback); - output = listener.getOutput(); - - var expectedOutput = '[ \ - { \ - "elements": [ \ - { \ - "steps": [ \ - { \ - "name": "Step", \ - "line": 3, \ - "keyword": "Step", \ - "result": { \ - "status": "passed" \ - }, \ - "match": { \ - "location": "TODO" \ - } \ - } \ - ] \ - } \ - ] \ - } \ - ]'; - - var expectedJson = JSON.parse(expectedOutput); - var actualJsonString = JSON.stringify(output, null, 2); - var expectedJsonString = JSON.stringify(expectedJson, null, 2); - - expect(actualJsonString).toEqual(expectedJsonString); - - }); - - it("adds the step result to parent when there are multiple features in the output", function(){ - - stepResult.isSuccessful.andReturn(true); - - feature = createSpyWithStubs("feature", - {getKeyword: 'Feature', - getName: 'A Name', - getDescription: 'A Description', - getLine: 3, - getUri: 'TODO'}); - - feature_event = createSpyWithStubs("event", {getPayloadItem: feature}); - - listener.handleBeforeFeatureEvent(feature_event, callback); - listener.handleStepResultEvent(event, callback); - - listener.handleBeforeFeatureEvent(feature_event, callback); - listener.handleStepResultEvent(event, callback); - - listener.handleBeforeFeatureEvent(feature_event, callback); - listener.handleStepResultEvent(event, callback); - - // console.log(listener.getOutput()); - }); - - - it("gets the step result from the event payload", function() { - }); - - it("checks wether the step was successful or not", function() { - }); - - describe("when the step passed", function() { - }); - - describe("when the step did not pass", function() { - beforeEach(function() { - }); - - it("does not handle a successful step result", function() { - }); - - it("checks wether the step is pending", function() { - }); - - describe("when the step was pending", function() { - beforeEach(function() { - }); - - it("handles the pending step result", function() { - }); - }); - - describe("when the step was not pending", function() { - beforeEach(function() { - }); - - it("does not handle a pending step result", function() { - }); - - it("checks wether the step was skipped", function() { - }); - - describe("when the step was skipped", function() { - beforeEach(function() { - }); - - it("handles the skipped step result", function() { - }); - }); - - describe("when the step was not skipped", function() { - beforeEach(function() { - }); - - it("does not handle a skipped step result", function() { - }); - - it("checks wether the step was undefined", function() { - }); - - describe("when the step was undefined", function() { - beforeEach(function() { - }); - - it("handles the undefined step result", function() { - }); - }); - - describe("when the step was not undefined", function() { - beforeEach(function() { - }); - - it("does not handle a skipped step result", function() { - }); - - it("handles a failed step result", function() { - }); - }); - }); - }); - }); - - it("calls back", function() { - }); - }); - - // We're all done. Output the JSON. - - describe("handleAfterFeaturesEvent()", function() { - var features, callback; - - beforeEach(function() { - event = createSpy("Event"); - callback = createSpy("Callback"); - spyOn(process.stdout, 'write'); // prevent actual output during spec execution - }); - - it("writes to stdout", function() { - listener.handleAfterFeaturesEvent(event, callback); - expect(process.stdout.write).toHaveBeenCalled(); //TODO: With anything? - }); - - it("calls back", function() { - listener.handleAfterFeaturesEvent(event, callback); - expect(callback).toHaveBeenCalled(); - }); - - }); - -}); - From e0cf8a62a34c5d64d7735128d2b9e8a79041ea1f Mon Sep 17 00:00:00 2001 From: Chris Young Date: Mon, 25 Jun 2012 23:21:28 +0100 Subject: [PATCH 20/44] Adding renamed classes which now wrap the Gherkin JSON formatter --- .../listener/json_formatter_wrapper.js | 121 +++++ .../listener/json_formatter_wrapper_spec.js | 434 ++++++++++++++++++ 2 files changed, 555 insertions(+) create mode 100644 lib/cucumber/listener/json_formatter_wrapper.js create mode 100644 spec/cucumber/listener/json_formatter_wrapper_spec.js diff --git a/lib/cucumber/listener/json_formatter_wrapper.js b/lib/cucumber/listener/json_formatter_wrapper.js new file mode 100644 index 000000000..b8279fc9b --- /dev/null +++ b/lib/cucumber/listener/json_formatter_wrapper.js @@ -0,0 +1,121 @@ +var JsonFormatterWrapper = function(io) { + + // This seems a pretty ugly way refering to the gherkin json_formatter. QUESTION: Is there a more direct way of refering to it? + // Does Gherkin have to export it in gherkin.js? + var JSONFormatter = require('../../../node_modules/gherkin/lib/gherkin/formatter/json_formatter.js'); + + var formatter = new JSONFormatter(process.stdout); // HACK!!! io); + + var currentFeatureId = ''; + + var self = { + + hear: function hear(event, callback) { + + if (self.hasHandlerForEvent(event)) { + var handler = self.getHandlerForEvent(event); + handler(event, callback); + } else { + callback(); + } + }, + + hasHandlerForEvent: function hasHandlerForEvent(event) { + var handlerName = self.buildHandlerNameForEvent(event); + return self[handlerName] != undefined; + }, + + buildHandlerNameForEvent: function buildHandlerNameForEvent(event) { + var handlerName = + JsonFormatterWrapper.EVENT_HANDLER_NAME_PREFIX + + event.getName() + + JsonFormatterWrapper.EVENT_HANDLER_NAME_SUFFIX; + return handlerName; + }, + + getHandlerForEvent: function getHandlerForEvent(event) { + var eventHandlerName = self.buildHandlerNameForEvent(event); + return self[eventHandlerName]; + }, + + handleBeforeFeatureEvent: function handleBeforeFeatureEvent(event, callback) { + + var feature = event.getPayloadItem('feature'); + currentFeatureId = feature.getName().replace(' ','-'); + + formatter.uri(feature.getUri()); + + formatter.feature({id: currentFeatureId, + name: feature.getName(), + description: feature.getDescription(), + line: feature.getLine(), + keyword: feature.getKeyword()}); + + callback(); + }, + + handleBeforeScenarioEvent: function handleBeforeScenarioEvent(event, callback) { + + var scenario = event.getPayloadItem('scenario'); + + var id = currentFeatureId + ';' + scenario.getName().replace(/ /g, '-').toLowerCase(); + + formatter.scenario({keyword: "Scenario", name: scenario.getName(), description: scenario.getDescription(), line: scenario.getLine(), id: id}); + + callback(); + }, + + handleStepResultEvent: function handleStepResult(event, callback) { + var stepResult = event.getPayloadItem('stepResult'); + + var step = stepResult.getStep(); + + formatter.step({keyword: step.getKeyword(), name: step.getName(), line: step.getLine()}); + formatter.match({location: "TODO"}); + + var stepOutput = {}; + var resultStatus = "failed"; + + if (stepResult.isSuccessful()) { + resultStatus = "passed"; + } + else if (stepResult.isPending()) { + resultStatus = "pending"; + stepOutput["result"]["error_message"] = 'TODO'; + } + else if (stepResult.isSkipped()) { + resultStatus = "skipped"; + } + else if (stepResult.isUndefined()) { + resultStatus = "undefined"; + } + else { + var failureMessage = stepResult.getFailureException(); + stepOutput["error_message"] = (failureMessage.stack || failureMessage); + } + + stepOutput["status"] = resultStatus + + formatter.result(stepOutput); + + callback(); + }, + + handleAfterFeaturesEvent: function handleAfterFeaturesEvent(event, callback) { + + formatter.eof(); + formatter.done(); + + callback(); + } + + }; + return self; +}; + +// TODO: Factor out to make common to all handlers +JsonFormatterWrapper.EVENT_HANDLER_NAME_PREFIX = 'handle'; +JsonFormatterWrapper.EVENT_HANDLER_NAME_SUFFIX = 'Event'; + +module.exports = JsonFormatterWrapper; + diff --git a/spec/cucumber/listener/json_formatter_wrapper_spec.js b/spec/cucumber/listener/json_formatter_wrapper_spec.js new file mode 100644 index 000000000..e047c409d --- /dev/null +++ b/spec/cucumber/listener/json_formatter_wrapper_spec.js @@ -0,0 +1,434 @@ +require('../../support/spec_helper'); + +describe("Cucumber.Listener.JsonFormatterWrapper", function() { + var Cucumber = requireLib('cucumber'); + var listener, failedStepResults; + var buffer = new Buffer(1024); + + beforeEach(function() { + buffer.fill(0); + listener = Cucumber.Listener.JsonFormatterWrapper(buffer); + }); + + describe("constructor", function() { + }); + + // Get Event +/* + describe("hear()", function() { + var event, callback; + var eventHandler; + + beforeEach(function() { + event = createSpy("Event"); + callback = createSpy("Callback"); + spyOn(listener, 'hasHandlerForEvent'); + spyOn(listener, 'getHandlerForEvent'); + }); + + it("checks wether there is a handler for the event", function() { + listener.hear(event, callback); + expect(listener.hasHandlerForEvent).toHaveBeenCalledWith(event); + }); + + describe("when there is a handler for that event", function() { + beforeEach(function() { + eventHandler = createSpy("Event handler (function)"); + listener.hasHandlerForEvent.andReturn(true); + listener.getHandlerForEvent.andReturn(eventHandler); + }); + + it("gets the handler for that event", function() { + listener.hear(event, callback); + expect(listener.getHandlerForEvent).toHaveBeenCalledWith(event); + }); + + it("calls the handler with the event and the callback", function() { + listener.hear(event, callback); + expect(eventHandler).toHaveBeenCalledWith(event, callback); + }); + + it("does not callback", function() { + listener.hear(event, callback); + expect(callback).not.toHaveBeenCalled(); + }); + }); + + describe("when there are no handlers for that event", function() { + beforeEach(function() { + listener.hasHandlerForEvent.andReturn(false); + }); + + it("calls back", function() { + listener.hear(event, callback); + expect(callback).toHaveBeenCalled(); + }); + + it("does not get the handler for the event", function() { + listener.hear(event, callback); + expect(listener.getHandlerForEvent).not.toHaveBeenCalled(); + }); + }); + }); + + describe("hasHandlerForEvent", function() { + var event, eventHandlerName, eventHandler; + + beforeEach(function() { + event = createSpy("Event"); + eventHandlerName = createSpy("event handler name"); + spyOn(listener, 'buildHandlerNameForEvent').andReturn(eventHandlerName); + }); + + it("builds the name of the handler for that event", function() { + listener.hasHandlerForEvent(event); + expect(listener.buildHandlerNameForEvent).toHaveBeenCalledWith(event); + }); + + describe("when the handler exists", function() { + beforeEach(function() { + eventHandler = createSpy("event handler"); + listener[eventHandlerName] = eventHandler; + }); + + it("returns true", function() { + expect(listener.hasHandlerForEvent(event)).toBeTruthy(); + }); + }); + + describe("when the handler does not exist", function() { + it("returns false", function() { + expect(listener.hasHandlerForEvent(event)).toBeFalsy(); + }); + }); + }); + + describe("buildHandlerNameForEvent", function() { + var event, eventName; + + beforeEach(function() { + eventName = "SomeEventName"; + event = createSpyWithStubs("Event", {getName: eventName}); + }); + + it("gets the name of the event", function() { + listener.buildHandlerNameForEvent(event); + expect(event.getName).toHaveBeenCalled(); + }); + + it("returns the name of the event with prefix 'handle' and suffix 'Event'", function() { + expect(listener.buildHandlerNameForEvent(event)).toBe("handle" + eventName + "Event"); + }); + }); + + describe("getHandlerForEvent()", function() { + var event; + var eventHandlerName, eventHandler; + + beforeEach(function() { + event = createSpy("event"); + eventHandlerName = 'handleSomeEvent'; + eventHandler = createSpy("event handler"); + spyOn(listener, 'buildHandlerNameForEvent').andReturn(eventHandlerName); + }); + + it("gets the name of the handler for the event", function() { + listener.getHandlerForEvent(event); + expect(listener.buildHandlerNameForEvent).toHaveBeenCalledWith(event); + }); + + describe("when an event handler exists for the event", function() { + beforeEach(function() { + listener[eventHandlerName] = eventHandler; + }); + + it("returns the event handler", function() { + expect(listener.getHandlerForEvent(event)).toBe(eventHandler); + }); + }); + + describe("when no event handlers exist for the event", function() { + it("returns nothing", function() { + expect(listener.getHandlerForEvent(event)).toBeUndefined(); + }); + }); + }); +*/ + // Handle Feature + + describe("handleBeforeFeatureEvent()", function() { + var event, feature, callback; + + beforeEach(function() { + feature = createSpyWithStubs("feature", + {getKeyword: 'Feature', + getName: 'A Name', + getDescription: 'A Description', + getLine: 3, + getUri: 'TODO'}); + + event = createSpyWithStubs("event", {getPayloadItem: feature}); + + callback = createSpy("callback"); + }); + + it("adds the feature attributes to the output", function() { + listener.handleBeforeFeatureEvent(event, callback); + listener.handleAfterFeaturesEvent(event, callback); + + var output = buffer.toString(); + output = output.substr(0,output.indexOf(String.fromCharCode(0))); + + var expectedOutput = '[ { "id": "A-Name", \ + "name": "A Name", \ + "description": "A Description", \ + "line": 3, \ + "keyword": "Feature", \ + "uri": "TODO" } ]'; + + var expectedJson = JSON.parse(expectedOutput); + var expectedJsonString = JSON.stringify(expectedJson, null, 2); + var actualJson = JSON.parse(output); + var actualJsonString = JSON.stringify(actualJson, null, 2); + + expect(actualJsonString).toEqual(expectedJsonString); + + }); + + }); + + + // Handle Scenario + + describe("handleBeforeScenarioEvent()", function() { + var parent_feature_event, scenario, callback; + + beforeEach(function() { + feature = createSpyWithStubs("feature", + {getKeyword: 'Feature', + getName: 'A Name', + getDescription: 'A Description', + getLine: 3, + getUri: 'feature-uri'}); + + parent_feature_event = createSpyWithStubs("event", {getPayloadItem: feature}); + + scenario = createSpyWithStubs("scenario", + {getKeyword: 'Scenario', + getName: 'A Name', + getDescription: 'A Description', + getLine: 3}); + + event = createSpyWithStubs("event", {getPayloadItem: scenario}); + callback = createSpy("callback"); + }); + + it("adds the scenario attributes to the output", function() { + listener.handleBeforeFeatureEvent(parent_feature_event, callback); + listener.handleBeforeScenarioEvent(event, callback); + listener.handleAfterFeaturesEvent(parent_feature_event, callback); + var output = buffer.toString(); + output = output.substr(0,output.indexOf(String.fromCharCode(0))); + + var expected = '[{"id":"A-Name","name":"A Name","description":"A Description","line":3,"keyword":"Feature","uri":"feature-uri","elements":[{"keyword":"Scenario","name":"A Name","description":"A Description","line":3,"id":"A-Name;a-name"}]}]'; + + expect(output).toEqual(expected); + + }); + + }); + + // Handle Step Results + + describe("handleStepResultEvent()", function() { + var parent_feature_event, feature, parent_scenario_event, scenario, event, callback, stepResult; + + beforeEach(function() { + feature = createSpyWithStubs("feature", + {getKeyword: 'Feature', + getName: 'A Name', + getDescription: 'A Description', + getLine: 3, + getUri: 'feature-uri'}); + + parent_feature_event = createSpyWithStubs("event", {getPayloadItem: feature}); + + scenario = createSpyWithStubs("scenario", + {getKeyword: 'Scenario', + getName: 'A Name', + getDescription: 'A Description', + getLine: 3}); + + parent_scenario_event = createSpyWithStubs("event", {getPayloadItem: scenario}); + + step = createSpyWithStubs("step", { + getName: 'Step', + getLine: 3, + getKeyword: 'Step' + }); + + stepResult = createSpyWithStubs("step result", { + isSuccessful: undefined, + isPending: undefined, + isFailed: undefined, + isSkipped: undefined, + isUndefined: undefined, + getStep: step + }); + + event = createSpyWithStubs("event", {getPayloadItem: stepResult}); + callback = createSpy("Callback"); + }); + + + it("adds the step result to the parent scenario in the output", function(){ + stepResult.isSuccessful.andReturn(true); + listener.handleBeforeFeatureEvent(parent_feature_event, callback); + listener.handleBeforeScenarioEvent(parent_scenario_event, callback); + listener.handleStepResultEvent(event, callback); + listener.handleAfterFeaturesEvent(parent_feature_event, callback); + + var output = buffer.toString(); + output = output.substr(0,output.indexOf(String.fromCharCode(0))); + + var expected = '[]'; + + expect(output).toEqual(expected); + + }); + + }); + +/* + it("adds the step result to parent when there are multiple features in the output", function(){ + + stepResult.isSuccessful.andReturn(true); + + feature = createSpyWithStubs("feature", + {getKeyword: 'Feature', + getName: 'A Name', + getDescription: 'A Description', + getLine: 3, + getUri: 'TODO'}); + + feature_event = createSpyWithStubs("event", {getPayloadItem: feature}); + + listener.handleBeforeFeatureEvent(feature_event, callback); + listener.handleStepResultEvent(event, callback); + + listener.handleBeforeFeatureEvent(feature_event, callback); + listener.handleStepResultEvent(event, callback); + + listener.handleBeforeFeatureEvent(feature_event, callback); + listener.handleStepResultEvent(event, callback); + + }); + + + it("gets the step result from the event payload", function() { + }); + + it("checks wether the step was successful or not", function() { + }); + + describe("when the step passed", function() { + }); + + describe("when the step did not pass", function() { + beforeEach(function() { + }); + + it("does not handle a successful step result", function() { + }); + + it("checks wether the step is pending", function() { + }); + + describe("when the step was pending", function() { + beforeEach(function() { + }); + + it("handles the pending step result", function() { + }); + }); + + describe("when the step was not pending", function() { + beforeEach(function() { + }); + + it("does not handle a pending step result", function() { + }); + + it("checks wether the step was skipped", function() { + }); + + describe("when the step was skipped", function() { + beforeEach(function() { + }); + + it("handles the skipped step result", function() { + }); + }); + + describe("when the step was not skipped", function() { + beforeEach(function() { + }); + + it("does not handle a skipped step result", function() { + }); + + it("checks wether the step was undefined", function() { + }); + + describe("when the step was undefined", function() { + beforeEach(function() { + }); + + it("handles the undefined step result", function() { + }); + }); + + describe("when the step was not undefined", function() { + beforeEach(function() { + }); + + it("does not handle a skipped step result", function() { + }); + + it("handles a failed step result", function() { + }); + }); + }); + }); + }); + + it("calls back", function() { + }); + + }); +*/ + // We're all done. Output the JSON. + + describe("handleAfterFeaturesEvent()", function() { + var features, callback; + + beforeEach(function() { + event = createSpy("Event"); + callback = createSpy("Callback"); + + }); + + it("writes to stdout", function() { + listener.handleAfterFeaturesEvent(event, callback); + // expect(process.stdout.write).toHaveBeenCalled(); //TODO: With anything? + }); + + it("calls back", function() { + listener.handleAfterFeaturesEvent(event, callback); + expect(callback).toHaveBeenCalled(); + }); + + }); + +}); + From 95b7aba2bc203a972de118ae91658fdb21001bf7 Mon Sep 17 00:00:00 2001 From: Chris Young Date: Tue, 26 Jun 2012 18:05:02 +0100 Subject: [PATCH 21/44] Removed my now redundant json_formatter code and replaced with calls to the new Gherkin provided formatter. All features are now passing but there not all the jasmine tests. --- features/json_formatter.feature | 1755 ++++++++--------- .../listener/json_formatter_wrapper.js | 8 +- .../listener/json_formatter_wrapper_spec.js | 116 +- 3 files changed, 885 insertions(+), 994 deletions(-) diff --git a/features/json_formatter.feature b/features/json_formatter.feature index ae1482926..907ea154c 100644 --- a/features/json_formatter.feature +++ b/features/json_formatter.feature @@ -24,881 +24,880 @@ ] """ -# Scenario: output JSON for a feature with one undefined scenario -# Given a file named "features/a.feature" with: -# """ -# Feature: some feature - -# Scenario: I havn't done anything yet -# """ -# When I run `cucumber.js -f json` -# Then it should output this json: -# """ -# [ -# { -# "id":"some-feature", -# "name":"some feature", -# "description":"", -# "line":1, -# "keyword":"Feature", -# "uri":"$CUCUMBER_JS_HOME/tmp/cucumber-js-sandbox/features/a.feature", -# "elements":[ -# {"name":"I havn't done anything yet", -# "id":"some-feature;i-havn't-done-anything-yet", -# "line":3, -# "keyword":"Scenario", -# "description":"", -# "type":"scenario" -# } -# ] -# } -# ] -# """ - -# Scenario: output JSON for a feature with one scenario with one undefined step -# Given a file named "features/a.feature" with: -# """ -# Feature: some feature - -# Scenario: I've declaired one step but not yet defined it -# Given I have not defined this step -# """ -# When I run `cucumber.js -f json` -# Then it should output this json: -# """ -# [ -# { -# "id": "some-feature", -# "name": "some feature", -# "description": "", -# "line": 1, -# "keyword": "Feature", -# "uri":"$CUCUMBER_JS_HOME/tmp/cucumber-js-sandbox/features/a.feature", -# "elements": [ -# { -# "name": "I've declaired one step but not yet defined it", -# "id": "some-feature;i've-declaired-one-step-but-not-yet-defined-it", -# "line": 3, -# "keyword": "Scenario", -# "description": "", -# "type": "scenario", -# "steps":[ -# {"name":"I have not defined this step", -# "line":4, -# "keyword":"Given ", -# "result": -# {"status":"undefined" -# }, -# "match": -# {"location":"TODO" -# } -# } -# ] -# } -# ] -# } -# ] -# """ - -# Scenario: output JSON for a feature with one scenario with one pending step -# Given a file named "features/a.feature" with: -# """ -# Feature: some feature - -# Scenario: I've declaired one step which is pending -# Given This step is pending -# """ -# And a file named "features/step_definitions/cucumber_steps.js" with: -# """ -# var cucumberSteps = function() { -# this.Given(/^This step is pending$/, function(callback) { callback.pending(); }); -# }; -# module.exports = cucumberSteps; -# """ - -# When I run `cucumber.js -f json` -# Then it should output this json: -# """ -# [ -# { -# "id": "some-feature", -# "name": "some feature", -# "description": "", -# "line": 1, -# "keyword": "Feature", -# "uri":"$CUCUMBER_JS_HOME/tmp/cucumber-js-sandbox/features/a.feature", -# "elements": [ -# { -# "name": "I've declaired one step which is pending", -# "id": "some-feature;i've-declaired-one-step-which-is-pending", -# "line": 3, -# "keyword": "Scenario", -# "description": "", -# "type": "scenario", -# "steps": [ -# { -# "name": "This step is pending", -# "line": 4, -# "keyword": "Given ", -# "result": { -# "error_message": "TODO", -# "status": "pending" -# }, -# "match": { -# "location": "TODO" -# } -# } -# ] -# } -# ] -# } -# ] -# """ -# Scenario: output JSON for a feature with one scenario with failing step -# Given a file named "features/a.feature" with: -# """ -# Feature: some feature - -# Scenario: I've declaired one step but it is failing -# Given This step is failing -# """ -# And a file named "features/step_definitions/cucumber_steps.js" with: -# """ -# var cucumberSteps = function() { -# this.Given(/^This step is failing$/, function(callback) { callback.fail(); }); -# }; -# module.exports = cucumberSteps; -# """ -# When I run `cucumber.js -f json` -# Then it should output this json: -# """ -# [ -# { -# "id": "some-feature", -# "name": "some feature", -# "description": "", -# "line": 1, -# "keyword": "Feature", -# "uri":"$CUCUMBER_JS_HOME/tmp/cucumber-js-sandbox/features/a.feature", -# "elements": [ -# { -# "name": "I've declaired one step but it is failing", -# "id": "some-feature;i've-declaired-one-step-but-it-is-failing", -# "line": 3, -# "keyword": "Scenario", -# "description": "", -# "type": "scenario", -# "steps": [ -# { -# "name": "This step is failing", -# "line": 4, -# "keyword": "Given ", -# "result": { -# "error_message": "Error: Step failure\n at Function.fail ($CUCUMBER_JS_HOME/lib/cucumber/support_code/step_definition.js:41:49)\n at World. ($CUCUMBER_JS_HOME/tmp/cucumber-js-sandbox/features/step_definitions/cucumber_steps.js:2:70)\n at Object.invoke ($CUCUMBER_JS_HOME/lib/cucumber/support_code/step_definition.js:52:14)\n at Object.execute ($CUCUMBER_JS_HOME/lib/cucumber/ast/step.js:157:22)\n at Object.acceptVisitor ($CUCUMBER_JS_HOME/lib/cucumber/ast/step.js:149:12)\n at Object.executeStep ($CUCUMBER_JS_HOME/lib/cucumber/runtime/ast_tree_walker.js:170:12)\n at Object.processStep ($CUCUMBER_JS_HOME/lib/cucumber/runtime/ast_tree_walker.js:165:14)\n at $CUCUMBER_JS_HOME/lib/cucumber/runtime/ast_tree_walker.js:67:16\n at callUserFunctionAndBroadcastAfterEvent ($CUCUMBER_JS_HOME/lib/cucumber/runtime/ast_tree_walker.js:91:9)\n at iterate ($CUCUMBER_JS_HOME/lib/cucumber/type/collection.js:14:11)", -# "status": "failed" -# }, -# "match": { -# "location": "TODO" -# } -# } -# ] -# } -# ] -# } -# ] -# """ -# Scenario: output JSON for a feature with one scenario with passing step -# Given a file named "features/a.feature" with: -# """ -# Feature: some feature - -# Scenario: I've declaired one step which passes -# Given This step is passing -# """ -# And a file named "features/step_definitions/cucumber_steps.js" with: -# """ -# var cucumberSteps = function() { -# this.Given(/^This step is passing$/, function(callback) { callback(); }); -# }; -# module.exports = cucumberSteps; -# """ -# When I run `cucumber.js -f json` -# Then it should output this json: -# """ -# [ -# { -# "id": "some-feature", -# "name": "some feature", -# "description": "", -# "line": 1, -# "keyword": "Feature", -# "uri":"$CUCUMBER_JS_HOME/tmp/cucumber-js-sandbox/features/a.feature", -# "elements": [ -# { -# "name": "I've declaired one step which passes", -# "id": "some-feature;i've-declaired-one-step-which-passes", -# "line": 3, -# "keyword": "Scenario", -# "description": "", -# "type": "scenario", -# "steps": [ -# { -# "name": "This step is passing", -# "line": 4, -# "keyword": "Given ", -# "result": { -# "status": "passed" -# }, -# "match": { -# "location": "TODO" -# } -# } -# ] -# } -# ] -# } -# ] -# """ - -# Scenario: output JSON for a scenario with a passing step follwed by one that is pending and one that fails -# Given a file named "features/a.feature" with: -# """ -# Feature: some feature - -# Scenario: I've declaired one step which is passing, one pending and one failing. -# Given This step is passing -# And This step is pending -# And This step fails but will be skipped -# """ -# And a file named "features/step_definitions/cucumber_steps.js" with: -# """ -# var cucumberSteps = function() { -# this.Given(/^This step is passing$/, function(callback) { callback(); }); -# this.Given(/^This step is pending$/, function(callback) { callback.pending(); }); -# this.Given(/^This step fails but will be skipped$/, function(callback) { callback.fail(); }); -# }; -# module.exports = cucumberSteps; -# """ -# When I run `cucumber.js -f json` -# Then it should output this json: -# """ -# [ -# { -# "id": "some-feature", -# "name": "some feature", -# "description": "", -# "line": 1, -# "keyword": "Feature", -# "uri": "$CUCUMBER_JS_HOME/tmp/cucumber-js-sandbox/features/a.feature", -# "elements": [ -# { -# "name": "I've declaired one step which is passing, one pending and one failing.", -# "id": "some-feature;i've-declaired-one-step-which-is-passing,-one-pending-and-one-failing.", -# "line": 3, -# "keyword": "Scenario", -# "description": "", -# "type": "scenario", -# "steps": [ -# { -# "name": "This step is passing", -# "line": 4, -# "keyword": "Given ", -# "result": { -# "status": "passed" -# }, -# "match": { -# "location": "TODO" -# } -# }, -# { -# "name": "This step is pending", -# "line": 5, -# "keyword": "And ", -# "result": { -# "error_message": "TODO", -# "status": "pending" -# }, -# "match": { -# "location": "TODO" -# } -# }, -# { -# "name": "This step fails but will be skipped", -# "line": 6, -# "keyword": "And ", -# "result": { -# "status": "skipped" -# }, -# "match": { -# "location": "TODO" -# } -# } -# ] -# } -# ] -# } -# ] -# """ - -# Scenario: output JSON for a scenario with a pending step follwed by one that passes and one that fails -# Given a file named "features/a.feature" with: -# """ -# Feature: some feature - -# Scenario: I've declaired one step which is passing, one pending and one failing. -# Given This step is pending -# And This step is passing but will be skipped -# And This step fails but will be skipped -# """ -# And a file named "features/step_definitions/cucumber_steps.js" with: -# """ -# var cucumberSteps = function() { -# this.Given(/^This step is pending$/, function(callback) { callback.pending(); }); -# this.Given(/^This step is passing but will be skipped$/, function(callback) { callback(); }); -# this.Given(/^This step fails but will be skipped$/, function(callback) { callback.fail(); }); -# }; -# module.exports = cucumberSteps; -# """ -# When I run `cucumber.js -f json` -# Then it should output this json: -# """ -# [ -# { -# "id": "some-feature", -# "name": "some feature", -# "description": "", -# "line": 1, -# "keyword": "Feature", -# "uri": "$CUCUMBER_JS_HOME/tmp/cucumber-js-sandbox/features/a.feature", -# "elements": [ -# { -# "name": "I've declaired one step which is passing, one pending and one failing.", -# "id": "some-feature;i've-declaired-one-step-which-is-passing,-one-pending-and-one-failing.", -# "line": 3, -# "keyword": "Scenario", -# "description": "", -# "type": "scenario", -# "steps": [ -# { -# "name": "This step is pending", -# "line": 4, -# "keyword": "Given ", -# "result": { -# "error_message": "TODO", -# "status": "pending" -# }, -# "match": { -# "location": "TODO" -# } -# }, -# { -# "name": "This step is passing but will be skipped", -# "line": 5, -# "keyword": "And ", -# "result": { -# "status": "skipped" -# }, -# "match": { -# "location": "TODO" -# } -# }, -# { -# "name": "This step fails but will be skipped", -# "line": 6, -# "keyword": "And ", -# "result": { -# "status": "skipped" -# }, -# "match": { -# "location": "TODO" -# } -# } -# ] -# } -# ] -# } -# ] -# """ - -# Scenario: one feature, one passing scenario, one failing scenario -# Given a file named "features/a.feature" with: -# """ -# Feature: one passes one fails - -# Scenario: This one passes -# Given This step is passing -# Scenario: This one fails -# Given This step is failing -# """ -# And a file named "features/step_definitions/cucumber_steps.js" with: -# """ -# var cucumberSteps = function() { -# this.Given(/^This step is passing$/, function(callback) { callback(); }); -# this.Given(/^This step is failing$/, function(callback) { callback.fail(); }); -# }; -# module.exports = cucumberSteps; -# """ -# When I run `cucumber.js -f json` -# Then it should output this json: -# """ -# [ -# { -# "id": "one-passes one fails", -# "name": "one passes one fails", -# "description": "", -# "line": 1, -# "keyword": "Feature", -# "uri": "$CUCUMBER_JS_HOME/tmp/cucumber-js-sandbox/features/a.feature", -# "elements": [ -# { -# "name": "This one passes", -# "id": "one-passes one fails;this-one-passes", -# "line": 3, -# "keyword": "Scenario", -# "description": "", -# "type": "scenario", -# "steps": [ -# { -# "name": "This step is passing", -# "line": 4, -# "keyword": "Given ", -# "result": { -# "status": "passed" -# }, -# "match": { -# "location": "TODO" -# } -# } -# ] -# }, -# { -# "name": "This one fails", -# "id": "one-passes one fails;this-one-fails", -# "line": 5, -# "keyword": "Scenario", -# "description": "", -# "type": "scenario", -# "steps": [ -# { -# "name": "This step is failing", -# "line": 6, -# "keyword": "Given ", -# "result": { -# "error_message": "Error: Step failure\n at Function.fail ($CUCUMBER_JS_HOME/lib/cucumber/support_code/step_definition.js:41:49)\n at World. ($CUCUMBER_JS_HOME/tmp/cucumber-js-sandbox/features/step_definitions/cucumber_steps.js:3:70)\n at Object.invoke ($CUCUMBER_JS_HOME/lib/cucumber/support_code/step_definition.js:52:14)\n at Object.execute ($CUCUMBER_JS_HOME/lib/cucumber/ast/step.js:157:22)\n at Object.acceptVisitor ($CUCUMBER_JS_HOME/lib/cucumber/ast/step.js:149:12)\n at Object.executeStep ($CUCUMBER_JS_HOME/lib/cucumber/runtime/ast_tree_walker.js:170:12)\n at Object.processStep ($CUCUMBER_JS_HOME/lib/cucumber/runtime/ast_tree_walker.js:165:14)\n at $CUCUMBER_JS_HOME/lib/cucumber/runtime/ast_tree_walker.js:67:16\n at callUserFunctionAndBroadcastAfterEvent ($CUCUMBER_JS_HOME/lib/cucumber/runtime/ast_tree_walker.js:91:9)\n at iterate ($CUCUMBER_JS_HOME/lib/cucumber/type/collection.js:14:11)", -# "status": "failed" -# }, -# "match": { -# "location": "TODO" -# } -# } -# ] -# } -# ] -# } -# ] -# """ -# Scenario: output JSON for multiple features -# Given a file named "features/a.feature" with: -# """ -# Feature: feature a - -# Scenario: This is the first feature -# Given This step is passing -# """ -# And a file named "features/b.feature" with: -# """ -# Feature: feature b - -# Scenario: This is the second feature -# Given This step is passing -# """ -# And a file named "features/c.feature" with: -# """ -# Feature: feature c - -# Scenario: This is the third feature -# Given This step is passing -# """ -# And a file named "features/step_definitions/cucumber_steps.js" with: -# """ -# var cucumberSteps = function() { -# this.Given(/^This step is passing$/, function(callback) { callback(); }); -# }; -# module.exports = cucumberSteps; -# """ -# When I run `cucumber.js -f json` -# Then it should output this json: -# """ -# [ -# { -# "id": "feature-a", -# "name": "feature a", -# "description": "", -# "line": 1, -# "keyword": "Feature", -# "uri": "$CUCUMBER_JS_HOME/tmp/cucumber-js-sandbox/features/a.feature", -# "elements": [ -# { -# "name": "This is the first feature", -# "id": "feature-a;this-is-the-first-feature", -# "line": 3, -# "keyword": "Scenario", -# "description": "", -# "type": "scenario", -# "steps": [ -# { -# "name": "This step is passing", -# "line": 4, -# "keyword": "Given ", -# "result": { -# "status": "passed" -# }, -# "match": { -# "location": "TODO" -# } -# } -# ] -# } -# ] -# }, -# { -# "id": "feature-b", -# "name": "feature b", -# "description": "", -# "line": 1, -# "keyword": "Feature", -# "uri": "$CUCUMBER_JS_HOME/tmp/cucumber-js-sandbox/features/b.feature", -# "elements": [ -# { -# "name": "This is the second feature", -# "id": "feature-b;this-is-the-second-feature", -# "line": 3, -# "keyword": "Scenario", -# "description": "", -# "type": "scenario", -# "steps": [ -# { -# "name": "This step is passing", -# "line": 4, -# "keyword": "Given ", -# "result": { -# "status": "passed" -# }, -# "match": { -# "location": "TODO" -# } -# } -# ] -# } -# ] -# }, -# { -# "id": "feature-c", -# "name": "feature c", -# "description": "", -# "line": 1, -# "keyword": "Feature", -# "uri": "$CUCUMBER_JS_HOME/tmp/cucumber-js-sandbox/features/c.feature", -# "elements": [ -# { -# "name": "This is the third feature", -# "id": "feature-c;this-is-the-third-feature", -# "line": 3, -# "keyword": "Scenario", -# "description": "", -# "type": "scenario", -# "steps": [ -# { -# "name": "This step is passing", -# "line": 4, -# "keyword": "Given ", -# "result": { -# "status": "passed" -# }, -# "match": { -# "location": "TODO" -# } -# } -# ] -# } -# ] -# } -# ] -# """ -# Scenario: output JSON for multiple features each with multiple scenarios -# Given a file named "features/a.feature" with: -# """ -# Feature: feature a - -# Scenario: This is the feature a scenario one -# Given This step is passing - -# Scenario: This is the feature a scenario two -# Given This step is passing - -# Scenario: This is the feature a scenario three -# Given This step is passing -# """ -# And a file named "features/b.feature" with: -# """ -# Feature: feature b - -# Scenario: This is the feature b scenario one -# Given This step is passing - -# Scenario: This is the feature b scenario two -# Given This step is passing - -# Scenario: This is the feature b scenario three -# Given This step is passing -# """ -# And a file named "features/c.feature" with: -# """ -# Feature: feature c - -# Scenario: This is the feature c scenario one -# Given This step is passing - -# Scenario: This is the feature c scenario two -# Given This step is passing - -# Scenario: This is the feature c scenario three -# Given This step is passing -# """ -# And a file named "features/step_definitions/cucumber_steps.js" with: -# """ -# var cucumberSteps = function() { -# this.Given(/^This step is passing$/, function(callback) { callback(); }); -# }; -# module.exports = cucumberSteps; -# """ -# When I run `cucumber.js -f json` -# Then it should output this json: -# """ -# [ -# { -# "id": "feature-a", -# "name": "feature a", -# "description": "", -# "line": 1, -# "keyword": "Feature", -# "uri": "$CUCUMBER_JS_HOME/tmp/cucumber-js-sandbox/features/a.feature", -# "elements": [ -# { -# "name": "This is the feature a scenario one", -# "id": "feature-a;this-is-the-feature-a-scenario-one", -# "line": 3, -# "keyword": "Scenario", -# "description": "", -# "type": "scenario", -# "steps": [ -# { -# "name": "This step is passing", -# "line": 4, -# "keyword": "Given ", -# "result": { -# "status": "passed" -# }, -# "match": { -# "location": "TODO" -# } -# } -# ] -# }, -# { -# "name": "This is the feature a scenario two", -# "id": "feature-a;this-is-the-feature-a-scenario-two", -# "line": 6, -# "keyword": "Scenario", -# "description": "", -# "type": "scenario", -# "steps": [ -# { -# "name": "This step is passing", -# "line": 7, -# "keyword": "Given ", -# "result": { -# "status": "passed" -# }, -# "match": { -# "location": "TODO" -# } -# } -# ] -# }, -# { -# "name": "This is the feature a scenario three", -# "id": "feature-a;this-is-the-feature-a-scenario-three", -# "line": 9, -# "keyword": "Scenario", -# "description": "", -# "type": "scenario", -# "steps": [ -# { -# "name": "This step is passing", -# "line": 10, -# "keyword": "Given ", -# "result": { -# "status": "passed" -# }, -# "match": { -# "location": "TODO" -# } -# } -# ] -# } -# ] -# }, -# { -# "id": "feature-b", -# "name": "feature b", -# "description": "", -# "line": 1, -# "keyword": "Feature", -# "uri": "$CUCUMBER_JS_HOME/tmp/cucumber-js-sandbox/features/b.feature", -# "elements": [ -# { -# "name": "This is the feature b scenario one", -# "id": "feature-b;this-is-the-feature-b-scenario-one", -# "line": 3, -# "keyword": "Scenario", -# "description": "", -# "type": "scenario", -# "steps": [ -# { -# "name": "This step is passing", -# "line": 4, -# "keyword": "Given ", -# "result": { -# "status": "passed" -# }, -# "match": { -# "location": "TODO" -# } -# } -# ] -# }, -# { -# "name": "This is the feature b scenario two", -# "id": "feature-b;this-is-the-feature-b-scenario-two", -# "line": 6, -# "keyword": "Scenario", -# "description": "", -# "type": "scenario", -# "steps": [ -# { -# "name": "This step is passing", -# "line": 7, -# "keyword": "Given ", -# "result": { -# "status": "passed" -# }, -# "match": { -# "location": "TODO" -# } -# } -# ] -# }, -# { -# "name": "This is the feature b scenario three", -# "id": "feature-b;this-is-the-feature-b-scenario-three", -# "line": 9, -# "keyword": "Scenario", -# "description": "", -# "type": "scenario", -# "steps": [ -# { -# "name": "This step is passing", -# "line": 10, -# "keyword": "Given ", -# "result": { -# "status": "passed" -# }, -# "match": { -# "location": "TODO" -# } -# } -# ] -# } -# ] -# }, -# { -# "id": "feature-c", -# "name": "feature c", -# "description": "", -# "line": 1, -# "keyword": "Feature", -# "uri": "$CUCUMBER_JS_HOME/tmp/cucumber-js-sandbox/features/c.feature", -# "elements": [ -# { -# "name": "This is the feature c scenario one", -# "id": "feature-c;this-is-the-feature-c-scenario-one", -# "line": 3, -# "keyword": "Scenario", -# "description": "", -# "type": "scenario", -# "steps": [ -# { -# "name": "This step is passing", -# "line": 4, -# "keyword": "Given ", -# "result": { -# "status": "passed" -# }, -# "match": { -# "location": "TODO" -# } -# } -# ] -# }, -# { -# "name": "This is the feature c scenario two", -# "id": "feature-c;this-is-the-feature-c-scenario-two", -# "line": 6, -# "keyword": "Scenario", -# "description": "", -# "type": "scenario", -# "steps": [ -# { -# "name": "This step is passing", -# "line": 7, -# "keyword": "Given ", -# "result": { -# "status": "passed" -# }, -# "match": { -# "location": "TODO" -# } -# } -# ] -# }, -# { -# "name": "This is the feature c scenario three", -# "id": "feature-c;this-is-the-feature-c-scenario-three", -# "line": 9, -# "keyword": "Scenario", -# "description": "", -# "type": "scenario", -# "steps": [ -# { -# "name": "This step is passing", -# "line": 10, -# "keyword": "Given ", -# "result": { -# "status": "passed" -# }, -# "match": { -# "location": "TODO" -# } -# } -# ] -# } -# ] -# } -# ] -# """ - -# # Embedings? - -# # DocString? - + Scenario: output JSON for a feature with one undefined scenario + Given a file named "features/a.feature" with: + """ + Feature: some feature + + Scenario: I havn't done anything yet + """ + When I run `cucumber.js -f json` + Then it should output this json: + """ + [ + { + "id":"some-feature", + "name":"some feature", + "description":"", + "line":1, + "keyword":"Feature", + "uri":"$CUCUMBER_JS_HOME/tmp/cucumber-js-sandbox/features/a.feature", + "elements":[ + {"name":"I havn't done anything yet", + "id":"some-feature;i-havn't-done-anything-yet", + "line":3, + "keyword":"Scenario", + "description":"", + "type":"scenario" + } + ] + } + ] + """ + + Scenario: output JSON for a feature with one scenario with one undefined step + Given a file named "features/a.feature" with: + """ + Feature: some feature + + Scenario: I've declaired one step but not yet defined it + Given I have not defined this step + """ + When I run `cucumber.js -f json` + Then it should output this json: + """ + [ + { + "id": "some-feature", + "name": "some feature", + "description": "", + "line": 1, + "keyword": "Feature", + "uri":"$CUCUMBER_JS_HOME/tmp/cucumber-js-sandbox/features/a.feature", + "elements": [ + { + "name": "I've declaired one step but not yet defined it", + "id": "some-feature;i've-declaired-one-step-but-not-yet-defined-it", + "line": 3, + "keyword": "Scenario", + "description": "", + "type": "scenario", + "steps":[ + {"name":"I have not defined this step", + "line":4, + "keyword":"Given ", + "result": + {"status":"undefined" + }, + "match": + {"location":"TODO" + } + } + ] + } + ] + } + ] + """ + + Scenario: output JSON for a feature with one scenario with one pending step + Given a file named "features/a.feature" with: + """ + Feature: some feature + + Scenario: I've declaired one step which is pending + Given This step is pending + """ + And a file named "features/step_definitions/cucumber_steps.js" with: + """ + var cucumberSteps = function() { + this.Given(/^This step is pending$/, function(callback) { callback.pending(); }); + }; + module.exports = cucumberSteps; + """ + + When I run `cucumber.js -f json` + Then it should output this json: + """ + [ + { + "id": "some-feature", + "name": "some feature", + "description": "", + "line": 1, + "keyword": "Feature", + "uri":"$CUCUMBER_JS_HOME/tmp/cucumber-js-sandbox/features/a.feature", + "elements": [ + { + "name": "I've declaired one step which is pending", + "id": "some-feature;i've-declaired-one-step-which-is-pending", + "line": 3, + "keyword": "Scenario", + "description": "", + "type": "scenario", + "steps": [ + { + "name": "This step is pending", + "line": 4, + "keyword": "Given ", + "result": { + "error_message": "TODO", + "status": "pending" + }, + "match": { + "location": "TODO" + } + } + ] + } + ] + } + ] + """ + Scenario: output JSON for a feature with one scenario with failing step + Given a file named "features/a.feature" with: + """ + Feature: some feature + + Scenario: I've declaired one step but it is failing + Given This step is failing + """ + And a file named "features/step_definitions/cucumber_steps.js" with: + """ + var cucumberSteps = function() { + this.Given(/^This step is failing$/, function(callback) { callback.fail(); }); + }; + module.exports = cucumberSteps; + """ + When I run `cucumber.js -f json` + Then it should output this json: + """ + [ + { + "id": "some-feature", + "name": "some feature", + "description": "", + "line": 1, + "keyword": "Feature", + "uri":"$CUCUMBER_JS_HOME/tmp/cucumber-js-sandbox/features/a.feature", + "elements": [ + { + "name": "I've declaired one step but it is failing", + "id": "some-feature;i've-declaired-one-step-but-it-is-failing", + "line": 3, + "keyword": "Scenario", + "description": "", + "type": "scenario", + "steps": [ + { + "name": "This step is failing", + "line": 4, + "keyword": "Given ", + "result": { + "error_message": "Error: Step failure\n at Function.fail ($CUCUMBER_JS_HOME/lib/cucumber/support_code/step_definition.js:41:49)\n at World. ($CUCUMBER_JS_HOME/tmp/cucumber-js-sandbox/features/step_definitions/cucumber_steps.js:2:70)\n at Object.invoke ($CUCUMBER_JS_HOME/lib/cucumber/support_code/step_definition.js:52:14)\n at Object.execute ($CUCUMBER_JS_HOME/lib/cucumber/ast/step.js:157:22)\n at Object.acceptVisitor ($CUCUMBER_JS_HOME/lib/cucumber/ast/step.js:149:12)\n at Object.executeStep ($CUCUMBER_JS_HOME/lib/cucumber/runtime/ast_tree_walker.js:170:12)\n at Object.processStep ($CUCUMBER_JS_HOME/lib/cucumber/runtime/ast_tree_walker.js:165:14)\n at $CUCUMBER_JS_HOME/lib/cucumber/runtime/ast_tree_walker.js:67:16\n at callUserFunctionAndBroadcastAfterEvent ($CUCUMBER_JS_HOME/lib/cucumber/runtime/ast_tree_walker.js:91:9)\n at iterate ($CUCUMBER_JS_HOME/lib/cucumber/type/collection.js:14:11)", + "status": "failed" + }, + "match": { + "location": "TODO" + } + } + ] + } + ] + } + ] + """ + Scenario: output JSON for a feature with one scenario with passing step + Given a file named "features/a.feature" with: + """ + Feature: some feature + + Scenario: I've declaired one step which passes + Given This step is passing + """ + And a file named "features/step_definitions/cucumber_steps.js" with: + """ + var cucumberSteps = function() { + this.Given(/^This step is passing$/, function(callback) { callback(); }); + }; + module.exports = cucumberSteps; + """ + When I run `cucumber.js -f json` + Then it should output this json: + """ + [ + { + "id": "some-feature", + "name": "some feature", + "description": "", + "line": 1, + "keyword": "Feature", + "uri":"$CUCUMBER_JS_HOME/tmp/cucumber-js-sandbox/features/a.feature", + "elements": [ + { + "name": "I've declaired one step which passes", + "id": "some-feature;i've-declaired-one-step-which-passes", + "line": 3, + "keyword": "Scenario", + "description": "", + "type": "scenario", + "steps": [ + { + "name": "This step is passing", + "line": 4, + "keyword": "Given ", + "result": { + "status": "passed" + }, + "match": { + "location": "TODO" + } + } + ] + } + ] + } + ] + """ + + Scenario: output JSON for a scenario with a passing step follwed by one that is pending and one that fails + Given a file named "features/a.feature" with: + """ + Feature: some feature + + Scenario: I've declaired one step which is passing, one pending and one failing. + Given This step is passing + And This step is pending + And This step fails but will be skipped + """ + And a file named "features/step_definitions/cucumber_steps.js" with: + """ + var cucumberSteps = function() { + this.Given(/^This step is passing$/, function(callback) { callback(); }); + this.Given(/^This step is pending$/, function(callback) { callback.pending(); }); + this.Given(/^This step fails but will be skipped$/, function(callback) { callback.fail(); }); + }; + module.exports = cucumberSteps; + """ + When I run `cucumber.js -f json` + Then it should output this json: + """ + [ + { + "id": "some-feature", + "name": "some feature", + "description": "", + "line": 1, + "keyword": "Feature", + "uri": "$CUCUMBER_JS_HOME/tmp/cucumber-js-sandbox/features/a.feature", + "elements": [ + { + "name": "I've declaired one step which is passing, one pending and one failing.", + "id": "some-feature;i've-declaired-one-step-which-is-passing,-one-pending-and-one-failing.", + "line": 3, + "keyword": "Scenario", + "description": "", + "type": "scenario", + "steps": [ + { + "name": "This step is passing", + "line": 4, + "keyword": "Given ", + "result": { + "status": "passed" + }, + "match": { + "location": "TODO" + } + }, + { + "name": "This step is pending", + "line": 5, + "keyword": "And ", + "result": { + "error_message": "TODO", + "status": "pending" + }, + "match": { + "location": "TODO" + } + }, + { + "name": "This step fails but will be skipped", + "line": 6, + "keyword": "And ", + "result": { + "status": "skipped" + }, + "match": { + "location": "TODO" + } + } + ] + } + ] + } + ] + """ + + Scenario: output JSON for a scenario with a pending step follwed by one that passes and one that fails + Given a file named "features/a.feature" with: + """ + Feature: some feature + + Scenario: I've declaired one step which is passing, one pending and one failing. + Given This step is pending + And This step is passing but will be skipped + And This step fails but will be skipped + """ + And a file named "features/step_definitions/cucumber_steps.js" with: + """ + var cucumberSteps = function() { + this.Given(/^This step is pending$/, function(callback) { callback.pending(); }); + this.Given(/^This step is passing but will be skipped$/, function(callback) { callback(); }); + this.Given(/^This step fails but will be skipped$/, function(callback) { callback.fail(); }); + }; + module.exports = cucumberSteps; + """ + When I run `cucumber.js -f json` + Then it should output this json: + """ + [ + { + "id": "some-feature", + "name": "some feature", + "description": "", + "line": 1, + "keyword": "Feature", + "uri": "$CUCUMBER_JS_HOME/tmp/cucumber-js-sandbox/features/a.feature", + "elements": [ + { + "name": "I've declaired one step which is passing, one pending and one failing.", + "id": "some-feature;i've-declaired-one-step-which-is-passing,-one-pending-and-one-failing.", + "line": 3, + "keyword": "Scenario", + "description": "", + "type": "scenario", + "steps": [ + { + "name": "This step is pending", + "line": 4, + "keyword": "Given ", + "result": { + "error_message": "TODO", + "status": "pending" + }, + "match": { + "location": "TODO" + } + }, + { + "name": "This step is passing but will be skipped", + "line": 5, + "keyword": "And ", + "result": { + "status": "skipped" + }, + "match": { + "location": "TODO" + } + }, + { + "name": "This step fails but will be skipped", + "line": 6, + "keyword": "And ", + "result": { + "status": "skipped" + }, + "match": { + "location": "TODO" + } + } + ] + } + ] + } + ] + """ + +Scenario: one feature, one passing scenario, one failing scenario + Given a file named "features/a.feature" with: + """ + Feature: one passes one fails + + Scenario: This one passes + Given This step is passing + Scenario: This one fails + Given This step is failing + """ + And a file named "features/step_definitions/cucumber_steps.js" with: + """ + var cucumberSteps = function() { + this.Given(/^This step is passing$/, function(callback) { callback(); }); + this.Given(/^This step is failing$/, function(callback) { callback.fail(); }); + }; + module.exports = cucumberSteps; + """ + When I run `cucumber.js -f json` + Then it should output this json: + """ + [ + { + "id": "one-passes one fails", + "name": "one passes one fails", + "description": "", + "line": 1, + "keyword": "Feature", + "uri": "$CUCUMBER_JS_HOME/tmp/cucumber-js-sandbox/features/a.feature", + "elements": [ + { + "name": "This one passes", + "id": "one-passes one fails;this-one-passes", + "line": 3, + "keyword": "Scenario", + "description": "", + "type": "scenario", + "steps": [ + { + "name": "This step is passing", + "line": 4, + "keyword": "Given ", + "result": { + "status": "passed" + }, + "match": { + "location": "TODO" + } + } + ] + }, + { + "name": "This one fails", + "id": "one-passes one fails;this-one-fails", + "line": 5, + "keyword": "Scenario", + "description": "", + "type": "scenario", + "steps": [ + { + "name": "This step is failing", + "line": 6, + "keyword": "Given ", + "result": { + "error_message": "Error: Step failure\n at Function.fail ($CUCUMBER_JS_HOME/lib/cucumber/support_code/step_definition.js:41:49)\n at World. ($CUCUMBER_JS_HOME/tmp/cucumber-js-sandbox/features/step_definitions/cucumber_steps.js:3:70)\n at Object.invoke ($CUCUMBER_JS_HOME/lib/cucumber/support_code/step_definition.js:52:14)\n at Object.execute ($CUCUMBER_JS_HOME/lib/cucumber/ast/step.js:157:22)\n at Object.acceptVisitor ($CUCUMBER_JS_HOME/lib/cucumber/ast/step.js:149:12)\n at Object.executeStep ($CUCUMBER_JS_HOME/lib/cucumber/runtime/ast_tree_walker.js:170:12)\n at Object.processStep ($CUCUMBER_JS_HOME/lib/cucumber/runtime/ast_tree_walker.js:165:14)\n at $CUCUMBER_JS_HOME/lib/cucumber/runtime/ast_tree_walker.js:67:16\n at callUserFunctionAndBroadcastAfterEvent ($CUCUMBER_JS_HOME/lib/cucumber/runtime/ast_tree_walker.js:91:9)\n at iterate ($CUCUMBER_JS_HOME/lib/cucumber/type/collection.js:14:11)", + "status": "failed" + }, + "match": { + "location": "TODO" + } + } + ] + } + ] + } + ] + """ + Scenario: output JSON for multiple features + Given a file named "features/a.feature" with: + """ + Feature: feature a + + Scenario: This is the first feature + Given This step is passing + """ + And a file named "features/b.feature" with: + """ + Feature: feature b + + Scenario: This is the second feature + Given This step is passing + """ + And a file named "features/c.feature" with: + """ + Feature: feature c + + Scenario: This is the third feature + Given This step is passing + """ + And a file named "features/step_definitions/cucumber_steps.js" with: + """ + var cucumberSteps = function() { + this.Given(/^This step is passing$/, function(callback) { callback(); }); + }; + module.exports = cucumberSteps; + """ + When I run `cucumber.js -f json` + Then it should output this json: + """ + [ + { + "id": "feature-a", + "name": "feature a", + "description": "", + "line": 1, + "keyword": "Feature", + "uri": "$CUCUMBER_JS_HOME/tmp/cucumber-js-sandbox/features/a.feature", + "elements": [ + { + "name": "This is the first feature", + "id": "feature-a;this-is-the-first-feature", + "line": 3, + "keyword": "Scenario", + "description": "", + "type": "scenario", + "steps": [ + { + "name": "This step is passing", + "line": 4, + "keyword": "Given ", + "result": { + "status": "passed" + }, + "match": { + "location": "TODO" + } + } + ] + } + ] + }, + { + "id": "feature-b", + "name": "feature b", + "description": "", + "line": 1, + "keyword": "Feature", + "uri": "$CUCUMBER_JS_HOME/tmp/cucumber-js-sandbox/features/b.feature", + "elements": [ + { + "name": "This is the second feature", + "id": "feature-b;this-is-the-second-feature", + "line": 3, + "keyword": "Scenario", + "description": "", + "type": "scenario", + "steps": [ + { + "name": "This step is passing", + "line": 4, + "keyword": "Given ", + "result": { + "status": "passed" + }, + "match": { + "location": "TODO" + } + } + ] + } + ] + }, + { + "id": "feature-c", + "name": "feature c", + "description": "", + "line": 1, + "keyword": "Feature", + "uri": "$CUCUMBER_JS_HOME/tmp/cucumber-js-sandbox/features/c.feature", + "elements": [ + { + "name": "This is the third feature", + "id": "feature-c;this-is-the-third-feature", + "line": 3, + "keyword": "Scenario", + "description": "", + "type": "scenario", + "steps": [ + { + "name": "This step is passing", + "line": 4, + "keyword": "Given ", + "result": { + "status": "passed" + }, + "match": { + "location": "TODO" + } + } + ] + } + ] + } + ] + """ + Scenario: output JSON for multiple features each with multiple scenarios + Given a file named "features/a.feature" with: + """ + Feature: feature a + + Scenario: This is the feature a scenario one + Given This step is passing + + Scenario: This is the feature a scenario two + Given This step is passing + + Scenario: This is the feature a scenario three + Given This step is passing + """ + And a file named "features/b.feature" with: + """ + Feature: feature b + + Scenario: This is the feature b scenario one + Given This step is passing + + Scenario: This is the feature b scenario two + Given This step is passing + + Scenario: This is the feature b scenario three + Given This step is passing + """ + And a file named "features/c.feature" with: + """ + Feature: feature c + + Scenario: This is the feature c scenario one + Given This step is passing + + Scenario: This is the feature c scenario two + Given This step is passing + + Scenario: This is the feature c scenario three + Given This step is passing + """ + And a file named "features/step_definitions/cucumber_steps.js" with: + """ + var cucumberSteps = function() { + this.Given(/^This step is passing$/, function(callback) { callback(); }); + }; + module.exports = cucumberSteps; + """ + When I run `cucumber.js -f json` + Then it should output this json: + """ + [ + { + "id": "feature-a", + "name": "feature a", + "description": "", + "line": 1, + "keyword": "Feature", + "uri": "$CUCUMBER_JS_HOME/tmp/cucumber-js-sandbox/features/a.feature", + "elements": [ + { + "name": "This is the feature a scenario one", + "id": "feature-a;this-is-the-feature-a-scenario-one", + "line": 3, + "keyword": "Scenario", + "description": "", + "type": "scenario", + "steps": [ + { + "name": "This step is passing", + "line": 4, + "keyword": "Given ", + "result": { + "status": "passed" + }, + "match": { + "location": "TODO" + } + } + ] + }, + { + "name": "This is the feature a scenario two", + "id": "feature-a;this-is-the-feature-a-scenario-two", + "line": 6, + "keyword": "Scenario", + "description": "", + "type": "scenario", + "steps": [ + { + "name": "This step is passing", + "line": 7, + "keyword": "Given ", + "result": { + "status": "passed" + }, + "match": { + "location": "TODO" + } + } + ] + }, + { + "name": "This is the feature a scenario three", + "id": "feature-a;this-is-the-feature-a-scenario-three", + "line": 9, + "keyword": "Scenario", + "description": "", + "type": "scenario", + "steps": [ + { + "name": "This step is passing", + "line": 10, + "keyword": "Given ", + "result": { + "status": "passed" + }, + "match": { + "location": "TODO" + } + } + ] + } + ] + }, + { + "id": "feature-b", + "name": "feature b", + "description": "", + "line": 1, + "keyword": "Feature", + "uri": "$CUCUMBER_JS_HOME/tmp/cucumber-js-sandbox/features/b.feature", + "elements": [ + { + "name": "This is the feature b scenario one", + "id": "feature-b;this-is-the-feature-b-scenario-one", + "line": 3, + "keyword": "Scenario", + "description": "", + "type": "scenario", + "steps": [ + { + "name": "This step is passing", + "line": 4, + "keyword": "Given ", + "result": { + "status": "passed" + }, + "match": { + "location": "TODO" + } + } + ] + }, + { + "name": "This is the feature b scenario two", + "id": "feature-b;this-is-the-feature-b-scenario-two", + "line": 6, + "keyword": "Scenario", + "description": "", + "type": "scenario", + "steps": [ + { + "name": "This step is passing", + "line": 7, + "keyword": "Given ", + "result": { + "status": "passed" + }, + "match": { + "location": "TODO" + } + } + ] + }, + { + "name": "This is the feature b scenario three", + "id": "feature-b;this-is-the-feature-b-scenario-three", + "line": 9, + "keyword": "Scenario", + "description": "", + "type": "scenario", + "steps": [ + { + "name": "This step is passing", + "line": 10, + "keyword": "Given ", + "result": { + "status": "passed" + }, + "match": { + "location": "TODO" + } + } + ] + } + ] + }, + { + "id": "feature-c", + "name": "feature c", + "description": "", + "line": 1, + "keyword": "Feature", + "uri": "$CUCUMBER_JS_HOME/tmp/cucumber-js-sandbox/features/c.feature", + "elements": [ + { + "name": "This is the feature c scenario one", + "id": "feature-c;this-is-the-feature-c-scenario-one", + "line": 3, + "keyword": "Scenario", + "description": "", + "type": "scenario", + "steps": [ + { + "name": "This step is passing", + "line": 4, + "keyword": "Given ", + "result": { + "status": "passed" + }, + "match": { + "location": "TODO" + } + } + ] + }, + { + "name": "This is the feature c scenario two", + "id": "feature-c;this-is-the-feature-c-scenario-two", + "line": 6, + "keyword": "Scenario", + "description": "", + "type": "scenario", + "steps": [ + { + "name": "This step is passing", + "line": 7, + "keyword": "Given ", + "result": { + "status": "passed" + }, + "match": { + "location": "TODO" + } + } + ] + }, + { + "name": "This is the feature c scenario three", + "id": "feature-c;this-is-the-feature-c-scenario-three", + "line": 9, + "keyword": "Scenario", + "description": "", + "type": "scenario", + "steps": [ + { + "name": "This step is passing", + "line": 10, + "keyword": "Given ", + "result": { + "status": "passed" + }, + "match": { + "location": "TODO" + } + } + ] + } + ] + } + ] + """ + + # TODO: Embedings + # TODO: DocString + # TODO: Backgrounds diff --git a/lib/cucumber/listener/json_formatter_wrapper.js b/lib/cucumber/listener/json_formatter_wrapper.js index b8279fc9b..c13249acf 100644 --- a/lib/cucumber/listener/json_formatter_wrapper.js +++ b/lib/cucumber/listener/json_formatter_wrapper.js @@ -60,7 +60,7 @@ var JsonFormatterWrapper = function(io) { var id = currentFeatureId + ';' + scenario.getName().replace(/ /g, '-').toLowerCase(); - formatter.scenario({keyword: "Scenario", name: scenario.getName(), description: scenario.getDescription(), line: scenario.getLine(), id: id}); + formatter.scenario({name: scenario.getName(), id: id, line: scenario.getLine(), keyword: "Scenario", description: scenario.getDescription(), type:"scenario"}); callback(); }, @@ -70,8 +70,7 @@ var JsonFormatterWrapper = function(io) { var step = stepResult.getStep(); - formatter.step({keyword: step.getKeyword(), name: step.getName(), line: step.getLine()}); - formatter.match({location: "TODO"}); + formatter.step({name: step.getName(), line: step.getLine(), keyword: step.getKeyword()}); var stepOutput = {}; var resultStatus = "failed"; @@ -81,7 +80,7 @@ var JsonFormatterWrapper = function(io) { } else if (stepResult.isPending()) { resultStatus = "pending"; - stepOutput["result"]["error_message"] = 'TODO'; + stepOutput["error_message"] = 'TODO'; } else if (stepResult.isSkipped()) { resultStatus = "skipped"; @@ -97,6 +96,7 @@ var JsonFormatterWrapper = function(io) { stepOutput["status"] = resultStatus formatter.result(stepOutput); + formatter.match({location: "TODO"}); callback(); }, diff --git a/spec/cucumber/listener/json_formatter_wrapper_spec.js b/spec/cucumber/listener/json_formatter_wrapper_spec.js index e047c409d..059cccc41 100644 --- a/spec/cucumber/listener/json_formatter_wrapper_spec.js +++ b/spec/cucumber/listener/json_formatter_wrapper_spec.js @@ -14,7 +14,7 @@ describe("Cucumber.Listener.JsonFormatterWrapper", function() { }); // Get Event -/* + describe("hear()", function() { var event, callback; var eventHandler; @@ -153,7 +153,7 @@ describe("Cucumber.Listener.JsonFormatterWrapper", function() { }); }); }); -*/ + // Handle Feature describe("handleBeforeFeatureEvent()", function() { @@ -291,7 +291,7 @@ describe("Cucumber.Listener.JsonFormatterWrapper", function() { var output = buffer.toString(); output = output.substr(0,output.indexOf(String.fromCharCode(0))); - var expected = '[]'; + var expected = '[{"id":"A-Name","name":"A Name","description":"A Description","line":3,"keyword":"Feature","uri":"feature-uri","elements":[{"keyword":"Scenario","name":"A Name","description":"A Description","line":3,"id":"A-Name;a-name"}]}]'; expect(output).toEqual(expected); @@ -299,114 +299,6 @@ describe("Cucumber.Listener.JsonFormatterWrapper", function() { }); -/* - it("adds the step result to parent when there are multiple features in the output", function(){ - - stepResult.isSuccessful.andReturn(true); - - feature = createSpyWithStubs("feature", - {getKeyword: 'Feature', - getName: 'A Name', - getDescription: 'A Description', - getLine: 3, - getUri: 'TODO'}); - - feature_event = createSpyWithStubs("event", {getPayloadItem: feature}); - - listener.handleBeforeFeatureEvent(feature_event, callback); - listener.handleStepResultEvent(event, callback); - - listener.handleBeforeFeatureEvent(feature_event, callback); - listener.handleStepResultEvent(event, callback); - - listener.handleBeforeFeatureEvent(feature_event, callback); - listener.handleStepResultEvent(event, callback); - - }); - - - it("gets the step result from the event payload", function() { - }); - - it("checks wether the step was successful or not", function() { - }); - - describe("when the step passed", function() { - }); - - describe("when the step did not pass", function() { - beforeEach(function() { - }); - - it("does not handle a successful step result", function() { - }); - - it("checks wether the step is pending", function() { - }); - - describe("when the step was pending", function() { - beforeEach(function() { - }); - - it("handles the pending step result", function() { - }); - }); - - describe("when the step was not pending", function() { - beforeEach(function() { - }); - - it("does not handle a pending step result", function() { - }); - - it("checks wether the step was skipped", function() { - }); - - describe("when the step was skipped", function() { - beforeEach(function() { - }); - - it("handles the skipped step result", function() { - }); - }); - - describe("when the step was not skipped", function() { - beforeEach(function() { - }); - - it("does not handle a skipped step result", function() { - }); - - it("checks wether the step was undefined", function() { - }); - - describe("when the step was undefined", function() { - beforeEach(function() { - }); - - it("handles the undefined step result", function() { - }); - }); - - describe("when the step was not undefined", function() { - beforeEach(function() { - }); - - it("does not handle a skipped step result", function() { - }); - - it("handles a failed step result", function() { - }); - }); - }); - }); - }); - - it("calls back", function() { - }); - - }); -*/ // We're all done. Output the JSON. describe("handleAfterFeaturesEvent()", function() { @@ -420,7 +312,7 @@ describe("Cucumber.Listener.JsonFormatterWrapper", function() { it("writes to stdout", function() { listener.handleAfterFeaturesEvent(event, callback); - // expect(process.stdout.write).toHaveBeenCalled(); //TODO: With anything? + expect(process.stdout.write).toHaveBeenCalled(); }); it("calls back", function() { From 31f260b7b158f22a1e9ae861a04dd9d4c88741d7 Mon Sep 17 00:00:00 2001 From: Chris Young Date: Tue, 26 Jun 2012 22:45:29 +0100 Subject: [PATCH 22/44] Jasmine tests all passing. Ugly relative path to require Gherkin removed thanks to @jbpros. --- lib/cucumber/cli/configuration.js | 2 +- lib/cucumber/listener/json_formatter_wrapper.js | 7 ++----- spec/cucumber/cli/configuration_spec.js | 4 ++-- spec/cucumber/listener/json_formatter_wrapper_spec.js | 10 +++------- 4 files changed, 8 insertions(+), 15 deletions(-) diff --git a/lib/cucumber/cli/configuration.js b/lib/cucumber/cli/configuration.js index 5a797dbf2..8562e5b34 100644 --- a/lib/cucumber/cli/configuration.js +++ b/lib/cucumber/cli/configuration.js @@ -16,7 +16,7 @@ var Configuration = function(argv) { formatter = Cucumber.Listener.PrettyFormatter(); break; case Configuration.JSON_FORMAT_NAME: - formatter = Cucumber.Listener.JsonFormatterWrapper(); + formatter = Cucumber.Listener.JsonFormatterWrapper(process.stdout); break; default: throw new Error("Unknown formatter name \"" + format + "\"."); diff --git a/lib/cucumber/listener/json_formatter_wrapper.js b/lib/cucumber/listener/json_formatter_wrapper.js index c13249acf..552fe35ee 100644 --- a/lib/cucumber/listener/json_formatter_wrapper.js +++ b/lib/cucumber/listener/json_formatter_wrapper.js @@ -1,10 +1,7 @@ var JsonFormatterWrapper = function(io) { - // This seems a pretty ugly way refering to the gherkin json_formatter. QUESTION: Is there a more direct way of refering to it? - // Does Gherkin have to export it in gherkin.js? - var JSONFormatter = require('../../../node_modules/gherkin/lib/gherkin/formatter/json_formatter.js'); - - var formatter = new JSONFormatter(process.stdout); // HACK!!! io); + var JSONFormatter = require('gherkin/lib/gherkin/formatter/json_formatter'); + var formatter = new JSONFormatter(io); var currentFeatureId = ''; diff --git a/spec/cucumber/cli/configuration_spec.js b/spec/cucumber/cli/configuration_spec.js index 8be11b378..abd26b6b1 100644 --- a/spec/cucumber/cli/configuration_spec.js +++ b/spec/cucumber/cli/configuration_spec.js @@ -242,10 +242,10 @@ describe("Cucumber.Cli.Configuration", function () { describe("getFormatter()", function() { beforeEach(function() { - spyOnStub(argumentParser, 'getFormat'); + spyOnStub(argumentParser, 'getFormat').andReturn("progress"); }); - it("asks the argument parser which format we should be outputting in", function() { + it("asks the argument parser which format we should be outputting", function() { configuration.getFormatter(); expect(argumentParser.getFormat).toHaveBeenCalled(); }); diff --git a/spec/cucumber/listener/json_formatter_wrapper_spec.js b/spec/cucumber/listener/json_formatter_wrapper_spec.js index 059cccc41..d04b2180f 100644 --- a/spec/cucumber/listener/json_formatter_wrapper_spec.js +++ b/spec/cucumber/listener/json_formatter_wrapper_spec.js @@ -197,7 +197,6 @@ describe("Cucumber.Listener.JsonFormatterWrapper", function() { }); - // Handle Scenario describe("handleBeforeScenarioEvent()", function() { @@ -230,7 +229,7 @@ describe("Cucumber.Listener.JsonFormatterWrapper", function() { var output = buffer.toString(); output = output.substr(0,output.indexOf(String.fromCharCode(0))); - var expected = '[{"id":"A-Name","name":"A Name","description":"A Description","line":3,"keyword":"Feature","uri":"feature-uri","elements":[{"keyword":"Scenario","name":"A Name","description":"A Description","line":3,"id":"A-Name;a-name"}]}]'; + var expected = '[{"id":"A-Name","name":"A Name","description":"A Description","line":3,"keyword":"Feature","uri":"feature-uri","elements":[{"name":"A Name","id":"A-Name;a-name","line":3,"keyword":"Scenario","description":"A Description","type":"scenario"}]}]'; expect(output).toEqual(expected); @@ -291,7 +290,7 @@ describe("Cucumber.Listener.JsonFormatterWrapper", function() { var output = buffer.toString(); output = output.substr(0,output.indexOf(String.fromCharCode(0))); - var expected = '[{"id":"A-Name","name":"A Name","description":"A Description","line":3,"keyword":"Feature","uri":"feature-uri","elements":[{"keyword":"Scenario","name":"A Name","description":"A Description","line":3,"id":"A-Name;a-name"}]}]'; + var expected = '[{"id":"A-Name","name":"A Name","description":"A Description","line":3,"keyword":"Feature","uri":"feature-uri","elements":[{"name":"A Name","id":"A-Name;a-name","line":3,"keyword":"Scenario","description":"A Description","type":"scenario","steps":[{"name":"Step","line":3,"keyword":"Step","result":{"status":"passed"},"match":{"location":"TODO"}}]}]}]'; expect(output).toEqual(expected); @@ -310,10 +309,7 @@ describe("Cucumber.Listener.JsonFormatterWrapper", function() { }); - it("writes to stdout", function() { - listener.handleAfterFeaturesEvent(event, callback); - expect(process.stdout.write).toHaveBeenCalled(); - }); + // TODO: What else should we test here? e.g. calls made to the formatter? it("calls back", function() { listener.handleAfterFeaturesEvent(event, callback); From 0e5d28b1b07b5840583ab098df554b839d3fd3b7 Mon Sep 17 00:00:00 2001 From: Chris Young Date: Thu, 5 Jul 2012 14:45:13 +0100 Subject: [PATCH 23/44] Refactored json_formatter_wrapper to use functionality provided by parent listener class. Added partial support for background. Does not deal with results of background steps yet. --- features/json_formatter.feature | 95 ++++++++- .../listener/json_formatter_wrapper.js | 147 ++++++-------- .../listener/json_formatter_wrapper_spec.js | 190 +++++------------- 3 files changed, 208 insertions(+), 224 deletions(-) diff --git a/features/json_formatter.feature b/features/json_formatter.feature index 907ea154c..bff7651b6 100644 --- a/features/json_formatter.feature +++ b/features/json_formatter.feature @@ -898,6 +898,99 @@ Scenario: one feature, one passing scenario, one failing scenario ] """ + Scenario: output JSON for a feature with a background + Given a file named "features/a.feature" with: + """ + Feature: some feature + + Background: + Given This applies to all scenarios + """ + And a file named "features/step_definitions/cucumber_steps.js" with: + """ + var cucumberSteps = function() { + this.Given(/^This applies to all scenarios$/, function(callback) { callback(); }); + }; + module.exports = cucumberSteps; + """ + When I run `cucumber.js -f json` + Then it should output this json: + """ + [ + { + "id": "some-feature", + "name": "some feature", + "description": "", + "line": 1, + "keyword": "Feature", + "uri": "$CUCUMBER_JS_HOME/tmp/cucumber-js-sandbox/features/a.feature", + "elements": [ + { + "name": "", + "keyword": "Background", + "description": "", + "type": "background", + "line": 3, + "steps": [ + { + "name": "This applies to all scenarios", + "line": 4, + "keyword": "Given " + } + ] + } + ] + } + ] + """ + + Scenario: output JSON for a feature with a failing background + Given a file named "features/a.feature" with: + """ + Feature: some feature + + Background: + Given This applies to all scenarios but fails + """ + And a file named "features/step_definitions/cucumber_steps.js" with: + """ + var cucumberSteps = function() { + this.Given(/^This applies to all scenarios but fails$/, function(callback) { callback.fail(); }); + }; + module.exports = cucumberSteps; + """ + When I run `cucumber.js -f json` + Then it should output this json: + """ + [ + { + "id": "some-feature", + "name": "some feature", + "description": "", + "line": 1, + "keyword": "Feature", + "uri": "$CUCUMBER_JS_HOME/tmp/cucumber-js-sandbox/features/a.feature", + "elements": [ + { + "name": "", + "keyword": "Background", + "description": "", + "type": "background", + "line": 3, + "steps": [ + { + "name": "This applies to all scenarios but fails", + "line": 4, + "keyword": "Given " + } + ] + } + ] + } + ] + """ + + # TODO: Add step results to background steps # TODO: Embedings # TODO: DocString - # TODO: Backgrounds + # TODO: Tags diff --git a/lib/cucumber/listener/json_formatter_wrapper.js b/lib/cucumber/listener/json_formatter_wrapper.js index 552fe35ee..80b61f82d 100644 --- a/lib/cucumber/listener/json_formatter_wrapper.js +++ b/lib/cucumber/listener/json_formatter_wrapper.js @@ -5,114 +5,95 @@ var JsonFormatterWrapper = function(io) { var currentFeatureId = ''; - var self = { + var Cucumber = require('../../cucumber'); + var self = Cucumber.Listener(); - hear: function hear(event, callback) { - - if (self.hasHandlerForEvent(event)) { - var handler = self.getHandlerForEvent(event); - handler(event, callback); - } else { - callback(); - } - }, + function formatStep(step) { - hasHandlerForEvent: function hasHandlerForEvent(event) { - var handlerName = self.buildHandlerNameForEvent(event); - return self[handlerName] != undefined; - }, + formatter.step({name: step.getName(), line: step.getLine(), keyword: step.getKeyword()}); - buildHandlerNameForEvent: function buildHandlerNameForEvent(event) { - var handlerName = - JsonFormatterWrapper.EVENT_HANDLER_NAME_PREFIX + - event.getName() + - JsonFormatterWrapper.EVENT_HANDLER_NAME_SUFFIX; - return handlerName; - }, + } - getHandlerForEvent: function getHandlerForEvent(event) { - var eventHandlerName = self.buildHandlerNameForEvent(event); - return self[eventHandlerName]; - }, + self.handleBeforeFeatureEvent = function handleBeforeFeatureEvent(event, callback) { - handleBeforeFeatureEvent: function handleBeforeFeatureEvent(event, callback) { + var feature = event.getPayloadItem('feature'); + currentFeatureId = feature.getName().replace(' ','-'); - var feature = event.getPayloadItem('feature'); - currentFeatureId = feature.getName().replace(' ','-'); + formatter.uri(feature.getUri()); - formatter.uri(feature.getUri()); + formatter.feature({id: currentFeatureId, + name: feature.getName(), + description: feature.getDescription(), + line: feature.getLine(), + keyword: feature.getKeyword()}); - formatter.feature({id: currentFeatureId, - name: feature.getName(), - description: feature.getDescription(), - line: feature.getLine(), - keyword: feature.getKeyword()}); + callback(); + } - callback(); - }, + self.handleBackgroundEvent = function handleBackgroundEvent(event, callback) { + var background = event.getPayloadItem('background'); + formatter.background({name: background.getName(), keyword: "Background", description: background.getDescription(), type: "background", line: background.getLine()}) + var steps = background.getSteps(); + steps.forEach(function(value, index, ar) { formatStep(value); }); + callback(); + } - handleBeforeScenarioEvent: function handleBeforeScenarioEvent(event, callback) { + self.handleBeforeScenarioEvent = function handleBeforeScenarioEvent(event, callback) { - var scenario = event.getPayloadItem('scenario'); + var scenario = event.getPayloadItem('scenario'); - var id = currentFeatureId + ';' + scenario.getName().replace(/ /g, '-').toLowerCase(); + var id = currentFeatureId + ';' + scenario.getName().replace(/ /g, '-').toLowerCase(); - formatter.scenario({name: scenario.getName(), id: id, line: scenario.getLine(), keyword: "Scenario", description: scenario.getDescription(), type:"scenario"}); + formatter.scenario({name: scenario.getName(), id: id, line: scenario.getLine(), keyword: "Scenario", description: scenario.getDescription(), type: "scenario"}); - callback(); - }, + callback(); + } - handleStepResultEvent: function handleStepResult(event, callback) { - var stepResult = event.getPayloadItem('stepResult'); + self.handleStepResultEvent = function handleStepResult(event, callback) { + var stepResult = event.getPayloadItem('stepResult'); - var step = stepResult.getStep(); + var step = stepResult.getStep(); + formatStep(step); - formatter.step({name: step.getName(), line: step.getLine(), keyword: step.getKeyword()}); + var stepOutput = {}; + var resultStatus = "failed"; - var stepOutput = {}; - var resultStatus = "failed"; - - if (stepResult.isSuccessful()) { - resultStatus = "passed"; - } - else if (stepResult.isPending()) { - resultStatus = "pending"; - stepOutput["error_message"] = 'TODO'; - } - else if (stepResult.isSkipped()) { - resultStatus = "skipped"; - } - else if (stepResult.isUndefined()) { - resultStatus = "undefined"; - } - else { - var failureMessage = stepResult.getFailureException(); - stepOutput["error_message"] = (failureMessage.stack || failureMessage); - } - - stepOutput["status"] = resultStatus + if (stepResult.isSuccessful()) { + resultStatus = "passed"; + } + else if (stepResult.isPending()) { + resultStatus = "pending"; + stepOutput["error_message"] = 'TODO'; + } + else if (stepResult.isSkipped()) { + resultStatus = "skipped"; + } + else if (stepResult.isUndefined()) { + resultStatus = "undefined"; + } + else { + var failureMessage = stepResult.getFailureException(); + stepOutput["error_message"] = (failureMessage.stack || failureMessage); + } - formatter.result(stepOutput); - formatter.match({location: "TODO"}); + stepOutput["status"] = resultStatus; - callback(); - }, + formatter.result(stepOutput); + formatter.match({location: "TODO"}); + + callback(); + } - handleAfterFeaturesEvent: function handleAfterFeaturesEvent(event, callback) { + self.handleAfterFeaturesEvent = function handleAfterFeaturesEvent(event, callback) { - formatter.eof(); - formatter.done(); + formatter.eof(); + formatter.done(); - callback(); - } + callback(); + } - }; return self; }; -// TODO: Factor out to make common to all handlers -JsonFormatterWrapper.EVENT_HANDLER_NAME_PREFIX = 'handle'; -JsonFormatterWrapper.EVENT_HANDLER_NAME_SUFFIX = 'Event'; - -module.exports = JsonFormatterWrapper; +module.exports = JsonFormatterWrapper; diff --git a/spec/cucumber/listener/json_formatter_wrapper_spec.js b/spec/cucumber/listener/json_formatter_wrapper_spec.js index d04b2180f..79714df6d 100644 --- a/spec/cucumber/listener/json_formatter_wrapper_spec.js +++ b/spec/cucumber/listener/json_formatter_wrapper_spec.js @@ -13,146 +13,6 @@ describe("Cucumber.Listener.JsonFormatterWrapper", function() { describe("constructor", function() { }); - // Get Event - - describe("hear()", function() { - var event, callback; - var eventHandler; - - beforeEach(function() { - event = createSpy("Event"); - callback = createSpy("Callback"); - spyOn(listener, 'hasHandlerForEvent'); - spyOn(listener, 'getHandlerForEvent'); - }); - - it("checks wether there is a handler for the event", function() { - listener.hear(event, callback); - expect(listener.hasHandlerForEvent).toHaveBeenCalledWith(event); - }); - - describe("when there is a handler for that event", function() { - beforeEach(function() { - eventHandler = createSpy("Event handler (function)"); - listener.hasHandlerForEvent.andReturn(true); - listener.getHandlerForEvent.andReturn(eventHandler); - }); - - it("gets the handler for that event", function() { - listener.hear(event, callback); - expect(listener.getHandlerForEvent).toHaveBeenCalledWith(event); - }); - - it("calls the handler with the event and the callback", function() { - listener.hear(event, callback); - expect(eventHandler).toHaveBeenCalledWith(event, callback); - }); - - it("does not callback", function() { - listener.hear(event, callback); - expect(callback).not.toHaveBeenCalled(); - }); - }); - - describe("when there are no handlers for that event", function() { - beforeEach(function() { - listener.hasHandlerForEvent.andReturn(false); - }); - - it("calls back", function() { - listener.hear(event, callback); - expect(callback).toHaveBeenCalled(); - }); - - it("does not get the handler for the event", function() { - listener.hear(event, callback); - expect(listener.getHandlerForEvent).not.toHaveBeenCalled(); - }); - }); - }); - - describe("hasHandlerForEvent", function() { - var event, eventHandlerName, eventHandler; - - beforeEach(function() { - event = createSpy("Event"); - eventHandlerName = createSpy("event handler name"); - spyOn(listener, 'buildHandlerNameForEvent').andReturn(eventHandlerName); - }); - - it("builds the name of the handler for that event", function() { - listener.hasHandlerForEvent(event); - expect(listener.buildHandlerNameForEvent).toHaveBeenCalledWith(event); - }); - - describe("when the handler exists", function() { - beforeEach(function() { - eventHandler = createSpy("event handler"); - listener[eventHandlerName] = eventHandler; - }); - - it("returns true", function() { - expect(listener.hasHandlerForEvent(event)).toBeTruthy(); - }); - }); - - describe("when the handler does not exist", function() { - it("returns false", function() { - expect(listener.hasHandlerForEvent(event)).toBeFalsy(); - }); - }); - }); - - describe("buildHandlerNameForEvent", function() { - var event, eventName; - - beforeEach(function() { - eventName = "SomeEventName"; - event = createSpyWithStubs("Event", {getName: eventName}); - }); - - it("gets the name of the event", function() { - listener.buildHandlerNameForEvent(event); - expect(event.getName).toHaveBeenCalled(); - }); - - it("returns the name of the event with prefix 'handle' and suffix 'Event'", function() { - expect(listener.buildHandlerNameForEvent(event)).toBe("handle" + eventName + "Event"); - }); - }); - - describe("getHandlerForEvent()", function() { - var event; - var eventHandlerName, eventHandler; - - beforeEach(function() { - event = createSpy("event"); - eventHandlerName = 'handleSomeEvent'; - eventHandler = createSpy("event handler"); - spyOn(listener, 'buildHandlerNameForEvent').andReturn(eventHandlerName); - }); - - it("gets the name of the handler for the event", function() { - listener.getHandlerForEvent(event); - expect(listener.buildHandlerNameForEvent).toHaveBeenCalledWith(event); - }); - - describe("when an event handler exists for the event", function() { - beforeEach(function() { - listener[eventHandlerName] = eventHandler; - }); - - it("returns the event handler", function() { - expect(listener.getHandlerForEvent(event)).toBe(eventHandler); - }); - }); - - describe("when no event handlers exist for the event", function() { - it("returns nothing", function() { - expect(listener.getHandlerForEvent(event)).toBeUndefined(); - }); - }); - }); // Handle Feature @@ -197,6 +57,56 @@ describe("Cucumber.Listener.JsonFormatterWrapper", function() { }); + // Handle Background + + describe("handleBackgroundEvent()", function() { + + var parent_feature_event, background, step, steps, event, callback; + + beforeEach(function() { + feature = createSpyWithStubs("feature", + {getKeyword: 'Feature', + getName: 'A Name', + getDescription: 'A Description', + getLine: 3, + getUri: 'feature-uri'}); + + parent_feature_event = createSpyWithStubs("event", {getPayloadItem: feature}); + + step = createSpyWithStubs("step", { + getName: 'Step', + getLine: 3, + getKeyword: 'Step' + }); + + steps = [step]; + + background = createSpyWithStubs("background", + {getKeyword: 'Background', + getName: 'A Name', + getDescription: 'A Description', + getLine: 3, + getSteps: steps}); + + event = createSpyWithStubs("event", {getPayloadItem: background}); + callback = createSpy("callback"); + }); + + it("adds the background attributes to the output", function() { + listener.handleBeforeFeatureEvent(parent_feature_event, callback); + listener.handleBackgroundEvent(event, callback); + listener.handleAfterFeaturesEvent(parent_feature_event, callback); + var output = buffer.toString(); + output = output.substr(0,output.indexOf(String.fromCharCode(0))); + + var expected = '[{"id":"A-Name","name":"A Name","description":"A Description","line":3,"keyword":"Feature","uri":"feature-uri","elements":[{"name":"A Name","keyword":"Background","description":"A Description","type":"background","line":3,"steps":[{"name":"Step","line":3,"keyword":"Step"}]}]}]'; + + expect(output).toEqual(expected); + + }); + +}); + // Handle Scenario describe("handleBeforeScenarioEvent()", function() { From f5c2112c832381f496bda99e1a777e2f3baab7a8 Mon Sep 17 00:00:00 2001 From: Chris Young Date: Thu, 19 Jul 2012 11:39:35 +0100 Subject: [PATCH 24/44] Added Work In Progress DocString formatting. Fixed fail in previous merge from upstream. --- features/json_formatter.feature | 64 ++++++++++++++++++- lib/cucumber/cli/configuration.js | 4 -- .../listener/json_formatter_wrapper.js | 26 ++++---- .../listener/json_formatter_wrapper_spec.js | 6 +- 4 files changed, 80 insertions(+), 20 deletions(-) diff --git a/features/json_formatter.feature b/features/json_formatter.feature index bff7651b6..aaf3d9b7d 100644 --- a/features/json_formatter.feature +++ b/features/json_formatter.feature @@ -944,7 +944,10 @@ Scenario: one feature, one passing scenario, one failing scenario ] """ - Scenario: output JSON for a feature with a failing background + Scenario: output JSON for a feature with a failing background + # Since the background step is re-evaluated for each scenario that is where the result of the step is currently recorded in the JSON output + # If the background is being reevaluated for each scenario then it would be misleading to only output the result for the first time it was evaluated. + Given a file named "features/a.feature" with: """ Feature: some feature @@ -989,8 +992,63 @@ Scenario: one feature, one passing scenario, one failing scenario } ] """ + + Scenario: output JSON for a feature with a DocString + Given a file named "features/a.feature" with: + """ + Feature: some feature - # TODO: Add step results to background steps + Scenario: Scenario with DocString + Given we have this DocString: + \"\"\" + This is a DocString + \"\"\" + """ + And a file named "features/step_definitions/cucumber_steps.js" with: + """ + var cucumberSteps = function() { + this.Given(/^we have this DocString:$/, function(string, callback) { callback(); }); + }; + module.exports = cucumberSteps; + """ + When I run `cucumber.js -f json` + Then it should output this json: + # DocString doesn't appear to be being populated. QUESTION: Is this expected behaviour? + """ + [ + { + "id": "some-feature", + "name": "some feature", + "description": "", + "line": 1, + "keyword": "Feature", + "uri": "$CUCUMBER_JS_HOME/tmp/cucumber-js-sandbox/features/a.feature", + "elements": [ + { + "name": "Scenario with DocString", + "id": "some-feature;scenario-with-docstring", + "line": 3, + "keyword": "Scenario", + "description": "", + "type": "scenario", + "steps": [ + { + "name": "we have this DocString:", + "line": 4, + "keyword": "Given ", + "doc_string": {}, + "result": { + "status": "passed" + }, + "match": { + "location": "TODO" + } + } + ] + } + ] + } + ] + """ # TODO: Embedings - # TODO: DocString # TODO: Tags diff --git a/lib/cucumber/cli/configuration.js b/lib/cucumber/cli/configuration.js index 075ea45cb..43bbce5b2 100644 --- a/lib/cucumber/cli/configuration.js +++ b/lib/cucumber/cli/configuration.js @@ -72,10 +72,6 @@ var Configuration = function(argv) { }; Configuration.PRETTY_FORMAT_NAME = "pretty"; Configuration.PROGRESS_FORMAT_NAME = "progress"; -<<<<<<< HEAD Configuration.JSON_FORMAT_NAME = "json"; - -======= Configuration.SUMMARY_FORMAT_NAME = "summary"; ->>>>>>> upstream/master module.exports = Configuration; diff --git a/lib/cucumber/listener/json_formatter_wrapper.js b/lib/cucumber/listener/json_formatter_wrapper.js index 80b61f82d..51370b875 100644 --- a/lib/cucumber/listener/json_formatter_wrapper.js +++ b/lib/cucumber/listener/json_formatter_wrapper.js @@ -10,7 +10,12 @@ var JsonFormatterWrapper = function(io) { function formatStep(step) { - formatter.step({name: step.getName(), line: step.getLine(), keyword: step.getKeyword()}); + var stepProperties = {name: step.getName(), line: step.getLine(), keyword: step.getKeyword()}; + if (step.hasDocString()) { + stepProperties['doc_string'] = step.getDocString(); + } + + formatter.step(stepProperties); } @@ -56,31 +61,30 @@ var JsonFormatterWrapper = function(io) { formatStep(step); var stepOutput = {}; - var resultStatus = "failed"; + var resultStatus = 'failed'; if (stepResult.isSuccessful()) { - resultStatus = "passed"; + resultStatus = 'passed'; } else if (stepResult.isPending()) { - resultStatus = "pending"; - stepOutput["error_message"] = 'TODO'; + resultStatus = 'pending'; + stepOutput['error_message'] = 'TODO'; } else if (stepResult.isSkipped()) { - resultStatus = "skipped"; + resultStatus = 'skipped'; } else if (stepResult.isUndefined()) { - resultStatus = "undefined"; + resultStatus = 'undefined'; } else { var failureMessage = stepResult.getFailureException(); - stepOutput["error_message"] = (failureMessage.stack || failureMessage); + stepOutput['error_message'] = (failureMessage.stack || failureMessage); } - stepOutput["status"] = resultStatus; + stepOutput['status'] = resultStatus; formatter.result(stepOutput); - formatter.match({location: "TODO"}); - + formatter.match({location: 'TODO'}); callback(); } diff --git a/spec/cucumber/listener/json_formatter_wrapper_spec.js b/spec/cucumber/listener/json_formatter_wrapper_spec.js index 79714df6d..73c9076a2 100644 --- a/spec/cucumber/listener/json_formatter_wrapper_spec.js +++ b/spec/cucumber/listener/json_formatter_wrapper_spec.js @@ -76,7 +76,8 @@ describe("Cucumber.Listener.JsonFormatterWrapper", function() { step = createSpyWithStubs("step", { getName: 'Step', getLine: 3, - getKeyword: 'Step' + getKeyword: 'Step', + hasDocString: false }); steps = [step]; @@ -173,7 +174,8 @@ describe("Cucumber.Listener.JsonFormatterWrapper", function() { step = createSpyWithStubs("step", { getName: 'Step', getLine: 3, - getKeyword: 'Step' + getKeyword: 'Step', + hasDocString: false }); stepResult = createSpyWithStubs("step result", { From 8272e2fad687b07a49b4bd153404aae0c883e3e0 Mon Sep 17 00:00:00 2001 From: Chris Young Date: Thu, 19 Jul 2012 14:12:17 +0100 Subject: [PATCH 25/44] Added DocString support --- features/json_formatter.feature | 8 ++++++-- lib/cucumber/listener/json_formatter_wrapper.js | 3 ++- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/features/json_formatter.feature b/features/json_formatter.feature index aaf3d9b7d..ed500dcb2 100644 --- a/features/json_formatter.feature +++ b/features/json_formatter.feature @@ -1013,7 +1013,6 @@ Scenario: one feature, one passing scenario, one failing scenario """ When I run `cucumber.js -f json` Then it should output this json: - # DocString doesn't appear to be being populated. QUESTION: Is this expected behaviour? """ [ { @@ -1036,7 +1035,12 @@ Scenario: one feature, one passing scenario, one failing scenario "name": "we have this DocString:", "line": 4, "keyword": "Given ", - "doc_string": {}, + "doc_string": + { + "value": "This is a DocString", + "line": 5, + "content_type": "" + }, "result": { "status": "passed" }, diff --git a/lib/cucumber/listener/json_formatter_wrapper.js b/lib/cucumber/listener/json_formatter_wrapper.js index 51370b875..1c0056ee3 100644 --- a/lib/cucumber/listener/json_formatter_wrapper.js +++ b/lib/cucumber/listener/json_formatter_wrapper.js @@ -12,7 +12,8 @@ var JsonFormatterWrapper = function(io) { var stepProperties = {name: step.getName(), line: step.getLine(), keyword: step.getKeyword()}; if (step.hasDocString()) { - stepProperties['doc_string'] = step.getDocString(); + var docString = step.getDocString(); + stepProperties['doc_string'] = {value: docString.getContents(), line: docString.getLine(), content_type: docString.getContentType()}; } formatter.step(stepProperties); From dd5eb0a7a2a2d1e6c8e84c40a5b5544edd621ace Mon Sep 17 00:00:00 2001 From: Chris Young Date: Thu, 19 Jul 2012 16:53:20 +0100 Subject: [PATCH 26/44] Added support for scenario level tags --- features/json_formatter.feature | 70 +++++++++++++++++++ .../listener/json_formatter_wrapper.js | 12 +++- .../listener/json_formatter_wrapper_spec.js | 6 +- 3 files changed, 85 insertions(+), 3 deletions(-) diff --git a/features/json_formatter.feature b/features/json_formatter.feature index ed500dcb2..70bf44f9f 100644 --- a/features/json_formatter.feature +++ b/features/json_formatter.feature @@ -1054,5 +1054,75 @@ Scenario: one feature, one passing scenario, one failing scenario } ] """ + + Scenario: Scenario with tags + + Given a file named "features/a.feature" with: + """ + Feature: some feature + + @one @two @three + Scenario: This scenario has tags + Given This step is passing + """ + And a file named "features/step_definitions/cucumber_steps.js" with: + """ + var cucumberSteps = function() { + this.Given(/^This step is passing$/, function(callback) { callback(); }); + }; + module.exports = cucumberSteps; + """ + When I run `cucumber.js -f json` + Then it should output this json: + """ + [ + { + "id": "some-feature", + "name": "some feature", + "description": "", + "line": 1, + "keyword": "Feature", + "uri": "$CUCUMBER_JS_HOME/tmp/cucumber-js-sandbox/features/a.feature", + "elements": [ + { + "name": "This scenario has tags", + "id": "some-feature;this-scenario-has-tags", + "line": 4, + "keyword": "Scenario", + "description": "", + "type": "scenario", + "tags": [ + { + "name": "@one", + "line": 3 + }, + { + "name": "@two", + "line": 3 + }, + { + "name": "@three", + "line": 3 + } + ], + "steps": [ + { + "name": "This step is passing", + "line": 5, + "keyword": "Given ", + "result": { + "status": "passed" + }, + "match": { + "location": "TODO" + } + } + ] + } + ] + } + ] + """ + # TODO: Embedings # TODO: Tags diff --git a/lib/cucumber/listener/json_formatter_wrapper.js b/lib/cucumber/listener/json_formatter_wrapper.js index 1c0056ee3..8d5e6e7f0 100644 --- a/lib/cucumber/listener/json_formatter_wrapper.js +++ b/lib/cucumber/listener/json_formatter_wrapper.js @@ -49,8 +49,18 @@ var JsonFormatterWrapper = function(io) { var scenario = event.getPayloadItem('scenario'); var id = currentFeatureId + ';' + scenario.getName().replace(/ /g, '-').toLowerCase(); + var scenarioProperties = {name: scenario.getName(), id: id, line: scenario.getLine(), keyword: "Scenario", description: scenario.getDescription(), type: "scenario"}; + + var tags = scenario.getTags(); + if (tags.length > 0) { + var tagsProperties = []; + for (tag in tags) { + tagsProperties.push({name: tags[tag].getName(), line: tags[tag].getLine()}); + } + scenarioProperties['tags'] = tagsProperties; + } - formatter.scenario({name: scenario.getName(), id: id, line: scenario.getLine(), keyword: "Scenario", description: scenario.getDescription(), type: "scenario"}); + formatter.scenario(scenarioProperties); callback(); } diff --git a/spec/cucumber/listener/json_formatter_wrapper_spec.js b/spec/cucumber/listener/json_formatter_wrapper_spec.js index 73c9076a2..d30edc340 100644 --- a/spec/cucumber/listener/json_formatter_wrapper_spec.js +++ b/spec/cucumber/listener/json_formatter_wrapper_spec.js @@ -127,7 +127,8 @@ describe("Cucumber.Listener.JsonFormatterWrapper", function() { {getKeyword: 'Scenario', getName: 'A Name', getDescription: 'A Description', - getLine: 3}); + getLine: 3, + getTags: false}); event = createSpyWithStubs("event", {getPayloadItem: scenario}); callback = createSpy("callback"); @@ -167,7 +168,8 @@ describe("Cucumber.Listener.JsonFormatterWrapper", function() { {getKeyword: 'Scenario', getName: 'A Name', getDescription: 'A Description', - getLine: 3}); + getLine: 3, + getTags: false}); parent_scenario_event = createSpyWithStubs("event", {getPayloadItem: scenario}); From 0b8d93ed7b2c1875c0ca1cab6d625b30f1b6785d Mon Sep 17 00:00:00 2001 From: Chris Young Date: Thu, 19 Jul 2012 17:27:56 +0100 Subject: [PATCH 27/44] Added support for Feature level tags --- features/json_formatter.feature | 70 ++++++++++++++++++- .../listener/json_formatter_wrapper.js | 46 +++++++++--- .../listener/json_formatter_wrapper_spec.js | 2 + 3 files changed, 107 insertions(+), 11 deletions(-) diff --git a/features/json_formatter.feature b/features/json_formatter.feature index 70bf44f9f..b94390200 100644 --- a/features/json_formatter.feature +++ b/features/json_formatter.feature @@ -1055,6 +1055,75 @@ Scenario: one feature, one passing scenario, one failing scenario ] """ + Scenario: Feature with tags + + Given a file named "features/a.feature" with: + """ + @alpha @beta @gamma + Feature: some feature + + Scenario: This scenario has no tags + Given This step is passing + """ + And a file named "features/step_definitions/cucumber_steps.js" with: + """ + var cucumberSteps = function() { + this.Given(/^This step is passing$/, function(callback) { callback(); }); + }; + module.exports = cucumberSteps; + """ + When I run `cucumber.js -f json` + Then it should output this json: + """ + [ + { + "id": "some-feature", + "name": "some feature", + "description": "", + "line": 2, + "keyword": "Feature", + "tags": [ + { + "name": "@alpha", + "line": 1 + }, + { + "name": "@beta", + "line": 1 + }, + { + "name": "@gamma", + "line": 1 + } + ], + "uri": "$CUCUMBER_JS_HOME/tmp/cucumber-js-sandbox/features/a.feature", + "elements": [ + { + "name": "This scenario has no tags", + "id": "some-feature;this-scenario-has-no-tags", + "line": 4, + "keyword": "Scenario", + "description": "", + "type": "scenario", + "steps": [ + { + "name": "This step is passing", + "line": 5, + "keyword": "Given ", + "result": { + "status": "passed" + }, + "match": { + "location": "TODO" + } + } + ] + } + ] + } + ] + """ + Scenario: Scenario with tags Given a file named "features/a.feature" with: @@ -1125,4 +1194,3 @@ Scenario: one feature, one passing scenario, one failing scenario """ # TODO: Embedings - # TODO: Tags diff --git a/lib/cucumber/listener/json_formatter_wrapper.js b/lib/cucumber/listener/json_formatter_wrapper.js index 8d5e6e7f0..e9e5df0c5 100644 --- a/lib/cucumber/listener/json_formatter_wrapper.js +++ b/lib/cucumber/listener/json_formatter_wrapper.js @@ -8,6 +8,8 @@ var JsonFormatterWrapper = function(io) { var Cucumber = require('../../cucumber'); var self = Cucumber.Listener(); + var parentFeatureTags; + function formatStep(step) { var stepProperties = {name: step.getName(), line: step.getLine(), keyword: step.getKeyword()}; @@ -20,6 +22,22 @@ var JsonFormatterWrapper = function(io) { } + function formatTags(tags, parent_tags) { + var tagsProperties = []; + for (tag in tags) { + var isParentTag = false; + for (parent_tag in parent_tags) { + if ((tags[tag].getName() == parent_tags[parent_tag].getName()) && (tags[tag].getLine() == parent_tags[parent_tag].getLine())) { + isParentTag = true; + } + } + if (!isParentTag) { + tagsProperties.push({name: tags[tag].getName(), line: tags[tag].getLine()}); + } + } + return tagsProperties; + } + self.handleBeforeFeatureEvent = function handleBeforeFeatureEvent(event, callback) { var feature = event.getPayloadItem('feature'); @@ -27,11 +45,20 @@ var JsonFormatterWrapper = function(io) { formatter.uri(feature.getUri()); - formatter.feature({id: currentFeatureId, - name: feature.getName(), - description: feature.getDescription(), - line: feature.getLine(), - keyword: feature.getKeyword()}); + var featureProperties = {id: currentFeatureId, + name: feature.getName(), + description: feature.getDescription(), + line: feature.getLine(), + keyword: feature.getKeyword()} + + var tags = feature.getTags(); + if (tags.length > 0) { + featureProperties['tags'] = formatTags(tags, []); + } + + formatter.feature(featureProperties); + + parentFeatureTags = tags; callback(); } @@ -53,13 +80,12 @@ var JsonFormatterWrapper = function(io) { var tags = scenario.getTags(); if (tags.length > 0) { - var tagsProperties = []; - for (tag in tags) { - tagsProperties.push({name: tags[tag].getName(), line: tags[tag].getLine()}); + var tagsProperties = formatTags(tags, parentFeatureTags); + if (tagsProperties.length > 0) { + scenarioProperties['tags'] = tagsProperties; } - scenarioProperties['tags'] = tagsProperties; } - + formatter.scenario(scenarioProperties); callback(); diff --git a/spec/cucumber/listener/json_formatter_wrapper_spec.js b/spec/cucumber/listener/json_formatter_wrapper_spec.js index d30edc340..0cf7f7790 100644 --- a/spec/cucumber/listener/json_formatter_wrapper_spec.js +++ b/spec/cucumber/listener/json_formatter_wrapper_spec.js @@ -5,6 +5,8 @@ describe("Cucumber.Listener.JsonFormatterWrapper", function() { var listener, failedStepResults; var buffer = new Buffer(1024); + var parentFeatureTags; + beforeEach(function() { buffer.fill(0); listener = Cucumber.Listener.JsonFormatterWrapper(buffer); From 1cdfafb3aa0fc214c3f6dbe873cb636097be9e73 Mon Sep 17 00:00:00 2001 From: Chris Young Date: Mon, 23 Jul 2012 14:05:49 +0100 Subject: [PATCH 28/44] Added support for Data Tables but with line for each row stubbed out as 'TODO' --- features/json_formatter.feature | 92 ++++++++++++++++++- .../listener/json_formatter_wrapper.js | 10 ++ .../listener/json_formatter_wrapper_spec.js | 22 +++-- 3 files changed, 114 insertions(+), 10 deletions(-) diff --git a/features/json_formatter.feature b/features/json_formatter.feature index b94390200..a434d21d0 100644 --- a/features/json_formatter.feature +++ b/features/json_formatter.feature @@ -1193,4 +1193,94 @@ Scenario: one feature, one passing scenario, one failing scenario ] """ - # TODO: Embedings + Scenario: Step with table + # Rows do not appear to support line attribute yet. + Given a file named "features/a.feature" with: + """ + Feature: some feature + + Scenario: This scenario contains a step with a table + Given This table: + |col 1|col 2|col 3| + |one |two |three| + |1 |2 |3 | + |! |~ |@ | + """ + And a file named "features/step_definitions/cucumber_steps.js" with: + """ + var cucumberSteps = function() { + this.Given(/^This table:$/, function(table, callback) { callback(); }); + }; + module.exports = cucumberSteps; + """ + When I run `cucumber.js -f json` + Then it should output this json: + """ + [ + { + "id": "some-feature", + "name": "some feature", + "description": "", + "line": 1, + "keyword": "Feature", + "uri": "$CUCUMBER_JS_HOME/tmp/cucumber-js-sandbox/features/a.feature", + "elements": [ + { + "name": "This scenario contains a step with a table", + "id": "some-feature;this-scenario-contains-a-step-with-a-table", + "line": 3, + "keyword": "Scenario", + "description": "", + "type": "scenario", + "steps": [ + { + "name": "This table:", + "line": 4, + "keyword": "Given ", + "rows": [ + { + "line": "TODO", + "cells": [ + "col 1", + "col 2", + "col 3" + ] + }, + { + "line": "TODO", + "cells": [ + "one", + "two", + "three" + ] + }, + { + "line": "TODO", + "cells": [ + "1", + "2", + "3" + ] + }, + { + "line": "TODO", + "cells": [ + "!", + "~", + "@" + ] + } + ], + "result": { + "status": "passed" + }, + "match": { + "location": "TODO" + } + } + ] + } + ] + } + ] + """ \ No newline at end of file diff --git a/lib/cucumber/listener/json_formatter_wrapper.js b/lib/cucumber/listener/json_formatter_wrapper.js index e9e5df0c5..67592d759 100644 --- a/lib/cucumber/listener/json_formatter_wrapper.js +++ b/lib/cucumber/listener/json_formatter_wrapper.js @@ -17,6 +17,16 @@ var JsonFormatterWrapper = function(io) { var docString = step.getDocString(); stepProperties['doc_string'] = {value: docString.getContents(), line: docString.getLine(), content_type: docString.getContentType()}; } + if (step.hasDataTable()) { + var tableContents = step.getDataTable().getContents(); + var raw = tableContents.raw(); + var tableProperties = []; + for (rawRow in raw) { + var row = {line: 'TODO', cells: raw[rawRow]}; + tableProperties.push(row); + } + stepProperties['rows'] = tableProperties; + } formatter.step(stepProperties); diff --git a/spec/cucumber/listener/json_formatter_wrapper_spec.js b/spec/cucumber/listener/json_formatter_wrapper_spec.js index 0cf7f7790..bd1160a4c 100644 --- a/spec/cucumber/listener/json_formatter_wrapper_spec.js +++ b/spec/cucumber/listener/json_formatter_wrapper_spec.js @@ -5,9 +5,7 @@ describe("Cucumber.Listener.JsonFormatterWrapper", function() { var listener, failedStepResults; var buffer = new Buffer(1024); - var parentFeatureTags; - - beforeEach(function() { + beforeEach(function() { buffer.fill(0); listener = Cucumber.Listener.JsonFormatterWrapper(buffer); }); @@ -27,7 +25,8 @@ describe("Cucumber.Listener.JsonFormatterWrapper", function() { getName: 'A Name', getDescription: 'A Description', getLine: 3, - getUri: 'TODO'}); + getUri: 'TODO', + getTags: false}); event = createSpyWithStubs("event", {getPayloadItem: feature}); @@ -71,7 +70,8 @@ describe("Cucumber.Listener.JsonFormatterWrapper", function() { getName: 'A Name', getDescription: 'A Description', getLine: 3, - getUri: 'feature-uri'}); + getUri: 'feature-uri', + getTags: false}); parent_feature_event = createSpyWithStubs("event", {getPayloadItem: feature}); @@ -79,7 +79,8 @@ describe("Cucumber.Listener.JsonFormatterWrapper", function() { getName: 'Step', getLine: 3, getKeyword: 'Step', - hasDocString: false + hasDocString: false, + hasDataTable: false }); steps = [step]; @@ -121,7 +122,8 @@ describe("Cucumber.Listener.JsonFormatterWrapper", function() { getName: 'A Name', getDescription: 'A Description', getLine: 3, - getUri: 'feature-uri'}); + getUri: 'feature-uri', + getTags: false}); parent_feature_event = createSpyWithStubs("event", {getPayloadItem: feature}); @@ -162,7 +164,8 @@ describe("Cucumber.Listener.JsonFormatterWrapper", function() { getName: 'A Name', getDescription: 'A Description', getLine: 3, - getUri: 'feature-uri'}); + getUri: 'feature-uri', + getTags: false}); parent_feature_event = createSpyWithStubs("event", {getPayloadItem: feature}); @@ -179,7 +182,8 @@ describe("Cucumber.Listener.JsonFormatterWrapper", function() { getName: 'Step', getLine: 3, getKeyword: 'Step', - hasDocString: false + hasDocString: false, + hasDataTable: false }); stepResult = createSpyWithStubs("step result", { From 0d62fb80f56373ffb857b9ddb9a1a93364f1f750 Mon Sep 17 00:00:00 2001 From: Chris Young Date: Mon, 23 Jul 2012 14:13:09 +0100 Subject: [PATCH 29/44] Added scenario to test for a background with a table --- features/json_formatter.feature | 87 ++++++++++++++++++++++++++++++++- 1 file changed, 86 insertions(+), 1 deletion(-) diff --git a/features/json_formatter.feature b/features/json_formatter.feature index a434d21d0..fa7221e87 100644 --- a/features/json_formatter.feature +++ b/features/json_formatter.feature @@ -1283,4 +1283,89 @@ Scenario: one feature, one passing scenario, one failing scenario ] } ] - """ \ No newline at end of file + """ + + Scenario: Background with table + # Rows do not appear to support line attribute yet. + Given a file named "features/a.feature" with: + """ + Feature: some feature + + Background: + Given This table: + |col 1|col 2|col 3| + |one |two |three| + |1 |2 |3 | + |! |~ |@ | + """ + And a file named "features/step_definitions/cucumber_steps.js" with: + """ + var cucumberSteps = function() { + this.Given(/^This table:$/, function(table, callback) { callback(); }); + }; + module.exports = cucumberSteps; + """ + When I run `cucumber.js -f json` + Then it should output this json: + """ + [ + { + "id": "some-feature", + "name": "some feature", + "description": "", + "line": 1, + "keyword": "Feature", + "uri": "$CUCUMBER_JS_HOME/tmp/cucumber-js-sandbox/features/a.feature", + "elements": [ + { + "name": "", + "keyword": "Background", + "description": "", + "type": "background", + "line": 3, + "steps": [ + { + "name": "This table:", + "line": 4, + "keyword": "Given ", + "rows": [ + { + "line": "TODO", + "cells": [ + "col 1", + "col 2", + "col 3" + ] + }, + { + "line": "TODO", + "cells": [ + "one", + "two", + "three" + ] + }, + { + "line": "TODO", + "cells": [ + "1", + "2", + "3" + ] + }, + { + "line": "TODO", + "cells": [ + "!", + "~", + "@" + ] + } + ] + } + ] + } + ] + } + ] + """ \ No newline at end of file From 0e92cced2832592f799fd6425066ee0f80abddee Mon Sep 17 00:00:00 2001 From: Chris Young Date: Mon, 23 Jul 2012 14:16:39 +0100 Subject: [PATCH 30/44] Added scenario to test for background with DocString --- features/json_formatter.feature | 60 +++++++++++++++++++++++++++++++-- 1 file changed, 57 insertions(+), 3 deletions(-) diff --git a/features/json_formatter.feature b/features/json_formatter.feature index fa7221e87..86eef573a 100644 --- a/features/json_formatter.feature +++ b/features/json_formatter.feature @@ -1055,7 +1055,61 @@ Scenario: one feature, one passing scenario, one failing scenario ] """ - Scenario: Feature with tags + Scenario: output JSON for background step with a DocString + Given a file named "features/a.feature" with: + """ + Feature: some feature + + Background: Background with DocString + Given we have this DocString: + \"\"\" + This is a DocString + \"\"\" + """ + And a file named "features/step_definitions/cucumber_steps.js" with: + """ + var cucumberSteps = function() { + this.Given(/^we have this DocString:$/, function(string, callback) { callback(); }); + }; + module.exports = cucumberSteps; + """ + When I run `cucumber.js -f json` + Then it should output this json: + """ + [ + { + "id": "some-feature", + "name": "some feature", + "description": "", + "line": 1, + "keyword": "Feature", + "uri": "/Users/chris/src/cucumber-js-fork/tmp/cucumber-js-sandbox/features/a.feature", + "elements": [ + { + "name": "Background with DocString", + "keyword": "Background", + "description": "", + "type": "background", + "line": 3, + "steps": [ + { + "name": "we have this DocString:", + "line": 4, + "keyword": "Given ", + "doc_string": { + "value": "This is a DocString", + "line": 5, + "content_type": "" + } + } + ] + } + ] + } + ] + """ + + Scenario: output JSON for a feature with tags Given a file named "features/a.feature" with: """ @@ -1193,7 +1247,7 @@ Scenario: one feature, one passing scenario, one failing scenario ] """ - Scenario: Step with table + Scenario: output JSON for a step with table # Rows do not appear to support line attribute yet. Given a file named "features/a.feature" with: """ @@ -1285,7 +1339,7 @@ Scenario: one feature, one passing scenario, one failing scenario ] """ - Scenario: Background with table + Scenario: output JSON for background with table # Rows do not appear to support line attribute yet. Given a file named "features/a.feature" with: """ From b23ee5a5bf9f5a7ce05f857e84f1f3b12a8956aa Mon Sep 17 00:00:00 2001 From: Chris Young Date: Mon, 23 Jul 2012 17:58:21 +0100 Subject: [PATCH 31/44] Made progress on the jasmine-node tests. In doing this refactored json_formatter_wrapper to take the json_formatter as an input parameter rather than creating it itself. Makes it easier to mock and so easier to test. --- lib/cucumber/cli/configuration.js | 4 +- .../listener/json_formatter_wrapper.js | 18 +- .../listener/json_formatter_wrapper_spec.js | 401 +++++++++++------- 3 files changed, 250 insertions(+), 173 deletions(-) diff --git a/lib/cucumber/cli/configuration.js b/lib/cucumber/cli/configuration.js index 43bbce5b2..237e1505c 100644 --- a/lib/cucumber/cli/configuration.js +++ b/lib/cucumber/cli/configuration.js @@ -16,7 +16,9 @@ var Configuration = function(argv) { formatter = Cucumber.Listener.PrettyFormatter(); break; case Configuration.JSON_FORMAT_NAME: - formatter = Cucumber.Listener.JsonFormatterWrapper(process.stdout); + var JSONFormatter = require('gherkin/lib/gherkin/formatter/json_formatter'); + var jsonFormatter = new JSONFormatter(process.stdout); + formatter = Cucumber.Listener.JsonFormatterWrapper(jsonFormatter); break; case Configuration.SUMMARY_FORMAT_NAME: formatter = Cucumber.Listener.SummaryFormatter(); diff --git a/lib/cucumber/listener/json_formatter_wrapper.js b/lib/cucumber/listener/json_formatter_wrapper.js index 67592d759..0a293ab12 100644 --- a/lib/cucumber/listener/json_formatter_wrapper.js +++ b/lib/cucumber/listener/json_formatter_wrapper.js @@ -1,16 +1,16 @@ -var JsonFormatterWrapper = function(io) { +var JsonFormatterWrapper = function(formatter) { - var JSONFormatter = require('gherkin/lib/gherkin/formatter/json_formatter'); - var formatter = new JSONFormatter(io); + var formatter = formatter; var currentFeatureId = ''; var Cucumber = require('../../cucumber'); + var self = Cucumber.Listener(); var parentFeatureTags; - function formatStep(step) { + self.formatStep = function formatStep(step) { var stepProperties = {name: step.getName(), line: step.getLine(), keyword: step.getKeyword()}; if (step.hasDocString()) { @@ -32,7 +32,7 @@ var JsonFormatterWrapper = function(io) { } - function formatTags(tags, parent_tags) { + self.formatTags = function formatTags(tags, parent_tags) { var tagsProperties = []; for (tag in tags) { var isParentTag = false; @@ -63,7 +63,7 @@ var JsonFormatterWrapper = function(io) { var tags = feature.getTags(); if (tags.length > 0) { - featureProperties['tags'] = formatTags(tags, []); + featureProperties['tags'] = self.formatTags(tags, []); } formatter.feature(featureProperties); @@ -77,7 +77,7 @@ var JsonFormatterWrapper = function(io) { var background = event.getPayloadItem('background'); formatter.background({name: background.getName(), keyword: "Background", description: background.getDescription(), type: "background", line: background.getLine()}) var steps = background.getSteps(); - steps.forEach(function(value, index, ar) { formatStep(value); }); + steps.forEach(function(value, index, ar) { self.formatStep(value); }); callback(); } @@ -90,7 +90,7 @@ var JsonFormatterWrapper = function(io) { var tags = scenario.getTags(); if (tags.length > 0) { - var tagsProperties = formatTags(tags, parentFeatureTags); + var tagsProperties = self.formatTags(tags, parentFeatureTags); if (tagsProperties.length > 0) { scenarioProperties['tags'] = tagsProperties; } @@ -105,7 +105,7 @@ var JsonFormatterWrapper = function(io) { var stepResult = event.getPayloadItem('stepResult'); var step = stepResult.getStep(); - formatStep(step); + self.formatStep(step); var stepOutput = {}; var resultStatus = 'failed'; diff --git a/spec/cucumber/listener/json_formatter_wrapper_spec.js b/spec/cucumber/listener/json_formatter_wrapper_spec.js index bd1160a4c..c4081fe57 100644 --- a/spec/cucumber/listener/json_formatter_wrapper_spec.js +++ b/spec/cucumber/listener/json_formatter_wrapper_spec.js @@ -3,182 +3,164 @@ require('../../support/spec_helper'); describe("Cucumber.Listener.JsonFormatterWrapper", function() { var Cucumber = requireLib('cucumber'); var listener, failedStepResults; - var buffer = new Buffer(1024); - - beforeEach(function() { - buffer.fill(0); - listener = Cucumber.Listener.JsonFormatterWrapper(buffer); - }); describe("constructor", function() { + // TODO! }); // Handle Feature - describe("handleBeforeFeatureEvent()", function() { - var event, feature, callback; + // describe("handleBeforeFeatureEvent()", function() { + // var event, feature, callback; - beforeEach(function() { - feature = createSpyWithStubs("feature", - {getKeyword: 'Feature', - getName: 'A Name', - getDescription: 'A Description', - getLine: 3, - getUri: 'TODO', - getTags: false}); + // beforeEach(function() { + // feature = createSpyWithStubs("feature", + // {getKeyword: 'Feature', + // getName: 'A Name', + // getDescription: 'A Description', + // getLine: 3, + // getUri: 'TODO', + // getTags: false}); - event = createSpyWithStubs("event", {getPayloadItem: feature}); + // event = createSpyWithStubs("event", {getPayloadItem: feature}); - callback = createSpy("callback"); - }); + // callback = createSpy("callback"); + // }); - it("adds the feature attributes to the output", function() { - listener.handleBeforeFeatureEvent(event, callback); - listener.handleAfterFeaturesEvent(event, callback); + // it("adds the feature attributes to the output", function() { + // listener.handleBeforeFeatureEvent(event, callback); + // listener.handleAfterFeaturesEvent(event, callback); - var output = buffer.toString(); - output = output.substr(0,output.indexOf(String.fromCharCode(0))); + // var output = buffer.toString(); + // output = output.substr(0,output.indexOf(String.fromCharCode(0))); - var expectedOutput = '[ { "id": "A-Name", \ - "name": "A Name", \ - "description": "A Description", \ - "line": 3, \ - "keyword": "Feature", \ - "uri": "TODO" } ]'; + // var expectedOutput = '[ { "id": "A-Name", \ + // "name": "A Name", \ + // "description": "A Description", \ + // "line": 3, \ + // "keyword": "Feature", \ + // "uri": "TODO" } ]'; - var expectedJson = JSON.parse(expectedOutput); - var expectedJsonString = JSON.stringify(expectedJson, null, 2); - var actualJson = JSON.parse(output); - var actualJsonString = JSON.stringify(actualJson, null, 2); + // var expectedJson = JSON.parse(expectedOutput); + // var expectedJsonString = JSON.stringify(expectedJson, null, 2); + // var actualJson = JSON.parse(output); + // var actualJsonString = JSON.stringify(actualJson, null, 2); - expect(actualJsonString).toEqual(expectedJsonString); + // expect(actualJsonString).toEqual(expectedJsonString); - }); + // }); - }); + // }); // Handle Background - describe("handleBackgroundEvent()", function() { +// describe("handleBackgroundEvent()", function() { - var parent_feature_event, background, step, steps, event, callback; +// var parent_feature_event, background, step, steps, event, callback; - beforeEach(function() { - feature = createSpyWithStubs("feature", - {getKeyword: 'Feature', - getName: 'A Name', - getDescription: 'A Description', - getLine: 3, - getUri: 'feature-uri', - getTags: false}); +// beforeEach(function() { +// feature = createSpyWithStubs("feature", +// {getKeyword: 'Feature', +// getName: 'A Name', +// getDescription: 'A Description', +// getLine: 3, +// getUri: 'feature-uri', +// getTags: false}); - parent_feature_event = createSpyWithStubs("event", {getPayloadItem: feature}); +// parent_feature_event = createSpyWithStubs("event", {getPayloadItem: feature}); - step = createSpyWithStubs("step", { - getName: 'Step', - getLine: 3, - getKeyword: 'Step', - hasDocString: false, - hasDataTable: false - }); +// step = createSpyWithStubs("step", { +// getName: 'Step', +// getLine: 3, +// getKeyword: 'Step', +// hasDocString: false, +// hasDataTable: false +// }); - steps = [step]; +// steps = [step]; - background = createSpyWithStubs("background", - {getKeyword: 'Background', - getName: 'A Name', - getDescription: 'A Description', - getLine: 3, - getSteps: steps}); +// background = createSpyWithStubs("background", +// {getKeyword: 'Background', +// getName: 'A Name', +// getDescription: 'A Description', +// getLine: 3, +// getSteps: steps}); - event = createSpyWithStubs("event", {getPayloadItem: background}); - callback = createSpy("callback"); - }); +// event = createSpyWithStubs("event", {getPayloadItem: background}); +// callback = createSpy("callback"); +// }); - it("adds the background attributes to the output", function() { - listener.handleBeforeFeatureEvent(parent_feature_event, callback); - listener.handleBackgroundEvent(event, callback); - listener.handleAfterFeaturesEvent(parent_feature_event, callback); - var output = buffer.toString(); - output = output.substr(0,output.indexOf(String.fromCharCode(0))); +// it("adds the background attributes to the output", function() { +// listener.handleBeforeFeatureEvent(parent_feature_event, callback); +// listener.handleBackgroundEvent(event, callback); +// listener.handleAfterFeaturesEvent(parent_feature_event, callback); +// var output = buffer.toString(); +// output = output.substr(0,output.indexOf(String.fromCharCode(0))); - var expected = '[{"id":"A-Name","name":"A Name","description":"A Description","line":3,"keyword":"Feature","uri":"feature-uri","elements":[{"name":"A Name","keyword":"Background","description":"A Description","type":"background","line":3,"steps":[{"name":"Step","line":3,"keyword":"Step"}]}]}]'; +// var expected = '[{"id":"A-Name","name":"A Name","description":"A Description","line":3,"keyword":"Feature","uri":"feature-uri","elements":[{"name":"A Name","keyword":"Background","description":"A Description","type":"background","line":3,"steps":[{"name":"Step","line":3,"keyword":"Step"}]}]}]'; - expect(output).toEqual(expected); +// expect(output).toEqual(expected); - }); +// }); -}); +// }); // Handle Scenario - describe("handleBeforeScenarioEvent()", function() { - var parent_feature_event, scenario, callback; + // describe("handleBeforeScenarioEvent()", function() { + // var parent_feature_event, scenario, callback; - beforeEach(function() { - feature = createSpyWithStubs("feature", - {getKeyword: 'Feature', - getName: 'A Name', - getDescription: 'A Description', - getLine: 3, - getUri: 'feature-uri', - getTags: false}); - - parent_feature_event = createSpyWithStubs("event", {getPayloadItem: feature}); - - scenario = createSpyWithStubs("scenario", - {getKeyword: 'Scenario', - getName: 'A Name', - getDescription: 'A Description', - getLine: 3, - getTags: false}); - - event = createSpyWithStubs("event", {getPayloadItem: scenario}); - callback = createSpy("callback"); - }); + // beforeEach(function() { + // feature = createSpyWithStubs("feature", + // {getKeyword: 'Feature', + // getName: 'A Name', + // getDescription: 'A Description', + // getLine: 3, + // getUri: 'feature-uri', + // getTags: false}); - it("adds the scenario attributes to the output", function() { - listener.handleBeforeFeatureEvent(parent_feature_event, callback); - listener.handleBeforeScenarioEvent(event, callback); - listener.handleAfterFeaturesEvent(parent_feature_event, callback); - var output = buffer.toString(); - output = output.substr(0,output.indexOf(String.fromCharCode(0))); + // parent_feature_event = createSpyWithStubs("event", {getPayloadItem: feature}); - var expected = '[{"id":"A-Name","name":"A Name","description":"A Description","line":3,"keyword":"Feature","uri":"feature-uri","elements":[{"name":"A Name","id":"A-Name;a-name","line":3,"keyword":"Scenario","description":"A Description","type":"scenario"}]}]'; + // scenario = createSpyWithStubs("scenario", + // {getKeyword: 'Scenario', + // getName: 'A Name', + // getDescription: 'A Description', + // getLine: 3, + // getTags: false}); - expect(output).toEqual(expected); + // event = createSpyWithStubs("event", {getPayloadItem: scenario}); + // callback = createSpy("callback"); + // }); - }); + // it("adds the scenario attributes to the output", function() { + // listener.handleBeforeFeatureEvent(parent_feature_event, callback); + // listener.handleBeforeScenarioEvent(event, callback); + // listener.handleAfterFeaturesEvent(parent_feature_event, callback); + // var output = buffer.toString(); + // output = output.substr(0,output.indexOf(String.fromCharCode(0))); - }); + // var expected = '[{"id":"A-Name","name":"A Name","description":"A Description","line":3,"keyword":"Feature","uri":"feature-uri","elements":[{"name":"A Name","id":"A-Name;a-name","line":3,"keyword":"Scenario","description":"A Description","type":"scenario"}]}]'; - // Handle Step Results + // expect(output).toEqual(expected); + + // }); - describe("handleStepResultEvent()", function() { - var parent_feature_event, feature, parent_scenario_event, scenario, event, callback, stepResult; + // }); + + // Step Formatting + + describe("formatStep()", function() { + + var fakeFormatter = createSpyObj('formatter', ['step']); beforeEach(function() { - feature = createSpyWithStubs("feature", - {getKeyword: 'Feature', - getName: 'A Name', - getDescription: 'A Description', - getLine: 3, - getUri: 'feature-uri', - getTags: false}); - - parent_feature_event = createSpyWithStubs("event", {getPayloadItem: feature}); - - scenario = createSpyWithStubs("scenario", - {getKeyword: 'Scenario', - getName: 'A Name', - getDescription: 'A Description', - getLine: 3, - getTags: false}); - - parent_scenario_event = createSpyWithStubs("event", {getPayloadItem: scenario}); - - step = createSpyWithStubs("step", { + listener = Cucumber.Listener.JsonFormatterWrapper(fakeFormatter); + }); + + it("adds name, line and keyword to the step properties", function(){ + + var step = createSpyWithStubs("step", { getName: 'Step', getLine: 3, getKeyword: 'Step', @@ -186,57 +168,150 @@ describe("Cucumber.Listener.JsonFormatterWrapper", function() { hasDataTable: false }); - stepResult = createSpyWithStubs("step result", { - isSuccessful: undefined, - isPending: undefined, - isFailed: undefined, - isSkipped: undefined, - isUndefined: undefined, - getStep: step + listener.formatStep(step); + expect(fakeFormatter.step).toHaveBeenCalledWith({ name : 'Step', line : 3, keyword : 'Step'}); + + }); + + it("if the step has one, adds a DocString to the step properties", function(){ + + var fakeDocString = createSpyWithStubs("docString", { + getContents: "This is a DocString", + getLine: 3, + getContentType: null}); + + var step = createSpyWithStubs("step", { + getName: 'Step', + getLine: 3, + getKeyword: 'Step', + hasDocString: true, + hasDataTable: false, + getDocString: fakeDocString }); - event = createSpyWithStubs("event", {getPayloadItem: stepResult}); - callback = createSpy("Callback"); + listener.formatStep(step); + expect(fakeFormatter.step).toHaveBeenCalledWith({ name : 'Step', line : 3, keyword : 'Step', doc_string : { value : 'This is a DocString', line : 3, content_type : null } }); + }); + it("if the step has one, adds a DataTable to the step properties", function(){ - it("adds the step result to the parent scenario in the output", function(){ - stepResult.isSuccessful.andReturn(true); - listener.handleBeforeFeatureEvent(parent_feature_event, callback); - listener.handleBeforeScenarioEvent(parent_scenario_event, callback); - listener.handleStepResultEvent(event, callback); - listener.handleAfterFeaturesEvent(parent_feature_event, callback); + var fakeContents = createSpyWithStubs("row", { + raw: "RAW DATA" + }) + + var fakeDataTable = createSpyWithStubs("dataTable", { + getContents: fakeContents + }); + + var stepTwo = createSpyWithStubs("step", { + getName: 'Step', + getLine: 3, + getKeyword: 'Step', + hasDocString: false, + hasDataTable: true, + getDataTable: fakeDataTable + }); - var output = buffer.toString(); - output = output.substr(0,output.indexOf(String.fromCharCode(0))); + listener.formatStep(stepTwo); + // expect(fakeFormatter.step).toHaveBeenCalledWith("FOO"); + }); - var expected = '[{"id":"A-Name","name":"A Name","description":"A Description","line":3,"keyword":"Feature","uri":"feature-uri","elements":[{"name":"A Name","id":"A-Name;a-name","line":3,"keyword":"Scenario","description":"A Description","type":"scenario","steps":[{"name":"Step","line":3,"keyword":"Step","result":{"status":"passed"},"match":{"location":"TODO"}}]}]}]'; + }); - expect(output).toEqual(expected); + describe("formatTags()", function() { + it("returns the given tags in the format expected by the JSON formatter", function(){ + }); + it("filters out any tags it is told to ignore - e.g. those of the parent feature", function(){ }); }); - // We're all done. Output the JSON. + // Handle Step Results - describe("handleAfterFeaturesEvent()", function() { - var features, callback; + // describe("handleStepResultEvent()", function() { + // var parent_feature_event, feature, parent_scenario_event, scenario, event, callback, stepResult; - beforeEach(function() { - event = createSpy("Event"); - callback = createSpy("Callback"); + // beforeEach(function() { + // feature = createSpyWithStubs("feature", + // {getKeyword: 'Feature', + // getName: 'A Name', + // getDescription: 'A Description', + // getLine: 3, + // getUri: 'feature-uri', + // getTags: false}); + + // parent_feature_event = createSpyWithStubs("event", {getPayloadItem: feature}); + + // scenario = createSpyWithStubs("scenario", + // {getKeyword: 'Scenario', + // getName: 'A Name', + // getDescription: 'A Description', + // getLine: 3, + // getTags: false}); + + // parent_scenario_event = createSpyWithStubs("event", {getPayloadItem: scenario}); + + // step = createSpyWithStubs("step", { + // getName: 'Step', + // getLine: 3, + // getKeyword: 'Step', + // hasDocString: false, + // hasDataTable: false + // }); + + // stepResult = createSpyWithStubs("step result", { + // isSuccessful: undefined, + // isPending: undefined, + // isFailed: undefined, + // isSkipped: undefined, + // isUndefined: undefined, + // getStep: step + // }); + + // event = createSpyWithStubs("event", {getPayloadItem: stepResult}); + // callback = createSpy("Callback"); + // }); + + + // it("adds the step result to the parent scenario in the output", function(){ + // stepResult.isSuccessful.andReturn(true); + // listener.handleBeforeFeatureEvent(parent_feature_event, callback); + // listener.handleBeforeScenarioEvent(parent_scenario_event, callback); + // listener.handleStepResultEvent(event, callback); + // listener.handleAfterFeaturesEvent(parent_feature_event, callback); + + // var output = buffer.toString(); + // output = output.substr(0,output.indexOf(String.fromCharCode(0))); + + // var expected = '[{"id":"A-Name","name":"A Name","description":"A Description","line":3,"keyword":"Feature","uri":"feature-uri","elements":[{"name":"A Name","id":"A-Name;a-name","line":3,"keyword":"Scenario","description":"A Description","type":"scenario","steps":[{"name":"Step","line":3,"keyword":"Step","result":{"status":"passed"},"match":{"location":"TODO"}}]}]}]'; + + // expect(output).toEqual(expected); + + // }); + + // }); + + // // We're all done. Output the JSON. + + // describe("handleAfterFeaturesEvent()", function() { + // var features, callback; + + // beforeEach(function() { + // event = createSpy("Event"); + // callback = createSpy("Callback"); - }); + // }); - // TODO: What else should we test here? e.g. calls made to the formatter? + // // TODO: What else should we test here? e.g. calls made to the formatter? - it("calls back", function() { - listener.handleAfterFeaturesEvent(event, callback); - expect(callback).toHaveBeenCalled(); - }); + // it("calls back", function() { + // listener.handleAfterFeaturesEvent(event, callback); + // expect(callback).toHaveBeenCalled(); + // }); - }); + // }); }); From a548a3bf85a4acbae1ec0689f502d186d46b1716 Mon Sep 17 00:00:00 2001 From: Chris Young Date: Tue, 24 Jul 2012 10:18:57 +0100 Subject: [PATCH 32/44] Fixed up Jasmine tests to make them test _just_ the json_formatter_wrapper by properly faking the Gherkin json_formatter and testing for calls to it. --- .../listener/json_formatter_wrapper.js | 6 +- .../listener/json_formatter_wrapper_spec.js | 372 +++++++++--------- 2 files changed, 187 insertions(+), 191 deletions(-) diff --git a/lib/cucumber/listener/json_formatter_wrapper.js b/lib/cucumber/listener/json_formatter_wrapper.js index 0a293ab12..d3edf59f4 100644 --- a/lib/cucumber/listener/json_formatter_wrapper.js +++ b/lib/cucumber/listener/json_formatter_wrapper.js @@ -2,7 +2,7 @@ var JsonFormatterWrapper = function(formatter) { var formatter = formatter; - var currentFeatureId = ''; + var currentFeatureId = 'undefined'; var Cucumber = require('../../cucumber'); @@ -75,7 +75,7 @@ var JsonFormatterWrapper = function(formatter) { self.handleBackgroundEvent = function handleBackgroundEvent(event, callback) { var background = event.getPayloadItem('background'); - formatter.background({name: background.getName(), keyword: "Background", description: background.getDescription(), type: "background", line: background.getLine()}) + formatter.background({name: background.getName(), keyword: "Background", description: background.getDescription(), type: 'background', line: background.getLine()}) var steps = background.getSteps(); steps.forEach(function(value, index, ar) { self.formatStep(value); }); callback(); @@ -86,7 +86,7 @@ var JsonFormatterWrapper = function(formatter) { var scenario = event.getPayloadItem('scenario'); var id = currentFeatureId + ';' + scenario.getName().replace(/ /g, '-').toLowerCase(); - var scenarioProperties = {name: scenario.getName(), id: id, line: scenario.getLine(), keyword: "Scenario", description: scenario.getDescription(), type: "scenario"}; + var scenarioProperties = {name: scenario.getName(), id: id, line: scenario.getLine(), keyword: 'Scenario', description: scenario.getDescription(), type: 'scenario'}; var tags = scenario.getTags(); if (tags.length > 0) { diff --git a/spec/cucumber/listener/json_formatter_wrapper_spec.js b/spec/cucumber/listener/json_formatter_wrapper_spec.js index c4081fe57..700147578 100644 --- a/spec/cucumber/listener/json_formatter_wrapper_spec.js +++ b/spec/cucumber/listener/json_formatter_wrapper_spec.js @@ -4,6 +4,12 @@ describe("Cucumber.Listener.JsonFormatterWrapper", function() { var Cucumber = requireLib('cucumber'); var listener, failedStepResults; + var fakeFormatter = createSpyObj('formatter', ['step', 'uri', 'feature', 'background', 'scenario', 'result', 'match', 'eof', 'done']); + + beforeEach(function() { + listener = Cucumber.Listener.JsonFormatterWrapper(fakeFormatter); + }); + describe("constructor", function() { // TODO! }); @@ -11,153 +17,128 @@ describe("Cucumber.Listener.JsonFormatterWrapper", function() { // Handle Feature - // describe("handleBeforeFeatureEvent()", function() { - // var event, feature, callback; - - // beforeEach(function() { - // feature = createSpyWithStubs("feature", - // {getKeyword: 'Feature', - // getName: 'A Name', - // getDescription: 'A Description', - // getLine: 3, - // getUri: 'TODO', - // getTags: false}); - - // event = createSpyWithStubs("event", {getPayloadItem: feature}); + describe("handleBeforeFeatureEvent()", function() { + var event, feature, callback; - // callback = createSpy("callback"); - // }); - - // it("adds the feature attributes to the output", function() { - // listener.handleBeforeFeatureEvent(event, callback); - // listener.handleAfterFeaturesEvent(event, callback); - - // var output = buffer.toString(); - // output = output.substr(0,output.indexOf(String.fromCharCode(0))); + beforeEach(function() { + feature = createSpyWithStubs("feature", + {getKeyword: 'Feature', + getName: 'A Name', + getDescription: 'A Description', + getLine: 3, + getUri: 'TODO', + getTags: false}); - // var expectedOutput = '[ { "id": "A-Name", \ - // "name": "A Name", \ - // "description": "A Description", \ - // "line": 3, \ - // "keyword": "Feature", \ - // "uri": "TODO" } ]'; + event = createSpyWithStubs("event", {getPayloadItem: feature}); - // var expectedJson = JSON.parse(expectedOutput); - // var expectedJsonString = JSON.stringify(expectedJson, null, 2); - // var actualJson = JSON.parse(output); - // var actualJsonString = JSON.stringify(actualJson, null, 2); + callback = createSpy("callback"); + }); - // expect(actualJsonString).toEqual(expectedJsonString); + it("adds the feature attributes to the output", function() { + listener.handleBeforeFeatureEvent(event, callback); + expect(fakeFormatter.uri).toHaveBeenCalledWith('TODO'); + expect(fakeFormatter.feature).toHaveBeenCalledWith({id: 'A-Name', + name: 'A Name', + description: 'A Description', + line: 3, + keyword: 'Feature'}); - // }); + }); - // }); + }); // Handle Background -// describe("handleBackgroundEvent()", function() { - -// var parent_feature_event, background, step, steps, event, callback; - -// beforeEach(function() { -// feature = createSpyWithStubs("feature", -// {getKeyword: 'Feature', -// getName: 'A Name', -// getDescription: 'A Description', -// getLine: 3, -// getUri: 'feature-uri', -// getTags: false}); - -// parent_feature_event = createSpyWithStubs("event", {getPayloadItem: feature}); + describe("handleBackgroundEvent()", function() { -// step = createSpyWithStubs("step", { -// getName: 'Step', -// getLine: 3, -// getKeyword: 'Step', -// hasDocString: false, -// hasDataTable: false -// }); + var parent_feature_event, background, step, steps, event, callback; -// steps = [step]; + beforeEach(function() { + feature = createSpyWithStubs("feature", + {getKeyword: 'Feature', + getName: 'A Name', + getDescription: 'A Description', + getLine: 3, + getUri: 'feature-uri', + getTags: false}); -// background = createSpyWithStubs("background", -// {getKeyword: 'Background', -// getName: 'A Name', -// getDescription: 'A Description', -// getLine: 3, -// getSteps: steps}); + parent_feature_event = createSpyWithStubs("event", {getPayloadItem: feature}); -// event = createSpyWithStubs("event", {getPayloadItem: background}); -// callback = createSpy("callback"); -// }); + step = createSpyWithStubs("step", { + getName: 'Step', + getLine: 3, + getKeyword: 'Step', + hasDocString: false, + hasDataTable: false + }); -// it("adds the background attributes to the output", function() { -// listener.handleBeforeFeatureEvent(parent_feature_event, callback); -// listener.handleBackgroundEvent(event, callback); -// listener.handleAfterFeaturesEvent(parent_feature_event, callback); -// var output = buffer.toString(); -// output = output.substr(0,output.indexOf(String.fromCharCode(0))); + steps = [step]; -// var expected = '[{"id":"A-Name","name":"A Name","description":"A Description","line":3,"keyword":"Feature","uri":"feature-uri","elements":[{"name":"A Name","keyword":"Background","description":"A Description","type":"background","line":3,"steps":[{"name":"Step","line":3,"keyword":"Step"}]}]}]'; + background = createSpyWithStubs("background", + {getKeyword: 'Background', + getName: 'A Name', + getDescription: 'A Description', + getLine: 3, + getSteps: steps}); -// expect(output).toEqual(expected); + event = createSpyWithStubs("event", {getPayloadItem: background}); + callback = createSpy("callback"); + }); -// }); + it("adds the background attributes to the output", function() { + listener.handleBackgroundEvent(event, callback); + expect(fakeFormatter.background).toHaveBeenCalledWith({name: 'A Name', + keyword: 'Background', + description: 'A Description', + type: 'background', + line: 3 }); + }); -// }); + }); // Handle Scenario - // describe("handleBeforeScenarioEvent()", function() { - // var parent_feature_event, scenario, callback; - - // beforeEach(function() { - // feature = createSpyWithStubs("feature", - // {getKeyword: 'Feature', - // getName: 'A Name', - // getDescription: 'A Description', - // getLine: 3, - // getUri: 'feature-uri', - // getTags: false}); - - // parent_feature_event = createSpyWithStubs("event", {getPayloadItem: feature}); - - // scenario = createSpyWithStubs("scenario", - // {getKeyword: 'Scenario', - // getName: 'A Name', - // getDescription: 'A Description', - // getLine: 3, - // getTags: false}); + describe("handleBeforeScenarioEvent()", function() { + var parent_feature_event, scenario, callback; - // event = createSpyWithStubs("event", {getPayloadItem: scenario}); - // callback = createSpy("callback"); - // }); - - // it("adds the scenario attributes to the output", function() { - // listener.handleBeforeFeatureEvent(parent_feature_event, callback); - // listener.handleBeforeScenarioEvent(event, callback); - // listener.handleAfterFeaturesEvent(parent_feature_event, callback); - // var output = buffer.toString(); - // output = output.substr(0,output.indexOf(String.fromCharCode(0))); - - // var expected = '[{"id":"A-Name","name":"A Name","description":"A Description","line":3,"keyword":"Feature","uri":"feature-uri","elements":[{"name":"A Name","id":"A-Name;a-name","line":3,"keyword":"Scenario","description":"A Description","type":"scenario"}]}]'; - - // expect(output).toEqual(expected); + beforeEach(function() { + feature = createSpyWithStubs("feature", + {getKeyword: 'Feature', + getName: 'A Name', + getDescription: 'A Description', + getLine: 3, + getUri: 'feature-uri', + getTags: false}); + + parent_feature_event = createSpyWithStubs("event", {getPayloadItem: feature}); + + scenario = createSpyWithStubs("scenario", + {getKeyword: 'Scenario', + getName: 'A Name', + getDescription: 'A Description', + getLine: 3, + getTags: false}); + + event = createSpyWithStubs("event", {getPayloadItem: scenario}); + callback = createSpy("callback"); + }); - // }); + it("adds the scenario attributes to the output", function() { + listener.handleBeforeScenarioEvent(event, callback); + expect(fakeFormatter.scenario).toHaveBeenCalledWith({name: 'A Name', + id: 'undefined;a-name', + line: 3, + keyword: 'Scenario', + description: 'A Description', + type: 'scenario' }); + }); - // }); + }); // Step Formatting describe("formatStep()", function() { - var fakeFormatter = createSpyObj('formatter', ['step']); - - beforeEach(function() { - listener = Cucumber.Listener.JsonFormatterWrapper(fakeFormatter); - }); - it("adds name, line and keyword to the step properties", function(){ var step = createSpyWithStubs("step", { @@ -190,21 +171,25 @@ describe("Cucumber.Listener.JsonFormatterWrapper", function() { }); listener.formatStep(step); - expect(fakeFormatter.step).toHaveBeenCalledWith({ name : 'Step', line : 3, keyword : 'Step', doc_string : { value : 'This is a DocString', line : 3, content_type : null } }); + expect(fakeFormatter.step).toHaveBeenCalledWith({name: 'Step', + line: 3, + keyword: 'Step', + doc_string: {value: 'This is a DocString', line: 3, content_type: null} + }); }); it("if the step has one, adds a DataTable to the step properties", function(){ var fakeContents = createSpyWithStubs("row", { - raw: "RAW DATA" + raw: [['a:1', 'a:2', 'a:3'],['b:1', 'b:2', 'b:3'],['c:1', 'c:2', 'c:3']] }) var fakeDataTable = createSpyWithStubs("dataTable", { getContents: fakeContents }); - var stepTwo = createSpyWithStubs("step", { + var step = createSpyWithStubs("step", { getName: 'Step', getLine: 3, getKeyword: 'Step', @@ -213,105 +198,116 @@ describe("Cucumber.Listener.JsonFormatterWrapper", function() { getDataTable: fakeDataTable }); - listener.formatStep(stepTwo); - // expect(fakeFormatter.step).toHaveBeenCalledWith("FOO"); + listener.formatStep(step); + expect(fakeFormatter.step).toHaveBeenCalledWith({name: 'Step', + line: 3, + keyword: 'Step', + rows: [{line : 'TODO', cells: ['a:1', 'a:2', 'a:3'] }, + {line : 'TODO', cells: ['b:1', 'b:2', 'b:3'] }, + {line : 'TODO', cells: ['c:1', 'c:2', 'c:3'] }] + }); }); }); + // Tag Formatting + describe("formatTags()", function() { + it("returns the given tags in the format expected by the JSON formatter", function(){ - }); - it("filters out any tags it is told to ignore - e.g. those of the parent feature", function(){ + var tags = [createSpyWithStubs("tag", {getName: "tag_one", getLine:1}), + createSpyWithStubs("tag", {getName: "tag_two", getLine:2}), + createSpyWithStubs("tag", {getName: "tag_three", getLine:3})]; + + expect(listener.formatTags(tags, null)).toEqual([{name: 'tag_one', line :1}, + {name: 'tag_two', line :2}, + {name: 'tag_three', line :3}]); + }); - }); + it("filters out any tags it is told to ignore - e.g. those of the parent feature", function(){ - // Handle Step Results + var tags = [createSpyWithStubs("tag", {getName: "tag_one", getLine:1}), + createSpyWithStubs("tag", {getName: "tag_two", getLine:2}), + createSpyWithStubs("tag", {getName: "parent_one", getLine:3}), + createSpyWithStubs("tag", {getName: "parent_two", getLine:3})]; - // describe("handleStepResultEvent()", function() { - // var parent_feature_event, feature, parent_scenario_event, scenario, event, callback, stepResult; + var parent_tags = [createSpyWithStubs("tag", {getName: "parent_one", getLine:3}), + createSpyWithStubs("tag", {getName: "parent_two", getLine:3})]; - // beforeEach(function() { - // feature = createSpyWithStubs("feature", - // {getKeyword: 'Feature', - // getName: 'A Name', - // getDescription: 'A Description', - // getLine: 3, - // getUri: 'feature-uri', - // getTags: false}); - // parent_feature_event = createSpyWithStubs("event", {getPayloadItem: feature}); + expect(listener.formatTags(tags, parent_tags)).toEqual([{name: 'tag_one', line :1}, + {name: 'tag_two', line :2}]); + }); - // scenario = createSpyWithStubs("scenario", - // {getKeyword: 'Scenario', - // getName: 'A Name', - // getDescription: 'A Description', - // getLine: 3, - // getTags: false}); + }); - // parent_scenario_event = createSpyWithStubs("event", {getPayloadItem: scenario}); + // Handle Step Results - // step = createSpyWithStubs("step", { - // getName: 'Step', - // getLine: 3, - // getKeyword: 'Step', - // hasDocString: false, - // hasDataTable: false - // }); + describe("handleStepResultEvent()", function() { + var parent_feature_event, feature, parent_scenario_event, scenario, event, callback, stepResult; - // stepResult = createSpyWithStubs("step result", { - // isSuccessful: undefined, - // isPending: undefined, - // isFailed: undefined, - // isSkipped: undefined, - // isUndefined: undefined, - // getStep: step - // }); + beforeEach(function() { - // event = createSpyWithStubs("event", {getPayloadItem: stepResult}); - // callback = createSpy("Callback"); - // }); + step = createSpyWithStubs("step", { + getName: 'Step', + getLine: 3, + getKeyword: 'Step', + hasDocString: false, + hasDataTable: false + }); + stepResult = createSpyWithStubs("step result", { + isSuccessful: undefined, + isPending: undefined, + isFailed: undefined, + isSkipped: undefined, + isUndefined: undefined, + getStep: step + }); - // it("adds the step result to the parent scenario in the output", function(){ - // stepResult.isSuccessful.andReturn(true); - // listener.handleBeforeFeatureEvent(parent_feature_event, callback); - // listener.handleBeforeScenarioEvent(parent_scenario_event, callback); - // listener.handleStepResultEvent(event, callback); - // listener.handleAfterFeaturesEvent(parent_feature_event, callback); + event = createSpyWithStubs("event", {getPayloadItem: stepResult}); + callback = createSpy("Callback"); + }); - // var output = buffer.toString(); - // output = output.substr(0,output.indexOf(String.fromCharCode(0))); + // Extra tests for errors etc... - // var expected = '[{"id":"A-Name","name":"A Name","description":"A Description","line":3,"keyword":"Feature","uri":"feature-uri","elements":[{"name":"A Name","id":"A-Name;a-name","line":3,"keyword":"Scenario","description":"A Description","type":"scenario","steps":[{"name":"Step","line":3,"keyword":"Step","result":{"status":"passed"},"match":{"location":"TODO"}}]}]}]'; + it("outputs a step and its result", function(){ + stepResult.isSuccessful.andReturn(true); + listener.handleStepResultEvent(event, callback); - // expect(output).toEqual(expected); + expect(fakeFormatter.step).toHaveBeenCalledWith({name: 'Step', line: 3, keyword: 'Step'}); + expect(fakeFormatter.result).toHaveBeenCalledWith({status: 'passed'}); + expect(fakeFormatter.match).toHaveBeenCalledWith({location: 'TODO'}); - // }); + }); - // }); + }); - // // We're all done. Output the JSON. + // We're all done. Output the JSON. - // describe("handleAfterFeaturesEvent()", function() { - // var features, callback; + describe("handleAfterFeaturesEvent()", function() { + var features, callback; - // beforeEach(function() { - // event = createSpy("Event"); - // callback = createSpy("Callback"); + beforeEach(function() { + event = createSpy("Event"); + callback = createSpy("Callback"); - // }); + }); - // // TODO: What else should we test here? e.g. calls made to the formatter? + it("finalises output", function() { + listener.handleAfterFeaturesEvent(event, callback); + expect(fakeFormatter.eof).toHaveBeenCalled(); + expect(fakeFormatter.done).toHaveBeenCalled(); + }); - // it("calls back", function() { - // listener.handleAfterFeaturesEvent(event, callback); - // expect(callback).toHaveBeenCalled(); - // }); + it("calls back", function() { + listener.handleAfterFeaturesEvent(event, callback); + expect(callback).toHaveBeenCalled(); + }); - // }); + }); }); From cf4e3012dd0c7d3df2a966fccbc2acebbf44ffb3 Mon Sep 17 00:00:00 2001 From: Chris Young Date: Tue, 24 Jul 2012 10:52:49 +0100 Subject: [PATCH 33/44] Added scenario to test for skipped steps --- features/json_formatter.feature | 67 +++++++++++++++++++ .../listener/json_formatter_wrapper.js | 4 +- .../listener/json_formatter_wrapper_spec.js | 24 +++---- 3 files changed, 79 insertions(+), 16 deletions(-) diff --git a/features/json_formatter.feature b/features/json_formatter.feature index 86eef573a..24c962ad0 100644 --- a/features/json_formatter.feature +++ b/features/json_formatter.feature @@ -100,6 +100,73 @@ ] """ + Scenario: output JSON for a feature with one undefined step and subsequent defined steps which should be skipped + Given a file named "features/a.feature" with: + """ + Feature: some feature + + Scenario: One pending step and two following steps which will be skipped + Given This step is undefined + Then this step should be skipped + + """ + And a file named "features/step_definitions/cucumber_steps.js" with: + """ + var cucumberSteps = function() { + this.Then(/^this step should be skipped$/, function(callback) { callback(); }); + }; + module.exports = cucumberSteps; + """ + + When I run `cucumber.js -f json` + Then it should output this json: + """ + [ + { + "id": "some-feature", + "name": "some feature", + "description": "", + "line": 1, + "keyword": "Feature", + "uri": "$CUCUMBER_JS_HOME/tmp/cucumber-js-sandbox/features/a.feature", + "elements": [ + { + "name": "One pending step and two following steps which will be skipped", + "id": "some-feature;one-pending-step-and-two-following-steps-which-will-be-skipped", + "line": 3, + "keyword": "Scenario", + "description": "", + "type": "scenario", + "steps": [ + { + "name": "This step is undefined", + "line": 4, + "keyword": "Given ", + "result": { + "status": "undefined" + }, + "match": { + "location": "TODO" + } + }, + { + "name": "this step should be skipped", + "line": 5, + "keyword": "Then ", + "result": { + "status": "skipped" + }, + "match": { + "location": "TODO" + } + } + ] + } + ] + } + ] + """ + Scenario: output JSON for a feature with one scenario with one pending step Given a file named "features/a.feature" with: """ diff --git a/lib/cucumber/listener/json_formatter_wrapper.js b/lib/cucumber/listener/json_formatter_wrapper.js index d3edf59f4..7ae528344 100644 --- a/lib/cucumber/listener/json_formatter_wrapper.js +++ b/lib/cucumber/listener/json_formatter_wrapper.js @@ -125,7 +125,9 @@ var JsonFormatterWrapper = function(formatter) { } else { var failureMessage = stepResult.getFailureException(); - stepOutput['error_message'] = (failureMessage.stack || failureMessage); + if (failureMessage) { + stepOutput['error_message'] = (failureMessage.stack || failureMessage); + } } stepOutput['status'] = resultStatus; diff --git a/spec/cucumber/listener/json_formatter_wrapper_spec.js b/spec/cucumber/listener/json_formatter_wrapper_spec.js index 700147578..2c38c2a92 100644 --- a/spec/cucumber/listener/json_formatter_wrapper_spec.js +++ b/spec/cucumber/listener/json_formatter_wrapper_spec.js @@ -10,11 +10,6 @@ describe("Cucumber.Listener.JsonFormatterWrapper", function() { listener = Cucumber.Listener.JsonFormatterWrapper(fakeFormatter); }); - describe("constructor", function() { - // TODO! - }); - - // Handle Feature describe("handleBeforeFeatureEvent()", function() { @@ -249,6 +244,10 @@ describe("Cucumber.Listener.JsonFormatterWrapper", function() { var parent_feature_event, feature, parent_scenario_event, scenario, event, callback, stepResult; beforeEach(function() { + callback = createSpy("Callback"); + }); + + it("outputs a step with failed status where no result has been defined", function(){ step = createSpyWithStubs("step", { getName: 'Step', @@ -258,27 +257,22 @@ describe("Cucumber.Listener.JsonFormatterWrapper", function() { hasDataTable: false }); - stepResult = createSpyWithStubs("step result", { + stepResult = createSpyWithStubs("stepResult", { isSuccessful: undefined, isPending: undefined, isFailed: undefined, isSkipped: undefined, isUndefined: undefined, + getFailureException: false, getStep: step }); - event = createSpyWithStubs("event", {getPayloadItem: stepResult}); - callback = createSpy("Callback"); - }); - - // Extra tests for errors etc... + fakeEvent = createSpyWithStubs("event", {getPayloadItem: stepResult}); - it("outputs a step and its result", function(){ - stepResult.isSuccessful.andReturn(true); - listener.handleStepResultEvent(event, callback); + listener.handleStepResultEvent(fakeEvent, callback); expect(fakeFormatter.step).toHaveBeenCalledWith({name: 'Step', line: 3, keyword: 'Step'}); - expect(fakeFormatter.result).toHaveBeenCalledWith({status: 'passed'}); + expect(fakeFormatter.result).toHaveBeenCalledWith({status: 'failed'}); expect(fakeFormatter.match).toHaveBeenCalledWith({location: 'TODO'}); }); From 57e6f5bd737d58b6548e9577f457b22a41fce797 Mon Sep 17 00:00:00 2001 From: Chris Young Date: Tue, 24 Jul 2012 11:01:39 +0100 Subject: [PATCH 34/44] Added more jasmine tests for step results --- .../listener/json_formatter_wrapper_spec.js | 155 ++++++++++++++++++ 1 file changed, 155 insertions(+) diff --git a/spec/cucumber/listener/json_formatter_wrapper_spec.js b/spec/cucumber/listener/json_formatter_wrapper_spec.js index 2c38c2a92..1ba9d3f7d 100644 --- a/spec/cucumber/listener/json_formatter_wrapper_spec.js +++ b/spec/cucumber/listener/json_formatter_wrapper_spec.js @@ -277,6 +277,161 @@ describe("Cucumber.Listener.JsonFormatterWrapper", function() { }); + it("outputs a step with passed status for a successful step", function(){ + + step = createSpyWithStubs("step", { + getName: 'Step', + getLine: 3, + getKeyword: 'Step', + hasDocString: false, + hasDataTable: false + }); + + stepResult = createSpyWithStubs("stepResult", { + isSuccessful: undefined, + isPending: undefined, + isFailed: undefined, + isSkipped: undefined, + isUndefined: undefined, + getFailureException: false, + getStep: step + }); + + stepResult.isSuccessful.andReturn(true); + fakeEvent = createSpyWithStubs("event", {getPayloadItem: stepResult}); + + listener.handleStepResultEvent(fakeEvent, callback); + + expect(fakeFormatter.step).toHaveBeenCalledWith({name: 'Step', line: 3, keyword: 'Step'}); + expect(fakeFormatter.result).toHaveBeenCalledWith({status: 'passed'}); + expect(fakeFormatter.match).toHaveBeenCalledWith({location: 'TODO'}); + + }); + + it("outputs a step with pending status where step is pending", function(){ + + step = createSpyWithStubs("step", { + getName: 'Step', + getLine: 3, + getKeyword: 'Step', + hasDocString: false, + hasDataTable: false + }); + + stepResult = createSpyWithStubs("stepResult", { + isSuccessful: undefined, + isPending: undefined, + isFailed: undefined, + isSkipped: undefined, + isUndefined: undefined, + getFailureException: false, + getStep: step + }); + + stepResult.isPending.andReturn(true); + fakeEvent = createSpyWithStubs("event", {getPayloadItem: stepResult}); + + listener.handleStepResultEvent(fakeEvent, callback); + + expect(fakeFormatter.step).toHaveBeenCalledWith({name: 'Step', line: 3, keyword: 'Step'}); + expect(fakeFormatter.result).toHaveBeenCalledWith({status: 'pending', error_message: 'TODO'}); + expect(fakeFormatter.match).toHaveBeenCalledWith({location: 'TODO'}); + + }); + + it("outputs a step with failed status where step fails", function(){ + + step = createSpyWithStubs("step", { + getName: 'Step', + getLine: 3, + getKeyword: 'Step', + hasDocString: false, + hasDataTable: false + }); + + stepResult = createSpyWithStubs("stepResult", { + isSuccessful: undefined, + isPending: undefined, + isFailed: undefined, + isSkipped: undefined, + isUndefined: undefined, + getFailureException: false, + getStep: step + }); + + stepResult.isFailed.andReturn(true); + fakeEvent = createSpyWithStubs("event", {getPayloadItem: stepResult}); + + listener.handleStepResultEvent(fakeEvent, callback); + + expect(fakeFormatter.step).toHaveBeenCalledWith({name: 'Step', line: 3, keyword: 'Step'}); + expect(fakeFormatter.result).toHaveBeenCalledWith({status: 'failed'}); + expect(fakeFormatter.match).toHaveBeenCalledWith({location: 'TODO'}); + + }); + + it("outputs a step with skipped status where step should be skipped", function(){ + + step = createSpyWithStubs("step", { + getName: 'Step', + getLine: 3, + getKeyword: 'Step', + hasDocString: false, + hasDataTable: false + }); + + stepResult = createSpyWithStubs("stepResult", { + isSuccessful: undefined, + isPending: undefined, + isFailed: undefined, + isSkipped: undefined, + isUndefined: undefined, + getFailureException: false, + getStep: step + }); + + stepResult.isSkipped.andReturn(true); + fakeEvent = createSpyWithStubs("event", {getPayloadItem: stepResult}); + + listener.handleStepResultEvent(fakeEvent, callback); + + expect(fakeFormatter.step).toHaveBeenCalledWith({name: 'Step', line: 3, keyword: 'Step'}); + expect(fakeFormatter.result).toHaveBeenCalledWith({status: 'skipped'}); + expect(fakeFormatter.match).toHaveBeenCalledWith({location: 'TODO'}); + + }); + + it("outputs a step with undefined status where step is undefined", function(){ + + step = createSpyWithStubs("step", { + getName: 'Step', + getLine: 3, + getKeyword: 'Step', + hasDocString: false, + hasDataTable: false + }); + + stepResult = createSpyWithStubs("stepResult", { + isSuccessful: undefined, + isPending: undefined, + isFailed: undefined, + isSkipped: undefined, + isUndefined: undefined, + getFailureException: false, + getStep: step + }); + + stepResult.isUndefined.andReturn(true); + fakeEvent = createSpyWithStubs("event", {getPayloadItem: stepResult}); + + listener.handleStepResultEvent(fakeEvent, callback); + + expect(fakeFormatter.step).toHaveBeenCalledWith({name: 'Step', line: 3, keyword: 'Step'}); + expect(fakeFormatter.result).toHaveBeenCalledWith({status: 'undefined'}); + expect(fakeFormatter.match).toHaveBeenCalledWith({location: 'TODO'}); + + }); + }); // We're all done. Output the JSON. From 8a953bc700722fde7d02af47d90e4537edb4fdec Mon Sep 17 00:00:00 2001 From: Chris Young Date: Tue, 24 Jul 2012 11:14:17 +0100 Subject: [PATCH 35/44] Corrected formatting in features file --- features/json_formatter.feature | 41 +++++++++++++++++---------------- 1 file changed, 21 insertions(+), 20 deletions(-) diff --git a/features/json_formatter.feature b/features/json_formatter.feature index 24c962ad0..1e9edfab8 100644 --- a/features/json_formatter.feature +++ b/features/json_formatter.feature @@ -1,28 +1,29 @@ - Feature: JSON Formatter - In order to simplify processing of Cucumber features and results - Developers should be able to consume features as JSON +Feature: JSON Formatter + In order to simplify processing of Cucumber features and results + Developers should be able to consume features as JSON - Background: - Given CUCUMBER_JS_HOME environment variable has been set to the cucumber-js install dir + Background: + Given CUCUMBER_JS_HOME environment variable has been set to the cucumber-js install dir - Scenario: output JSON for a feature with no scenarios - Given a file named "features/a.feature" with: - """ - Feature: some feature - """ - When I run `cucumber.js -f json` - Then it should output this json: - """ - [ - {"id":"some-feature", + Scenario: output JSON for a feature with no scenarios + Given a file named "features/a.feature" with: + """ + Feature: some feature + """ + When I run `cucumber.js -f json` + Then it should output this json: + """ + [ + { + "id":"some-feature", "name":"some feature", "description":"", "line":1, "keyword":"Feature", "uri":"$CUCUMBER_JS_HOME/tmp/cucumber-js-sandbox/features/a.feature" - } - ] - """ + } + ] + """ Scenario: output JSON for a feature with one undefined scenario Given a file named "features/a.feature" with: @@ -487,7 +488,7 @@ ] """ -Scenario: one feature, one passing scenario, one failing scenario + Scenario: output JSON for one feature, one passing scenario, one failing scenario Given a file named "features/a.feature" with: """ Feature: one passes one fails @@ -1245,7 +1246,7 @@ Scenario: one feature, one passing scenario, one failing scenario ] """ - Scenario: Scenario with tags + Scenario: output JSON for scenario with tags Given a file named "features/a.feature" with: """ From 0ee152a8ffa15fe5ff3512a2ec33536976064ab0 Mon Sep 17 00:00:00 2001 From: Chris Young Date: Tue, 24 Jul 2012 22:49:24 +0100 Subject: [PATCH 36/44] Deleted redundant a.feature file --- features/a.feature | 3 --- 1 file changed, 3 deletions(-) delete mode 100644 features/a.feature diff --git a/features/a.feature b/features/a.feature deleted file mode 100644 index d5f65c765..000000000 --- a/features/a.feature +++ /dev/null @@ -1,3 +0,0 @@ -Feature: Some Feature - Scenario: A Passing Step - When a step is passing From 243bce8727edb760a43bc47b1bb1293544b3a9ec Mon Sep 17 00:00:00 2001 From: Chris Young Date: Wed, 25 Jul 2012 10:36:01 +0100 Subject: [PATCH 37/44] Moved Gherkin JSON formatter back into Wrapper class. Added getter to allow us to spy on this in jasmine tests --- lib/cucumber/cli/configuration.js | 4 +- .../listener/json_formatter_wrapper.js | 9 ++- .../listener/json_formatter_wrapper_spec.js | 71 +++++++++++-------- 3 files changed, 50 insertions(+), 34 deletions(-) diff --git a/lib/cucumber/cli/configuration.js b/lib/cucumber/cli/configuration.js index 237e1505c..43bbce5b2 100644 --- a/lib/cucumber/cli/configuration.js +++ b/lib/cucumber/cli/configuration.js @@ -16,9 +16,7 @@ var Configuration = function(argv) { formatter = Cucumber.Listener.PrettyFormatter(); break; case Configuration.JSON_FORMAT_NAME: - var JSONFormatter = require('gherkin/lib/gherkin/formatter/json_formatter'); - var jsonFormatter = new JSONFormatter(process.stdout); - formatter = Cucumber.Listener.JsonFormatterWrapper(jsonFormatter); + formatter = Cucumber.Listener.JsonFormatterWrapper(process.stdout); break; case Configuration.SUMMARY_FORMAT_NAME: formatter = Cucumber.Listener.SummaryFormatter(); diff --git a/lib/cucumber/listener/json_formatter_wrapper.js b/lib/cucumber/listener/json_formatter_wrapper.js index 7ae528344..3cda112d6 100644 --- a/lib/cucumber/listener/json_formatter_wrapper.js +++ b/lib/cucumber/listener/json_formatter_wrapper.js @@ -1,6 +1,7 @@ -var JsonFormatterWrapper = function(formatter) { +var JsonFormatterWrapper = function(io) { - var formatter = formatter; + var JSONFormatter = require('gherkin/lib/gherkin/formatter/json_formatter'); + var formatter = new JSONFormatter(io); var currentFeatureId = 'undefined'; @@ -10,6 +11,10 @@ var JsonFormatterWrapper = function(formatter) { var parentFeatureTags; + self.getGherkinFormatter = function() { + return formatter; + } + self.formatStep = function formatStep(step) { var stepProperties = {name: step.getName(), line: step.getLine(), keyword: step.getKeyword()}; diff --git a/spec/cucumber/listener/json_formatter_wrapper_spec.js b/spec/cucumber/listener/json_formatter_wrapper_spec.js index 1ba9d3f7d..5bf90cf97 100644 --- a/spec/cucumber/listener/json_formatter_wrapper_spec.js +++ b/spec/cucumber/listener/json_formatter_wrapper_spec.js @@ -4,10 +4,23 @@ describe("Cucumber.Listener.JsonFormatterWrapper", function() { var Cucumber = requireLib('cucumber'); var listener, failedStepResults; - var fakeFormatter = createSpyObj('formatter', ['step', 'uri', 'feature', 'background', 'scenario', 'result', 'match', 'eof', 'done']); + // var fakeFormatter = createSpyObj('formatter', ['step', 'uri', 'feature', 'background', 'scenario', 'result', 'match', 'eof', 'done']); beforeEach(function() { - listener = Cucumber.Listener.JsonFormatterWrapper(fakeFormatter); + spyOn(process.stdout, 'write'); // prevent actual output during spec execution + listener = Cucumber.Listener.JsonFormatterWrapper(process.stdout); + formatter = listener.getGherkinFormatter(); + + spyOn(formatter, 'uri'); + spyOn(formatter, 'feature'); + spyOn(formatter, 'step'); + spyOn(formatter, 'background'); + spyOn(formatter, 'scenario'); + spyOn(formatter, 'result'); + spyOn(formatter, 'match'); + spyOn(formatter, 'eof'); + spyOn(formatter, 'done'); + }); // Handle Feature @@ -31,8 +44,8 @@ describe("Cucumber.Listener.JsonFormatterWrapper", function() { it("adds the feature attributes to the output", function() { listener.handleBeforeFeatureEvent(event, callback); - expect(fakeFormatter.uri).toHaveBeenCalledWith('TODO'); - expect(fakeFormatter.feature).toHaveBeenCalledWith({id: 'A-Name', + expect(formatter.uri).toHaveBeenCalledWith('TODO'); + expect(formatter.feature).toHaveBeenCalledWith({id: 'A-Name', name: 'A Name', description: 'A Description', line: 3, @@ -82,7 +95,7 @@ describe("Cucumber.Listener.JsonFormatterWrapper", function() { it("adds the background attributes to the output", function() { listener.handleBackgroundEvent(event, callback); - expect(fakeFormatter.background).toHaveBeenCalledWith({name: 'A Name', + expect(formatter.background).toHaveBeenCalledWith({name: 'A Name', keyword: 'Background', description: 'A Description', type: 'background', @@ -120,7 +133,7 @@ describe("Cucumber.Listener.JsonFormatterWrapper", function() { it("adds the scenario attributes to the output", function() { listener.handleBeforeScenarioEvent(event, callback); - expect(fakeFormatter.scenario).toHaveBeenCalledWith({name: 'A Name', + expect(formatter.scenario).toHaveBeenCalledWith({name: 'A Name', id: 'undefined;a-name', line: 3, keyword: 'Scenario', @@ -145,7 +158,7 @@ describe("Cucumber.Listener.JsonFormatterWrapper", function() { }); listener.formatStep(step); - expect(fakeFormatter.step).toHaveBeenCalledWith({ name : 'Step', line : 3, keyword : 'Step'}); + expect(formatter.step).toHaveBeenCalledWith({ name : 'Step', line : 3, keyword : 'Step'}); }); @@ -166,7 +179,7 @@ describe("Cucumber.Listener.JsonFormatterWrapper", function() { }); listener.formatStep(step); - expect(fakeFormatter.step).toHaveBeenCalledWith({name: 'Step', + expect(formatter.step).toHaveBeenCalledWith({name: 'Step', line: 3, keyword: 'Step', doc_string: {value: 'This is a DocString', line: 3, content_type: null} @@ -194,7 +207,7 @@ describe("Cucumber.Listener.JsonFormatterWrapper", function() { }); listener.formatStep(step); - expect(fakeFormatter.step).toHaveBeenCalledWith({name: 'Step', + expect(formatter.step).toHaveBeenCalledWith({name: 'Step', line: 3, keyword: 'Step', rows: [{line : 'TODO', cells: ['a:1', 'a:2', 'a:3'] }, @@ -271,9 +284,9 @@ describe("Cucumber.Listener.JsonFormatterWrapper", function() { listener.handleStepResultEvent(fakeEvent, callback); - expect(fakeFormatter.step).toHaveBeenCalledWith({name: 'Step', line: 3, keyword: 'Step'}); - expect(fakeFormatter.result).toHaveBeenCalledWith({status: 'failed'}); - expect(fakeFormatter.match).toHaveBeenCalledWith({location: 'TODO'}); + expect(formatter.step).toHaveBeenCalledWith({name: 'Step', line: 3, keyword: 'Step'}); + expect(formatter.result).toHaveBeenCalledWith({status: 'failed'}); + expect(formatter.match).toHaveBeenCalledWith({location: 'TODO'}); }); @@ -302,9 +315,9 @@ describe("Cucumber.Listener.JsonFormatterWrapper", function() { listener.handleStepResultEvent(fakeEvent, callback); - expect(fakeFormatter.step).toHaveBeenCalledWith({name: 'Step', line: 3, keyword: 'Step'}); - expect(fakeFormatter.result).toHaveBeenCalledWith({status: 'passed'}); - expect(fakeFormatter.match).toHaveBeenCalledWith({location: 'TODO'}); + expect(formatter.step).toHaveBeenCalledWith({name: 'Step', line: 3, keyword: 'Step'}); + expect(formatter.result).toHaveBeenCalledWith({status: 'passed'}); + expect(formatter.match).toHaveBeenCalledWith({location: 'TODO'}); }); @@ -333,9 +346,9 @@ describe("Cucumber.Listener.JsonFormatterWrapper", function() { listener.handleStepResultEvent(fakeEvent, callback); - expect(fakeFormatter.step).toHaveBeenCalledWith({name: 'Step', line: 3, keyword: 'Step'}); - expect(fakeFormatter.result).toHaveBeenCalledWith({status: 'pending', error_message: 'TODO'}); - expect(fakeFormatter.match).toHaveBeenCalledWith({location: 'TODO'}); + expect(formatter.step).toHaveBeenCalledWith({name: 'Step', line: 3, keyword: 'Step'}); + expect(formatter.result).toHaveBeenCalledWith({status: 'pending', error_message: 'TODO'}); + expect(formatter.match).toHaveBeenCalledWith({location: 'TODO'}); }); @@ -364,9 +377,9 @@ describe("Cucumber.Listener.JsonFormatterWrapper", function() { listener.handleStepResultEvent(fakeEvent, callback); - expect(fakeFormatter.step).toHaveBeenCalledWith({name: 'Step', line: 3, keyword: 'Step'}); - expect(fakeFormatter.result).toHaveBeenCalledWith({status: 'failed'}); - expect(fakeFormatter.match).toHaveBeenCalledWith({location: 'TODO'}); + expect(formatter.step).toHaveBeenCalledWith({name: 'Step', line: 3, keyword: 'Step'}); + expect(formatter.result).toHaveBeenCalledWith({status: 'failed'}); + expect(formatter.match).toHaveBeenCalledWith({location: 'TODO'}); }); @@ -395,9 +408,9 @@ describe("Cucumber.Listener.JsonFormatterWrapper", function() { listener.handleStepResultEvent(fakeEvent, callback); - expect(fakeFormatter.step).toHaveBeenCalledWith({name: 'Step', line: 3, keyword: 'Step'}); - expect(fakeFormatter.result).toHaveBeenCalledWith({status: 'skipped'}); - expect(fakeFormatter.match).toHaveBeenCalledWith({location: 'TODO'}); + expect(formatter.step).toHaveBeenCalledWith({name: 'Step', line: 3, keyword: 'Step'}); + expect(formatter.result).toHaveBeenCalledWith({status: 'skipped'}); + expect(formatter.match).toHaveBeenCalledWith({location: 'TODO'}); }); @@ -426,9 +439,9 @@ describe("Cucumber.Listener.JsonFormatterWrapper", function() { listener.handleStepResultEvent(fakeEvent, callback); - expect(fakeFormatter.step).toHaveBeenCalledWith({name: 'Step', line: 3, keyword: 'Step'}); - expect(fakeFormatter.result).toHaveBeenCalledWith({status: 'undefined'}); - expect(fakeFormatter.match).toHaveBeenCalledWith({location: 'TODO'}); + expect(formatter.step).toHaveBeenCalledWith({name: 'Step', line: 3, keyword: 'Step'}); + expect(formatter.result).toHaveBeenCalledWith({status: 'undefined'}); + expect(formatter.match).toHaveBeenCalledWith({location: 'TODO'}); }); @@ -447,8 +460,8 @@ describe("Cucumber.Listener.JsonFormatterWrapper", function() { it("finalises output", function() { listener.handleAfterFeaturesEvent(event, callback); - expect(fakeFormatter.eof).toHaveBeenCalled(); - expect(fakeFormatter.done).toHaveBeenCalled(); + expect(formatter.eof).toHaveBeenCalled(); + expect(formatter.done).toHaveBeenCalled(); }); it("calls back", function() { From 80b8b56993ceb427cb188d87deb817330ba5d52b Mon Sep 17 00:00:00 2001 From: Chris Young Date: Wed, 25 Jul 2012 10:46:04 +0100 Subject: [PATCH 38/44] Removed redundant spec for uri method --- spec/cucumber/ast/feature_spec.js | 6 ------ 1 file changed, 6 deletions(-) diff --git a/spec/cucumber/ast/feature_spec.js b/spec/cucumber/ast/feature_spec.js index 8a78f5146..dda4d36cf 100644 --- a/spec/cucumber/ast/feature_spec.js +++ b/spec/cucumber/ast/feature_spec.js @@ -56,12 +56,6 @@ describe("Cucumber.Ast.Feature", function() { }); }); - describe("getUri()", function() { - it("returns a uniform resource identifier for the feature consisting of the filename containing the feature", function() { - expect(feature.getUri()).toBe(uri); - }); - }); - describe("getBackground() [addBackground()]", function() { describe("when a background was previously added", function() { var background; From b827efbf17fd6cd2aaf285622c074ce43ed03906 Mon Sep 17 00:00:00 2001 From: Chris Young Date: Wed, 25 Jul 2012 10:52:14 +0100 Subject: [PATCH 39/44] Removed spurious spec which has nothing to do with JSON formatter --- spec/cucumber/cli/argument_parser_spec.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/spec/cucumber/cli/argument_parser_spec.js b/spec/cucumber/cli/argument_parser_spec.js index a99ac9170..de9ce1f4d 100644 --- a/spec/cucumber/cli/argument_parser_spec.js +++ b/spec/cucumber/cli/argument_parser_spec.js @@ -116,9 +116,6 @@ describe("Cucumber.Cli.ArgumentParser", function () { expect(shortenedOptionDefinitions[aliasName]).toEqual(aliasValue); }); - it("defines an alias to --output as -o", function() { - }); - }); describe("getFeatureFilePaths()", function () { From 575305844a3dda6e9919385eedf721b9b4e1ff7f Mon Sep 17 00:00:00 2001 From: Chris Young Date: Wed, 25 Jul 2012 11:19:28 +0100 Subject: [PATCH 40/44] Renamed JSON Formatter to make it consistent with existing formatters --- lib/cucumber/cli/configuration.js | 4 +-- lib/cucumber/listener.js | 2 +- ...formatter_wrapper.js => json_formatter.js} | 28 +++++++++---------- ...wrapper_spec.js => json_formatter_spec.js} | 4 +-- 4 files changed, 18 insertions(+), 20 deletions(-) rename lib/cucumber/listener/{json_formatter_wrapper.js => json_formatter.js} (83%) rename spec/cucumber/listener/{json_formatter_wrapper_spec.js => json_formatter_spec.js} (98%) diff --git a/lib/cucumber/cli/configuration.js b/lib/cucumber/cli/configuration.js index 43bbce5b2..42082cbfc 100644 --- a/lib/cucumber/cli/configuration.js +++ b/lib/cucumber/cli/configuration.js @@ -15,8 +15,8 @@ var Configuration = function(argv) { case Configuration.PRETTY_FORMAT_NAME: formatter = Cucumber.Listener.PrettyFormatter(); break; - case Configuration.JSON_FORMAT_NAME: - formatter = Cucumber.Listener.JsonFormatterWrapper(process.stdout); + case Configuration.JSON_FORMAT_NAME: + formatter = Cucumber.Listener.JsonFormatter(process.stdout); break; case Configuration.SUMMARY_FORMAT_NAME: formatter = Cucumber.Listener.SummaryFormatter(); diff --git a/lib/cucumber/listener.js b/lib/cucumber/listener.js index 9abb897cc..bf04e36c8 100644 --- a/lib/cucumber/listener.js +++ b/lib/cucumber/listener.js @@ -36,7 +36,7 @@ Listener.EVENT_HANDLER_NAME_SUFFIX = 'Event'; Listener.Formatter = require('./listener/formatter'); Listener.PrettyFormatter = require('./listener/pretty_formatter'); Listener.ProgressFormatter = require('./listener/progress_formatter'); -Listener.JsonFormatterWrapper = require('./listener/json_formatter_wrapper'); +Listener.JsonFormatter = require('./listener/json_formatter'); Listener.StatsJournal = require('./listener/stats_journal'); Listener.SummaryFormatter = require('./listener/summary_formatter'); module.exports = Listener; diff --git a/lib/cucumber/listener/json_formatter_wrapper.js b/lib/cucumber/listener/json_formatter.js similarity index 83% rename from lib/cucumber/listener/json_formatter_wrapper.js rename to lib/cucumber/listener/json_formatter.js index 3cda112d6..07ece0537 100644 --- a/lib/cucumber/listener/json_formatter_wrapper.js +++ b/lib/cucumber/listener/json_formatter.js @@ -1,7 +1,7 @@ -var JsonFormatterWrapper = function(io) { +var JsonFormatter = function(io) { - var JSONFormatter = require('gherkin/lib/gherkin/formatter/json_formatter'); - var formatter = new JSONFormatter(io); + var GherkinJsonFormatter = require('gherkin/lib/gherkin/formatter/json_formatter'); + var gherkinJsonFormatter = new GherkinJsonFormatter(io); var currentFeatureId = 'undefined'; @@ -12,7 +12,7 @@ var JsonFormatterWrapper = function(io) { var parentFeatureTags; self.getGherkinFormatter = function() { - return formatter; + return gherkinJsonFormatter; } self.formatStep = function formatStep(step) { @@ -33,7 +33,7 @@ var JsonFormatterWrapper = function(io) { stepProperties['rows'] = tableProperties; } - formatter.step(stepProperties); + gherkinJsonFormatter.step(stepProperties); } @@ -58,7 +58,7 @@ var JsonFormatterWrapper = function(io) { var feature = event.getPayloadItem('feature'); currentFeatureId = feature.getName().replace(' ','-'); - formatter.uri(feature.getUri()); + gherkinJsonFormatter.uri(feature.getUri()); var featureProperties = {id: currentFeatureId, name: feature.getName(), @@ -71,7 +71,7 @@ var JsonFormatterWrapper = function(io) { featureProperties['tags'] = self.formatTags(tags, []); } - formatter.feature(featureProperties); + gherkinJsonFormatter.feature(featureProperties); parentFeatureTags = tags; @@ -80,7 +80,7 @@ var JsonFormatterWrapper = function(io) { self.handleBackgroundEvent = function handleBackgroundEvent(event, callback) { var background = event.getPayloadItem('background'); - formatter.background({name: background.getName(), keyword: "Background", description: background.getDescription(), type: 'background', line: background.getLine()}) + gherkinJsonFormatter.background({name: background.getName(), keyword: "Background", description: background.getDescription(), type: 'background', line: background.getLine()}) var steps = background.getSteps(); steps.forEach(function(value, index, ar) { self.formatStep(value); }); callback(); @@ -101,7 +101,7 @@ var JsonFormatterWrapper = function(io) { } } - formatter.scenario(scenarioProperties); + gherkinJsonFormatter.scenario(scenarioProperties); callback(); } @@ -137,15 +137,15 @@ var JsonFormatterWrapper = function(io) { stepOutput['status'] = resultStatus; - formatter.result(stepOutput); - formatter.match({location: 'TODO'}); + gherkinJsonFormatter.result(stepOutput); + gherkinJsonFormatter.match({location: 'TODO'}); callback(); } self.handleAfterFeaturesEvent = function handleAfterFeaturesEvent(event, callback) { - formatter.eof(); - formatter.done(); + gherkinJsonFormatter.eof(); + gherkinJsonFormatter.done(); callback(); } @@ -153,5 +153,5 @@ var JsonFormatterWrapper = function(io) { return self; }; -module.exports = JsonFormatterWrapper; +module.exports = JsonFormatter; diff --git a/spec/cucumber/listener/json_formatter_wrapper_spec.js b/spec/cucumber/listener/json_formatter_spec.js similarity index 98% rename from spec/cucumber/listener/json_formatter_wrapper_spec.js rename to spec/cucumber/listener/json_formatter_spec.js index 5bf90cf97..242f34a04 100644 --- a/spec/cucumber/listener/json_formatter_wrapper_spec.js +++ b/spec/cucumber/listener/json_formatter_spec.js @@ -4,11 +4,9 @@ describe("Cucumber.Listener.JsonFormatterWrapper", function() { var Cucumber = requireLib('cucumber'); var listener, failedStepResults; - // var fakeFormatter = createSpyObj('formatter', ['step', 'uri', 'feature', 'background', 'scenario', 'result', 'match', 'eof', 'done']); - beforeEach(function() { spyOn(process.stdout, 'write'); // prevent actual output during spec execution - listener = Cucumber.Listener.JsonFormatterWrapper(process.stdout); + listener = Cucumber.Listener.JsonFormatter(process.stdout); formatter = listener.getGherkinFormatter(); spyOn(formatter, 'uri'); From 73b52b6f9668994317089e358c40326268b8a481 Mon Sep 17 00:00:00 2001 From: Chris Young Date: Wed, 25 Jul 2012 11:25:13 +0100 Subject: [PATCH 41/44] Removed spurious feature file --- features/simple.feature | 5 ----- 1 file changed, 5 deletions(-) delete mode 100644 features/simple.feature diff --git a/features/simple.feature b/features/simple.feature deleted file mode 100644 index cee1a50e8..000000000 --- a/features/simple.feature +++ /dev/null @@ -1,5 +0,0 @@ -Feature: Simple Feature - - Scenario: display Cucumber version - When I run `cucumber.js --version` - Then I see the version of Cucumber From e37a4cce605aa79a2bcb01bc8942129214ec8821 Mon Sep 17 00:00:00 2001 From: Chris Young Date: Wed, 25 Jul 2012 17:02:40 +0100 Subject: [PATCH 42/44] Removed need for CUCUMBER_JS_HOME environment variable by substituting those strings in the actual json output that contain the paths to either the cucumber.js source or the sandbox with sanitised versions of the strings. The two elements affected are uri and error_message, the content of which are not the primary concern of the formatter nor of these tests. Replacing them with sanitised versions makes the tests more portable and resiliant to chnge. --- features/json_formatter.feature | 61 ++++++++++++-------------- features/step_definitions/cli_steps.js | 14 +++--- 2 files changed, 35 insertions(+), 40 deletions(-) diff --git a/features/json_formatter.feature b/features/json_formatter.feature index 1e9edfab8..2f845a1f5 100644 --- a/features/json_formatter.feature +++ b/features/json_formatter.feature @@ -2,9 +2,6 @@ Feature: JSON Formatter In order to simplify processing of Cucumber features and results Developers should be able to consume features as JSON - Background: - Given CUCUMBER_JS_HOME environment variable has been set to the cucumber-js install dir - Scenario: output JSON for a feature with no scenarios Given a file named "features/a.feature" with: """ @@ -20,7 +17,7 @@ Feature: JSON Formatter "description":"", "line":1, "keyword":"Feature", - "uri":"$CUCUMBER_JS_HOME/tmp/cucumber-js-sandbox/features/a.feature" + "uri":"/path/to/sandbox/tmp/cucumber-js-sandbox/features/a.feature" } ] """ @@ -42,7 +39,7 @@ Feature: JSON Formatter "description":"", "line":1, "keyword":"Feature", - "uri":"$CUCUMBER_JS_HOME/tmp/cucumber-js-sandbox/features/a.feature", + "uri":"/path/to/sandbox/tmp/cucumber-js-sandbox/features/a.feature", "elements":[ {"name":"I havn't done anything yet", "id":"some-feature;i-havn't-done-anything-yet", @@ -74,7 +71,7 @@ Feature: JSON Formatter "description": "", "line": 1, "keyword": "Feature", - "uri":"$CUCUMBER_JS_HOME/tmp/cucumber-js-sandbox/features/a.feature", + "uri":"/path/to/sandbox/tmp/cucumber-js-sandbox/features/a.feature", "elements": [ { "name": "I've declaired one step but not yet defined it", @@ -129,7 +126,7 @@ Feature: JSON Formatter "description": "", "line": 1, "keyword": "Feature", - "uri": "$CUCUMBER_JS_HOME/tmp/cucumber-js-sandbox/features/a.feature", + "uri": "/path/to/sandbox/tmp/cucumber-js-sandbox/features/a.feature", "elements": [ { "name": "One pending step and two following steps which will be skipped", @@ -194,7 +191,7 @@ Feature: JSON Formatter "description": "", "line": 1, "keyword": "Feature", - "uri":"$CUCUMBER_JS_HOME/tmp/cucumber-js-sandbox/features/a.feature", + "uri":"/path/to/sandbox/tmp/cucumber-js-sandbox/features/a.feature", "elements": [ { "name": "I've declaired one step which is pending", @@ -209,7 +206,7 @@ Feature: JSON Formatter "line": 4, "keyword": "Given ", "result": { - "error_message": "TODO", + "error_message": "ERROR_MESSAGE", "status": "pending" }, "match": { @@ -247,7 +244,7 @@ Feature: JSON Formatter "description": "", "line": 1, "keyword": "Feature", - "uri":"$CUCUMBER_JS_HOME/tmp/cucumber-js-sandbox/features/a.feature", + "uri":"/path/to/sandbox/tmp/cucumber-js-sandbox/features/a.feature", "elements": [ { "name": "I've declaired one step but it is failing", @@ -262,7 +259,7 @@ Feature: JSON Formatter "line": 4, "keyword": "Given ", "result": { - "error_message": "Error: Step failure\n at Function.fail ($CUCUMBER_JS_HOME/lib/cucumber/support_code/step_definition.js:41:49)\n at World. ($CUCUMBER_JS_HOME/tmp/cucumber-js-sandbox/features/step_definitions/cucumber_steps.js:2:70)\n at Object.invoke ($CUCUMBER_JS_HOME/lib/cucumber/support_code/step_definition.js:52:14)\n at Object.execute ($CUCUMBER_JS_HOME/lib/cucumber/ast/step.js:157:22)\n at Object.acceptVisitor ($CUCUMBER_JS_HOME/lib/cucumber/ast/step.js:149:12)\n at Object.executeStep ($CUCUMBER_JS_HOME/lib/cucumber/runtime/ast_tree_walker.js:170:12)\n at Object.processStep ($CUCUMBER_JS_HOME/lib/cucumber/runtime/ast_tree_walker.js:165:14)\n at $CUCUMBER_JS_HOME/lib/cucumber/runtime/ast_tree_walker.js:67:16\n at callUserFunctionAndBroadcastAfterEvent ($CUCUMBER_JS_HOME/lib/cucumber/runtime/ast_tree_walker.js:91:9)\n at iterate ($CUCUMBER_JS_HOME/lib/cucumber/type/collection.js:14:11)", + "error_message": "ERROR_MESSAGE", "status": "failed" }, "match": { @@ -300,7 +297,7 @@ Feature: JSON Formatter "description": "", "line": 1, "keyword": "Feature", - "uri":"$CUCUMBER_JS_HOME/tmp/cucumber-js-sandbox/features/a.feature", + "uri":"/path/to/sandbox/tmp/cucumber-js-sandbox/features/a.feature", "elements": [ { "name": "I've declaired one step which passes", @@ -357,7 +354,7 @@ Feature: JSON Formatter "description": "", "line": 1, "keyword": "Feature", - "uri": "$CUCUMBER_JS_HOME/tmp/cucumber-js-sandbox/features/a.feature", + "uri": "/path/to/sandbox/tmp/cucumber-js-sandbox/features/a.feature", "elements": [ { "name": "I've declaired one step which is passing, one pending and one failing.", @@ -383,7 +380,7 @@ Feature: JSON Formatter "line": 5, "keyword": "And ", "result": { - "error_message": "TODO", + "error_message": "ERROR_MESSAGE", "status": "pending" }, "match": { @@ -437,7 +434,7 @@ Feature: JSON Formatter "description": "", "line": 1, "keyword": "Feature", - "uri": "$CUCUMBER_JS_HOME/tmp/cucumber-js-sandbox/features/a.feature", + "uri": "/path/to/sandbox/tmp/cucumber-js-sandbox/features/a.feature", "elements": [ { "name": "I've declaired one step which is passing, one pending and one failing.", @@ -452,7 +449,7 @@ Feature: JSON Formatter "line": 4, "keyword": "Given ", "result": { - "error_message": "TODO", + "error_message": "ERROR_MESSAGE", "status": "pending" }, "match": { @@ -516,7 +513,7 @@ Feature: JSON Formatter "description": "", "line": 1, "keyword": "Feature", - "uri": "$CUCUMBER_JS_HOME/tmp/cucumber-js-sandbox/features/a.feature", + "uri": "/path/to/sandbox/tmp/cucumber-js-sandbox/features/a.feature", "elements": [ { "name": "This one passes", @@ -552,7 +549,7 @@ Feature: JSON Formatter "line": 6, "keyword": "Given ", "result": { - "error_message": "Error: Step failure\n at Function.fail ($CUCUMBER_JS_HOME/lib/cucumber/support_code/step_definition.js:41:49)\n at World. ($CUCUMBER_JS_HOME/tmp/cucumber-js-sandbox/features/step_definitions/cucumber_steps.js:3:70)\n at Object.invoke ($CUCUMBER_JS_HOME/lib/cucumber/support_code/step_definition.js:52:14)\n at Object.execute ($CUCUMBER_JS_HOME/lib/cucumber/ast/step.js:157:22)\n at Object.acceptVisitor ($CUCUMBER_JS_HOME/lib/cucumber/ast/step.js:149:12)\n at Object.executeStep ($CUCUMBER_JS_HOME/lib/cucumber/runtime/ast_tree_walker.js:170:12)\n at Object.processStep ($CUCUMBER_JS_HOME/lib/cucumber/runtime/ast_tree_walker.js:165:14)\n at $CUCUMBER_JS_HOME/lib/cucumber/runtime/ast_tree_walker.js:67:16\n at callUserFunctionAndBroadcastAfterEvent ($CUCUMBER_JS_HOME/lib/cucumber/runtime/ast_tree_walker.js:91:9)\n at iterate ($CUCUMBER_JS_HOME/lib/cucumber/type/collection.js:14:11)", + "error_message": "ERROR_MESSAGE", "status": "failed" }, "match": { @@ -604,7 +601,7 @@ Feature: JSON Formatter "description": "", "line": 1, "keyword": "Feature", - "uri": "$CUCUMBER_JS_HOME/tmp/cucumber-js-sandbox/features/a.feature", + "uri": "/path/to/sandbox/tmp/cucumber-js-sandbox/features/a.feature", "elements": [ { "name": "This is the first feature", @@ -635,7 +632,7 @@ Feature: JSON Formatter "description": "", "line": 1, "keyword": "Feature", - "uri": "$CUCUMBER_JS_HOME/tmp/cucumber-js-sandbox/features/b.feature", + "uri": "/path/to/sandbox/tmp/cucumber-js-sandbox/features/b.feature", "elements": [ { "name": "This is the second feature", @@ -666,7 +663,7 @@ Feature: JSON Formatter "description": "", "line": 1, "keyword": "Feature", - "uri": "$CUCUMBER_JS_HOME/tmp/cucumber-js-sandbox/features/c.feature", + "uri": "/path/to/sandbox/tmp/cucumber-js-sandbox/features/c.feature", "elements": [ { "name": "This is the third feature", @@ -750,7 +747,7 @@ Feature: JSON Formatter "description": "", "line": 1, "keyword": "Feature", - "uri": "$CUCUMBER_JS_HOME/tmp/cucumber-js-sandbox/features/a.feature", + "uri": "/path/to/sandbox/tmp/cucumber-js-sandbox/features/a.feature", "elements": [ { "name": "This is the feature a scenario one", @@ -823,7 +820,7 @@ Feature: JSON Formatter "description": "", "line": 1, "keyword": "Feature", - "uri": "$CUCUMBER_JS_HOME/tmp/cucumber-js-sandbox/features/b.feature", + "uri": "/path/to/sandbox/tmp/cucumber-js-sandbox/features/b.feature", "elements": [ { "name": "This is the feature b scenario one", @@ -896,7 +893,7 @@ Feature: JSON Formatter "description": "", "line": 1, "keyword": "Feature", - "uri": "$CUCUMBER_JS_HOME/tmp/cucumber-js-sandbox/features/c.feature", + "uri": "/path/to/sandbox/tmp/cucumber-js-sandbox/features/c.feature", "elements": [ { "name": "This is the feature c scenario one", @@ -991,7 +988,7 @@ Feature: JSON Formatter "description": "", "line": 1, "keyword": "Feature", - "uri": "$CUCUMBER_JS_HOME/tmp/cucumber-js-sandbox/features/a.feature", + "uri": "/path/to/sandbox/tmp/cucumber-js-sandbox/features/a.feature", "elements": [ { "name": "", @@ -1040,7 +1037,7 @@ Feature: JSON Formatter "description": "", "line": 1, "keyword": "Feature", - "uri": "$CUCUMBER_JS_HOME/tmp/cucumber-js-sandbox/features/a.feature", + "uri": "/path/to/sandbox/tmp/cucumber-js-sandbox/features/a.feature", "elements": [ { "name": "", @@ -1089,7 +1086,7 @@ Feature: JSON Formatter "description": "", "line": 1, "keyword": "Feature", - "uri": "$CUCUMBER_JS_HOME/tmp/cucumber-js-sandbox/features/a.feature", + "uri": "/path/to/sandbox/tmp/cucumber-js-sandbox/features/a.feature", "elements": [ { "name": "Scenario with DocString", @@ -1151,7 +1148,7 @@ Feature: JSON Formatter "description": "", "line": 1, "keyword": "Feature", - "uri": "/Users/chris/src/cucumber-js-fork/tmp/cucumber-js-sandbox/features/a.feature", + "uri": "/path/to/sandbox/tmp/cucumber-js-sandbox/features/a.feature", "elements": [ { "name": "Background with DocString", @@ -1218,7 +1215,7 @@ Feature: JSON Formatter "line": 1 } ], - "uri": "$CUCUMBER_JS_HOME/tmp/cucumber-js-sandbox/features/a.feature", + "uri": "/path/to/sandbox/tmp/cucumber-js-sandbox/features/a.feature", "elements": [ { "name": "This scenario has no tags", @@ -1273,7 +1270,7 @@ Feature: JSON Formatter "description": "", "line": 1, "keyword": "Feature", - "uri": "$CUCUMBER_JS_HOME/tmp/cucumber-js-sandbox/features/a.feature", + "uri": "/path/to/sandbox/tmp/cucumber-js-sandbox/features/a.feature", "elements": [ { "name": "This scenario has tags", @@ -1345,7 +1342,7 @@ Feature: JSON Formatter "description": "", "line": 1, "keyword": "Feature", - "uri": "$CUCUMBER_JS_HOME/tmp/cucumber-js-sandbox/features/a.feature", + "uri": "/path/to/sandbox/tmp/cucumber-js-sandbox/features/a.feature", "elements": [ { "name": "This scenario contains a step with a table", @@ -1437,7 +1434,7 @@ Feature: JSON Formatter "description": "", "line": 1, "keyword": "Feature", - "uri": "$CUCUMBER_JS_HOME/tmp/cucumber-js-sandbox/features/a.feature", + "uri": "/path/to/sandbox/tmp/cucumber-js-sandbox/features/a.feature", "elements": [ { "name": "", diff --git a/features/step_definitions/cli_steps.js b/features/step_definitions/cli_steps.js index 9eb979c23..ee1bd855d 100644 --- a/features/step_definitions/cli_steps.js +++ b/features/step_definitions/cli_steps.js @@ -98,14 +98,6 @@ var cliSteps = function cliSteps() { throw new Error("Error parsing actual JSON:\n" + actualOutput); } - if (expectedOutput.indexOf('$CUCUMBER_JS_HOME') != -1) { - if (!process.env.CUCUMBER_JS_HOME) { - callback.fail(new Error("CUCUMBER_JS_HOME has not been set.")); - } else { - expectedOutput = expectedOutput.replace(/\$CUCUMBER_JS_HOME/g, process.env.CUCUMBER_JS_HOME); - } - } - try { var expectedJson = JSON.parse(expectedOutput); } @@ -114,6 +106,12 @@ var cliSteps = function cliSteps() { } var actualJsonString = JSON.stringify(actualJson, null, 2); + + // remove path to sandbox from uris + actualJsonString = actualJsonString.replace(/(\"uri\"\:\ \")(.+?)(\/tmp\/cucumber-js-sandbox\/)/g, "$1/path/to/sandbox$3"); + // remove location specific error messages + actualJsonString = actualJsonString.replace(/(\"error_message\"\:\ \")(.+?)(\"\,)/g, "$1ERROR_MESSAGE$3"); + var expectedJsonString = JSON.stringify(expectedJson, null, 2); if (actualJsonString != expectedJsonString) From 64d54384b1a414861cd9f2cf805861df4ac52ac1 Mon Sep 17 00:00:00 2001 From: Chris Young Date: Mon, 6 Aug 2012 12:38:54 +0100 Subject: [PATCH 43/44] Updated package.json to indicate dependency on version of gherkin that provides json formatter --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 7771cef91..a998a0f3b 100644 --- a/package.json +++ b/package.json @@ -40,7 +40,7 @@ "node": "0.6 || 0.7 || 0.8" }, "dependencies": { - "gherkin": "2.11.0", + "gherkin": "2.11.1", "jasmine-node": "1.0.26", "connect": "2.3.2", "browserify": "1.13.2", From 2c9d5e0397fe9405565bc259d0501392b8f65ba4 Mon Sep 17 00:00:00 2001 From: Chris Young Date: Mon, 6 Aug 2012 14:55:01 +0100 Subject: [PATCH 44/44] Made JsonFormatter consistent with others by making constructor take no arguments. Removed 'TODO' strings from output of formatter. Where a value is undefined it is passed to the Gherkin formatter as undefined. This as the effect of supressing these values in the final JSON output. Changed underscore_separated_variable to be camelCase. --- features/json_formatter.feature | 45 +------------------ lib/cucumber/ast/feature.js | 5 +-- lib/cucumber/cli/configuration.js | 2 +- lib/cucumber/listener/json_formatter.js | 16 +++---- spec/cucumber/listener/json_formatter_spec.js | 24 +++++----- 5 files changed, 25 insertions(+), 67 deletions(-) diff --git a/features/json_formatter.feature b/features/json_formatter.feature index 2f845a1f5..f4984f35d 100644 --- a/features/json_formatter.feature +++ b/features/json_formatter.feature @@ -87,9 +87,8 @@ Feature: JSON Formatter "result": {"status":"undefined" }, - "match": - {"location":"TODO" - } + "match": { + } } ] } @@ -144,7 +143,6 @@ Feature: JSON Formatter "status": "undefined" }, "match": { - "location": "TODO" } }, { @@ -155,7 +153,6 @@ Feature: JSON Formatter "status": "skipped" }, "match": { - "location": "TODO" } } ] @@ -206,11 +203,9 @@ Feature: JSON Formatter "line": 4, "keyword": "Given ", "result": { - "error_message": "ERROR_MESSAGE", "status": "pending" }, "match": { - "location": "TODO" } } ] @@ -263,7 +258,6 @@ Feature: JSON Formatter "status": "failed" }, "match": { - "location": "TODO" } } ] @@ -315,7 +309,6 @@ Feature: JSON Formatter "status": "passed" }, "match": { - "location": "TODO" } } ] @@ -372,7 +365,6 @@ Feature: JSON Formatter "status": "passed" }, "match": { - "location": "TODO" } }, { @@ -380,11 +372,9 @@ Feature: JSON Formatter "line": 5, "keyword": "And ", "result": { - "error_message": "ERROR_MESSAGE", "status": "pending" }, "match": { - "location": "TODO" } }, { @@ -395,7 +385,6 @@ Feature: JSON Formatter "status": "skipped" }, "match": { - "location": "TODO" } } ] @@ -449,11 +438,9 @@ Feature: JSON Formatter "line": 4, "keyword": "Given ", "result": { - "error_message": "ERROR_MESSAGE", "status": "pending" }, "match": { - "location": "TODO" } }, { @@ -464,7 +451,6 @@ Feature: JSON Formatter "status": "skipped" }, "match": { - "location": "TODO" } }, { @@ -475,7 +461,6 @@ Feature: JSON Formatter "status": "skipped" }, "match": { - "location": "TODO" } } ] @@ -531,7 +516,6 @@ Feature: JSON Formatter "status": "passed" }, "match": { - "location": "TODO" } } ] @@ -553,7 +537,6 @@ Feature: JSON Formatter "status": "failed" }, "match": { - "location": "TODO" } } ] @@ -619,7 +602,6 @@ Feature: JSON Formatter "status": "passed" }, "match": { - "location": "TODO" } } ] @@ -650,7 +632,6 @@ Feature: JSON Formatter "status": "passed" }, "match": { - "location": "TODO" } } ] @@ -681,7 +662,6 @@ Feature: JSON Formatter "status": "passed" }, "match": { - "location": "TODO" } } ] @@ -765,7 +745,6 @@ Feature: JSON Formatter "status": "passed" }, "match": { - "location": "TODO" } } ] @@ -786,7 +765,6 @@ Feature: JSON Formatter "status": "passed" }, "match": { - "location": "TODO" } } ] @@ -807,7 +785,6 @@ Feature: JSON Formatter "status": "passed" }, "match": { - "location": "TODO" } } ] @@ -838,7 +815,6 @@ Feature: JSON Formatter "status": "passed" }, "match": { - "location": "TODO" } } ] @@ -859,7 +835,6 @@ Feature: JSON Formatter "status": "passed" }, "match": { - "location": "TODO" } } ] @@ -880,7 +855,6 @@ Feature: JSON Formatter "status": "passed" }, "match": { - "location": "TODO" } } ] @@ -911,7 +885,6 @@ Feature: JSON Formatter "status": "passed" }, "match": { - "location": "TODO" } } ] @@ -932,7 +905,6 @@ Feature: JSON Formatter "status": "passed" }, "match": { - "location": "TODO" } } ] @@ -953,7 +925,6 @@ Feature: JSON Formatter "status": "passed" }, "match": { - "location": "TODO" } } ] @@ -1110,7 +1081,6 @@ Feature: JSON Formatter "status": "passed" }, "match": { - "location": "TODO" } } ] @@ -1233,7 +1203,6 @@ Feature: JSON Formatter "status": "passed" }, "match": { - "location": "TODO" } } ] @@ -1302,7 +1271,6 @@ Feature: JSON Formatter "status": "passed" }, "match": { - "location": "TODO" } } ] @@ -1358,7 +1326,6 @@ Feature: JSON Formatter "keyword": "Given ", "rows": [ { - "line": "TODO", "cells": [ "col 1", "col 2", @@ -1366,7 +1333,6 @@ Feature: JSON Formatter ] }, { - "line": "TODO", "cells": [ "one", "two", @@ -1374,7 +1340,6 @@ Feature: JSON Formatter ] }, { - "line": "TODO", "cells": [ "1", "2", @@ -1382,7 +1347,6 @@ Feature: JSON Formatter ] }, { - "line": "TODO", "cells": [ "!", "~", @@ -1394,7 +1358,6 @@ Feature: JSON Formatter "status": "passed" }, "match": { - "location": "TODO" } } ] @@ -1449,7 +1412,6 @@ Feature: JSON Formatter "keyword": "Given ", "rows": [ { - "line": "TODO", "cells": [ "col 1", "col 2", @@ -1457,7 +1419,6 @@ Feature: JSON Formatter ] }, { - "line": "TODO", "cells": [ "one", "two", @@ -1465,7 +1426,6 @@ Feature: JSON Formatter ] }, { - "line": "TODO", "cells": [ "1", "2", @@ -1473,7 +1433,6 @@ Feature: JSON Formatter ] }, { - "line": "TODO", "cells": [ "!", "~", diff --git a/lib/cucumber/ast/feature.js b/lib/cucumber/ast/feature.js index 53c4c3669..30048d8ca 100644 --- a/lib/cucumber/ast/feature.js +++ b/lib/cucumber/ast/feature.js @@ -1,4 +1,4 @@ -var Feature = function(keyword, name, description, uri, line) { + var Feature = function(keyword, name, description, uri, line) { var Cucumber = require('../../cucumber'); var background; @@ -27,9 +27,8 @@ var Feature = function(keyword, name, description, uri, line) { }, getUri: function getUri() { - //TODO: Add uri to feature if (!uri) { - return 'TODO'; + return undefined; } else { return uri; } diff --git a/lib/cucumber/cli/configuration.js b/lib/cucumber/cli/configuration.js index c29c19d61..0c82d6003 100644 --- a/lib/cucumber/cli/configuration.js +++ b/lib/cucumber/cli/configuration.js @@ -16,7 +16,7 @@ var Configuration = function(argv) { formatter = Cucumber.Listener.PrettyFormatter(); break; case Configuration.JSON_FORMAT_NAME: - formatter = Cucumber.Listener.JsonFormatter(process.stdout); + formatter = Cucumber.Listener.JsonFormatter(); break; case Configuration.SUMMARY_FORMAT_NAME: formatter = Cucumber.Listener.SummaryFormatter(); diff --git a/lib/cucumber/listener/json_formatter.js b/lib/cucumber/listener/json_formatter.js index 07ece0537..7351bef85 100644 --- a/lib/cucumber/listener/json_formatter.js +++ b/lib/cucumber/listener/json_formatter.js @@ -1,7 +1,7 @@ -var JsonFormatter = function(io) { +var JsonFormatter = function() { var GherkinJsonFormatter = require('gherkin/lib/gherkin/formatter/json_formatter'); - var gherkinJsonFormatter = new GherkinJsonFormatter(io); + var gherkinJsonFormatter = new GherkinJsonFormatter(process.stdout); var currentFeatureId = 'undefined'; @@ -27,7 +27,7 @@ var JsonFormatter = function(io) { var raw = tableContents.raw(); var tableProperties = []; for (rawRow in raw) { - var row = {line: 'TODO', cells: raw[rawRow]}; + var row = {line: undefined, cells: raw[rawRow]}; tableProperties.push(row); } stepProperties['rows'] = tableProperties; @@ -37,12 +37,12 @@ var JsonFormatter = function(io) { } - self.formatTags = function formatTags(tags, parent_tags) { + self.formatTags = function formatTags(tags, parentTags) { var tagsProperties = []; for (tag in tags) { var isParentTag = false; - for (parent_tag in parent_tags) { - if ((tags[tag].getName() == parent_tags[parent_tag].getName()) && (tags[tag].getLine() == parent_tags[parent_tag].getLine())) { + for (parentTag in parentTags) { + if ((tags[tag].getName() == parentTags[parentTag].getName()) && (tags[tag].getLine() == parentTags[parentTag].getLine())) { isParentTag = true; } } @@ -120,7 +120,7 @@ var JsonFormatter = function(io) { } else if (stepResult.isPending()) { resultStatus = 'pending'; - stepOutput['error_message'] = 'TODO'; + stepOutput['error_message'] = undefined; } else if (stepResult.isSkipped()) { resultStatus = 'skipped'; @@ -138,7 +138,7 @@ var JsonFormatter = function(io) { stepOutput['status'] = resultStatus; gherkinJsonFormatter.result(stepOutput); - gherkinJsonFormatter.match({location: 'TODO'}); + gherkinJsonFormatter.match({location: undefined}); callback(); } diff --git a/spec/cucumber/listener/json_formatter_spec.js b/spec/cucumber/listener/json_formatter_spec.js index 242f34a04..a2afbb0f2 100644 --- a/spec/cucumber/listener/json_formatter_spec.js +++ b/spec/cucumber/listener/json_formatter_spec.js @@ -32,7 +32,7 @@ describe("Cucumber.Listener.JsonFormatterWrapper", function() { getName: 'A Name', getDescription: 'A Description', getLine: 3, - getUri: 'TODO', + getUri: undefined, getTags: false}); event = createSpyWithStubs("event", {getPayloadItem: feature}); @@ -42,7 +42,7 @@ describe("Cucumber.Listener.JsonFormatterWrapper", function() { it("adds the feature attributes to the output", function() { listener.handleBeforeFeatureEvent(event, callback); - expect(formatter.uri).toHaveBeenCalledWith('TODO'); + expect(formatter.uri).toHaveBeenCalledWith(undefined); expect(formatter.feature).toHaveBeenCalledWith({id: 'A-Name', name: 'A Name', description: 'A Description', @@ -208,9 +208,9 @@ describe("Cucumber.Listener.JsonFormatterWrapper", function() { expect(formatter.step).toHaveBeenCalledWith({name: 'Step', line: 3, keyword: 'Step', - rows: [{line : 'TODO', cells: ['a:1', 'a:2', 'a:3'] }, - {line : 'TODO', cells: ['b:1', 'b:2', 'b:3'] }, - {line : 'TODO', cells: ['c:1', 'c:2', 'c:3'] }] + rows: [{line : undefined, cells: ['a:1', 'a:2', 'a:3'] }, + {line : undefined, cells: ['b:1', 'b:2', 'b:3'] }, + {line : undefined, cells: ['c:1', 'c:2', 'c:3'] }] }); }); @@ -284,7 +284,7 @@ describe("Cucumber.Listener.JsonFormatterWrapper", function() { expect(formatter.step).toHaveBeenCalledWith({name: 'Step', line: 3, keyword: 'Step'}); expect(formatter.result).toHaveBeenCalledWith({status: 'failed'}); - expect(formatter.match).toHaveBeenCalledWith({location: 'TODO'}); + expect(formatter.match).toHaveBeenCalledWith({location: undefined}); }); @@ -315,7 +315,7 @@ describe("Cucumber.Listener.JsonFormatterWrapper", function() { expect(formatter.step).toHaveBeenCalledWith({name: 'Step', line: 3, keyword: 'Step'}); expect(formatter.result).toHaveBeenCalledWith({status: 'passed'}); - expect(formatter.match).toHaveBeenCalledWith({location: 'TODO'}); + expect(formatter.match).toHaveBeenCalledWith({location: undefined}); }); @@ -345,8 +345,8 @@ describe("Cucumber.Listener.JsonFormatterWrapper", function() { listener.handleStepResultEvent(fakeEvent, callback); expect(formatter.step).toHaveBeenCalledWith({name: 'Step', line: 3, keyword: 'Step'}); - expect(formatter.result).toHaveBeenCalledWith({status: 'pending', error_message: 'TODO'}); - expect(formatter.match).toHaveBeenCalledWith({location: 'TODO'}); + expect(formatter.result).toHaveBeenCalledWith({status: 'pending', error_message: undefined}); + expect(formatter.match).toHaveBeenCalledWith({location: undefined}); }); @@ -377,7 +377,7 @@ describe("Cucumber.Listener.JsonFormatterWrapper", function() { expect(formatter.step).toHaveBeenCalledWith({name: 'Step', line: 3, keyword: 'Step'}); expect(formatter.result).toHaveBeenCalledWith({status: 'failed'}); - expect(formatter.match).toHaveBeenCalledWith({location: 'TODO'}); + expect(formatter.match).toHaveBeenCalledWith({location: undefined}); }); @@ -408,7 +408,7 @@ describe("Cucumber.Listener.JsonFormatterWrapper", function() { expect(formatter.step).toHaveBeenCalledWith({name: 'Step', line: 3, keyword: 'Step'}); expect(formatter.result).toHaveBeenCalledWith({status: 'skipped'}); - expect(formatter.match).toHaveBeenCalledWith({location: 'TODO'}); + expect(formatter.match).toHaveBeenCalledWith({location: undefined}); }); @@ -439,7 +439,7 @@ describe("Cucumber.Listener.JsonFormatterWrapper", function() { expect(formatter.step).toHaveBeenCalledWith({name: 'Step', line: 3, keyword: 'Step'}); expect(formatter.result).toHaveBeenCalledWith({status: 'undefined'}); - expect(formatter.match).toHaveBeenCalledWith({location: 'TODO'}); + expect(formatter.match).toHaveBeenCalledWith({location: undefined}); });