Skip to content

Commit

Permalink
Filter stack traces (close #157)
Browse files Browse the repository at this point in the history
  • Loading branch information
jbpros committed May 15, 2015
1 parent d9a5b06 commit f584233
Show file tree
Hide file tree
Showing 13 changed files with 183 additions and 17 deletions.
6 changes: 6 additions & 0 deletions features/synchronous_callbacks.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
Feature: Synchronous callbacks

Scenario: calling back synchronously from a step definition
Given a scenario calling a synchronous step definition
When Cucumber executes the scenario
Then the scenario passes
15 changes: 11 additions & 4 deletions lib/cucumber/cli/argument_parser.js
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ function ArgumentParser(argv) {
definitions[ArgumentParser.COFFEE_SCRIPT_SNIPPETS_FLAG_NAME] = Boolean;
definitions[ArgumentParser.SNIPPETS_FLAG_NAME] = Boolean;
definitions[ArgumentParser.STRICT_FLAG_NAME] = Boolean;
definitions[ArgumentParser.BACKTRACE_FLAG_NAME] = Boolean;
return definitions;
},

Expand All @@ -82,6 +83,7 @@ function ArgumentParser(argv) {
definitions[ArgumentParser.HELP_FLAG_SHORT_NAME] = [ArgumentParser.LONG_OPTION_PREFIX + ArgumentParser.HELP_FLAG_NAME];
definitions[ArgumentParser.SNIPPETS_FLAG_SHORT_NAME] = [ArgumentParser.LONG_OPTION_PREFIX + 'no-' + ArgumentParser.SNIPPETS_FLAG_NAME];
definitions[ArgumentParser.STRICT_FLAG_SHORT_NAME] = [ArgumentParser.LONG_OPTION_PREFIX + ArgumentParser.STRICT_FLAG_NAME];
definitions[ArgumentParser.BACKTRACE_FLAG_SHORT_NAME] = [ArgumentParser.LONG_OPTION_PREFIX + ArgumentParser.BACKTRACE_FLAG_NAME];
return definitions;
},

Expand All @@ -98,13 +100,15 @@ function ArgumentParser(argv) {
},

shouldSnippetsBeInCoffeeScript: function shouldSnippetsBeInCoffeeScript() {
var areSnippetsInCoffeeScript = self.getOptionOrDefault(ArgumentParser.COFFEE_SCRIPT_SNIPPETS_FLAG_NAME, ArgumentParser.DEFAULT_COFFEE_SCRIPT_SNIPPETS_FLAG_VALUE);
return areSnippetsInCoffeeScript;
return self.getOptionOrDefault(ArgumentParser.COFFEE_SCRIPT_SNIPPETS_FLAG_NAME, ArgumentParser.DEFAULT_COFFEE_SCRIPT_SNIPPETS_FLAG_VALUE);
},

shouldSnippetsBeShown: function shouldSnippetsBeInCoffeeScript() {
var areSnippetsShown = self.getOptionOrDefault(ArgumentParser.SNIPPETS_FLAG_NAME, ArgumentParser.DEFAULT_SNIPPETS_FLAG_VALUE);
return areSnippetsShown;
return self.getOptionOrDefault(ArgumentParser.SNIPPETS_FLAG_NAME, ArgumentParser.DEFAULT_SNIPPETS_FLAG_VALUE);
},

shouldFilterStackTraces: function shouldFilterStackTraces() {
return !self.getOptionOrDefault(ArgumentParser.BACKTRACE_FLAG_NAME, ArgumentParser.DEFAULT_BACKTRACE_FLAG_VALUE);
},

storeOptions: function storeOptions(newOptions) {
Expand Down Expand Up @@ -149,6 +153,9 @@ ArgumentParser.DEFAULT_COFFEE_SCRIPT_SNIPPETS_FLAG_VALUE = false;
ArgumentParser.SNIPPETS_FLAG_NAME = 'snippets';
ArgumentParser.SNIPPETS_FLAG_SHORT_NAME = 'i';
ArgumentParser.DEFAULT_SNIPPETS_FLAG_VALUE = true;
ArgumentParser.BACKTRACE_FLAG_NAME = 'backtrace';
ArgumentParser.BACKTRACE_FLAG_SHORT_NAME = 'b';
ArgumentParser.DEFAULT_BACKTRACE_FLAG_VALUE = false;
ArgumentParser.FeaturePathExpander = require('./argument_parser/feature_path_expander');
ArgumentParser.PathExpander = require('./argument_parser/path_expander');
ArgumentParser.SupportCodePathExpander = require('./argument_parser/support_code_path_expander');
Expand Down
11 changes: 6 additions & 5 deletions lib/cucumber/cli/configuration.js
Original file line number Diff line number Diff line change
Expand Up @@ -80,15 +80,16 @@ function Configuration(argv) {
},

shouldSnippetsBeInCoffeeScript: function shouldSnippetsBeInCoffeeScript() {
var coffeeDisplay = argumentParser.shouldSnippetsBeInCoffeeScript();
return coffeeDisplay;
return argumentParser.shouldSnippetsBeInCoffeeScript();
},

shouldSnippetsBeShown: function shouldSnippetsBeShown() {
var snippetsDisplay = argumentParser.shouldSnippetsBeShown();
return snippetsDisplay;
}
return argumentParser.shouldSnippetsBeShown();
},

shouldFilterStackTraces: function shouldFilterStackTraces() {
return argumentParser.shouldFilterStackTraces();
}
};
return self;
}
Expand Down
3 changes: 2 additions & 1 deletion lib/cucumber/listener/summary_formatter.js
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,8 @@ function SummaryFormatter(options) {

self.logFailedStepResult = function logFailedStepResult(stepResult) {
var failureMessage = stepResult.getFailureException();
self.log(failureMessage.stack || failureMessage);
if (failureMessage)
self.log(failureMessage.stack || failureMessage);
self.log('\n\n');
};

Expand Down
10 changes: 9 additions & 1 deletion lib/cucumber/runtime.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,14 @@ function Runtime(configuration) {
var features = self.getFeatures();
var supportCodeLibrary = self.getSupportCodeLibrary();
var astTreeWalker = Runtime.AstTreeWalker(features, supportCodeLibrary, listeners, isStrictRequested);
astTreeWalker.walk(callback);

if (configuration.shouldFilterStackTraces())
Runtime.StackTraceFilter.filter();

astTreeWalker.walk(function (result) {
Runtime.StackTraceFilter.unfilter();
callback(result);
});
},

attachListener: function attachListener(listener) {
Expand Down Expand Up @@ -48,5 +55,6 @@ Runtime.FailedStepResult = require('./runtime/failed_step_result');
Runtime.SkippedStepResult = require('./runtime/skipped_step_result');
Runtime.UndefinedStepResult = require('./runtime/undefined_step_result');
Runtime.Attachment = require('./runtime/attachment');
Runtime.StackTraceFilter = require('./runtime/stack_trace_filter');

module.exports = Runtime;
4 changes: 2 additions & 2 deletions lib/cucumber/runtime/ast_tree_walker.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ function AstTreeWalker(features, supportCodeLibrary, listeners, strictMode) {
visitFeatures: function visitFeatures(features, callback) {
var payload = { features: features };
var event = AstTreeWalker.Event(AstTreeWalker.FEATURES_EVENT_NAME, payload);
self.broadcastEventAroundUserFunction (
self.broadcastEventAroundUserFunction(
event,
function (callback) { features.acceptVisitor(self, callback); },
callback
Expand All @@ -38,7 +38,7 @@ function AstTreeWalker(features, supportCodeLibrary, listeners, strictMode) {
visitFeature: function visitFeature(feature, callback) {
var payload = { feature: feature };
var event = AstTreeWalker.Event(AstTreeWalker.FEATURE_EVENT_NAME, payload);
self.broadcastEventAroundUserFunction (
self.broadcastEventAroundUserFunction(
event,
function (callback) { feature.acceptVisitor(self, callback); },
callback
Expand Down
23 changes: 23 additions & 0 deletions lib/cucumber/runtime/stack_trace_filter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
var path = require('path');
var chain = require('stack-chain');

var currentFilter = null;

function filter() {
currentFilter = chain.filter.attach(function (error, frames) {
return frames.filter(function (frame) {
var f = frame.getFileName() || '';
var ignoredPath = path.join(__dirname, '..');
return f.indexOf(ignoredPath) === -1;
});
});
}

function unfilter() {
chain.filter.deattach(currentFilter);
}

module.exports = {
filter: filter,
unfilter: unfilter
};
5 changes: 5 additions & 0 deletions lib/cucumber/volatile_configuration.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ function VolatileConfiguration(features, supportCodeInitializer, options) {
options = options || {};
var strictMode = !!options.strict;
var tagGroupStrings = options.tags || [];
var backtrace = !!options.backtrace;

var self = {
isStrictMode: function isStrictMode() {
Expand Down Expand Up @@ -44,6 +45,10 @@ function VolatileConfiguration(features, supportCodeInitializer, options) {
var tagGroup = tagGroupParser.parse();
var rule = Cucumber.Ast.Filter.AnyOfTagsRule(tagGroup);
return rule;
},

shouldFilterStackTraces: function shouldFilterStackTraces() {
return !backtrace;
}
};
return self;
Expand Down
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -78,8 +78,10 @@
"coffee-script": "1.8.0",
"cucumber-html": "0.2.3",
"gherkin": "2.12.2",
"hide-stack-frames-from": "^1.0.0",
"nopt": "3.0.1",
"pogo": "0.9.4",
"stack-chain": "^1.3.1",
"underscore": "1.7.0",
"underscore.string": "2.3.3",
"walkdir": "0.0.7"
Expand Down
34 changes: 34 additions & 0 deletions spec/cucumber/cli/argument_parser_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,11 @@ describe("Cucumber.Cli.ArgumentParser", function () {
var knownOptionDefinitions = argumentParser.getKnownOptionDefinitions();
expect(knownOptionDefinitions[Cucumber.Cli.ArgumentParser.SNIPPETS_FLAG_NAME]).toEqual(Boolean);
});

it("defines a --backtrace flag", function () {
var knownOptionDefinitions = argumentParser.getKnownOptionDefinitions();
expect(knownOptionDefinitions[Cucumber.Cli.ArgumentParser.BACKTRACE_FLAG_NAME]).toEqual(Boolean);
});
});

describe("getShortenedOptionDefinitions()", function () {
Expand Down Expand Up @@ -135,6 +140,14 @@ describe("Cucumber.Cli.ArgumentParser", function () {
var shortenedOptionDefinitions = argumentParser.getShortenedOptionDefinitions();
expect(shortenedOptionDefinitions[aliasName]).toEqual(aliasValue);
});

it("defines an alias to --backtrace as -b", function () {
var optionName = Cucumber.Cli.ArgumentParser.LONG_OPTION_PREFIX + Cucumber.Cli.ArgumentParser.BACKTRACE_FLAG_NAME;
var aliasName = Cucumber.Cli.ArgumentParser.BACKTRACE_FLAG_SHORT_NAME;
var aliasValue = [optionName];
var shortenedOptionDefinitions = argumentParser.getShortenedOptionDefinitions();
expect(shortenedOptionDefinitions[aliasName]).toEqual(aliasValue);
});
});

describe("getFeatureFilePaths()", function () {
Expand Down Expand Up @@ -416,6 +429,27 @@ describe("Cucumber.Cli.ArgumentParser", function () {
});
});

describe("shouldFilterStackTraces()", function () {
beforeEach(function () {
spyOn(argumentParser, 'getOptionOrDefault');
});

it("gets the 'backtrace' flag with a falsy default value", function () {
argumentParser.shouldFilterStackTraces();
expect(argumentParser.getOptionOrDefault).toHaveBeenCalledWith("backtrace", false);
});

it("returns true when the backtrace flag isn't set", function () {
argumentParser.getOptionOrDefault.andReturn(false);
expect(argumentParser.shouldFilterStackTraces()).toBeTruthy();
});

it("returns false when the backtrace flag is set", function () {
argumentParser.getOptionOrDefault.andReturn(true);
expect(argumentParser.shouldFilterStackTraces()).toBeFalsy();
});
});

describe("getOptions() [storeOptions()]", function () {
var options;

Expand Down
17 changes: 17 additions & 0 deletions spec/cucumber/cli/configuration_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -335,4 +335,21 @@ describe("Cucumber.Cli.Configuration", function () {
});
});

describe("shouldFilterStackTraces()", function () {
beforeEach(function () {
spyOnStub(argumentParser, 'shouldFilterStackTraces');
});

it("asks the argument parser whether the stack traces are filtered", function () {
configuration.shouldFilterStackTraces();
expect(argumentParser.shouldFilterStackTraces).toHaveBeenCalled();
});

it("tells whether the stack traces are filtered or not", function () {
var shouldStackTracesBeFiltered = createSpy("filter stack traces?");
argumentParser.shouldFilterStackTraces.andReturn(shouldStackTracesBeFiltered);
expect(configuration.shouldFilterStackTraces()).toBe(shouldStackTracesBeFiltered);
});
});

});
48 changes: 46 additions & 2 deletions spec/cucumber/runtime_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,10 @@ describe("Cucumber.Runtime", function () {
beforeEach(function () {
isStrictRequested = false;
listeners = createSpyWithStubs("listener collection", {add: null});
configuration = createSpyWithStubs("configuration", { isStrictRequested: isStrictRequested });
configuration = createSpyWithStubs("configuration", { isStrictRequested: isStrictRequested, shouldFilterStackTraces: true });
spyOn(Cucumber.Type, 'Collection').andReturn(listeners);
spyOn(Cucumber.Runtime.StackTraceFilter, 'filter');
spyOn(Cucumber.Runtime.StackTraceFilter, 'unfilter');
runtime = Cucumber.Runtime(configuration);
});

Expand Down Expand Up @@ -69,9 +71,51 @@ describe("Cucumber.Runtime", function () {
expect(Cucumber.Runtime.AstTreeWalker).toHaveBeenCalledWith(features, supportCodeLibrary, listeners, isStrictRequested);
});

describe("when stack traces should be filtered", function () {
beforeEach(function () {
configuration.shouldFilterStackTraces.andReturn(true);
});

it("activates the stack trace filter", function () {
runtime.start(callback);
expect(Cucumber.Runtime.StackTraceFilter.filter).toHaveBeenCalled();
});
});

describe("when stack traces should be unfiltered", function () {
beforeEach(function () {
configuration.shouldFilterStackTraces.andReturn(false);
});

it("does not activate the stack trace filter", function () {
runtime.start(callback);
expect(Cucumber.Runtime.StackTraceFilter.filter).not.toHaveBeenCalled();
});
});

it("tells the AST tree walker to walk", function () {
runtime.start(callback);
expect(astTreeWalker.walk).toHaveBeenCalledWith(callback);
expect(astTreeWalker.walk).toHaveBeenCalledWithAFunctionAsNthParameter(1);
});

describe("when the AST tree walker is done walking", function () {
var walkCallback, walkResults;

beforeEach(function () {
runtime.start(callback);
walkCallback = astTreeWalker.walk.mostRecentCall.args[0];
walkResults = createSpy("AST tree walker results");
});

it("deactivates the stack trace filter", function () {
walkCallback(walkResults);
expect(Cucumber.Runtime.StackTraceFilter.unfilter).toHaveBeenCalled();
});

it("calls back", function () {
walkCallback(walkResults);
expect(callback).toHaveBeenCalledWith(walkResults);
});
});
});

Expand Down
22 changes: 20 additions & 2 deletions spec/cucumber/volatile_configuration_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -78,11 +78,12 @@ describe("Cucumber.VolatileConfiguration", function () {

describe("isStrictMode()",function () {
it("is false if strict option is not specified",function () {
configuration = Cucumber.VolatileConfiguration(featureSources, supportCodeInitializer, {});
configuration = Cucumber.VolatileConfiguration(featureSources, supportCodeInitializer, {});
expect(configuration.isStrictMode()).toEqual(false);
});

it("is true if strict option is set",function () {
configuration = Cucumber.VolatileConfiguration(featureSources, supportCodeInitializer, {strict:true});
configuration = Cucumber.VolatileConfiguration(featureSources, supportCodeInitializer, {strict:true});
expect(configuration.isStrictMode()).toEqual(true);
});
});
Expand Down Expand Up @@ -151,4 +152,21 @@ describe("Cucumber.VolatileConfiguration", function () {
expect(returned).toBe(rule);
});
});

describe("shouldFilterStackTraces", function () {
it("returns true by default", function () {
configuration = Cucumber.VolatileConfiguration(featureSources, supportCodeInitializer, {});
expect(configuration.shouldFilterStackTraces()).toBeTruthy();
});

it("returns false when the backtrace option is truthy", function () {
configuration = Cucumber.VolatileConfiguration(featureSources, supportCodeInitializer, { backtrace: true });
expect(configuration.shouldFilterStackTraces()).toBeFalsy();
});

it("returns true when the backtrace option is falsy", function () {
configuration = Cucumber.VolatileConfiguration(featureSources, supportCodeInitializer, { backtrace: false});
expect(configuration.shouldFilterStackTraces()).toBeTruthy();
});
});
});

0 comments on commit f584233

Please sign in to comment.