Skip to content

Commit d1df428

Browse files
jbprosVadim Ivanov
authored and
Vadim Ivanov
committed
Filter stack traces (close cucumber#157)
1 parent 0f3876d commit d1df428

13 files changed

+183
-17
lines changed
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
Feature: Synchronous callbacks
2+
3+
Scenario: calling back synchronously from a step definition
4+
Given a scenario calling a synchronous step definition
5+
When Cucumber executes the scenario
6+
Then the scenario passes

lib/cucumber/cli/argument_parser.js

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ function ArgumentParser(argv) {
8383
definitions[ArgumentParser.COFFEE_SCRIPT_SNIPPETS_FLAG_NAME] = Boolean;
8484
definitions[ArgumentParser.SNIPPETS_FLAG_NAME] = Boolean;
8585
definitions[ArgumentParser.STRICT_FLAG_NAME] = Boolean;
86+
definitions[ArgumentParser.BACKTRACE_FLAG_NAME] = Boolean;
8687
return definitions;
8788
},
8889

@@ -93,6 +94,7 @@ function ArgumentParser(argv) {
9394
definitions[ArgumentParser.HELP_FLAG_SHORT_NAME] = [ArgumentParser.LONG_OPTION_PREFIX + ArgumentParser.HELP_FLAG_NAME];
9495
definitions[ArgumentParser.SNIPPETS_FLAG_SHORT_NAME] = [ArgumentParser.LONG_OPTION_PREFIX + 'no-' + ArgumentParser.SNIPPETS_FLAG_NAME];
9596
definitions[ArgumentParser.STRICT_FLAG_SHORT_NAME] = [ArgumentParser.LONG_OPTION_PREFIX + ArgumentParser.STRICT_FLAG_NAME];
97+
definitions[ArgumentParser.BACKTRACE_FLAG_SHORT_NAME] = [ArgumentParser.LONG_OPTION_PREFIX + ArgumentParser.BACKTRACE_FLAG_NAME];
9698
return definitions;
9799
},
98100

@@ -109,13 +111,15 @@ function ArgumentParser(argv) {
109111
},
110112

111113
shouldSnippetsBeInCoffeeScript: function shouldSnippetsBeInCoffeeScript() {
112-
var areSnippetsInCoffeeScript = self.getOptionOrDefault(ArgumentParser.COFFEE_SCRIPT_SNIPPETS_FLAG_NAME, ArgumentParser.DEFAULT_COFFEE_SCRIPT_SNIPPETS_FLAG_VALUE);
113-
return areSnippetsInCoffeeScript;
114+
return self.getOptionOrDefault(ArgumentParser.COFFEE_SCRIPT_SNIPPETS_FLAG_NAME, ArgumentParser.DEFAULT_COFFEE_SCRIPT_SNIPPETS_FLAG_VALUE);
114115
},
115116

116117
shouldSnippetsBeShown: function shouldSnippetsBeInCoffeeScript() {
117-
var areSnippetsShown = self.getOptionOrDefault(ArgumentParser.SNIPPETS_FLAG_NAME, ArgumentParser.DEFAULT_SNIPPETS_FLAG_VALUE);
118-
return areSnippetsShown;
118+
return self.getOptionOrDefault(ArgumentParser.SNIPPETS_FLAG_NAME, ArgumentParser.DEFAULT_SNIPPETS_FLAG_VALUE);
119+
},
120+
121+
shouldFilterStackTraces: function shouldFilterStackTraces() {
122+
return !self.getOptionOrDefault(ArgumentParser.BACKTRACE_FLAG_NAME, ArgumentParser.DEFAULT_BACKTRACE_FLAG_VALUE);
119123
},
120124

121125
storeOptions: function storeOptions(newOptions) {
@@ -160,6 +164,9 @@ ArgumentParser.DEFAULT_COFFEE_SCRIPT_SNIPPETS_FLAG_VALUE = false;
160164
ArgumentParser.SNIPPETS_FLAG_NAME = 'snippets';
161165
ArgumentParser.SNIPPETS_FLAG_SHORT_NAME = 'i';
162166
ArgumentParser.DEFAULT_SNIPPETS_FLAG_VALUE = true;
167+
ArgumentParser.BACKTRACE_FLAG_NAME = 'backtrace';
168+
ArgumentParser.BACKTRACE_FLAG_SHORT_NAME = 'b';
169+
ArgumentParser.DEFAULT_BACKTRACE_FLAG_VALUE = false;
163170
ArgumentParser.FeaturePathExpander = require('./argument_parser/feature_path_expander');
164171
ArgumentParser.PathExpander = require('./argument_parser/path_expander');
165172
ArgumentParser.SupportCodePathExpander = require('./argument_parser/support_code_path_expander');

lib/cucumber/cli/configuration.js

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -90,15 +90,16 @@ function Configuration(argv) {
9090
},
9191

9292
shouldSnippetsBeInCoffeeScript: function shouldSnippetsBeInCoffeeScript() {
93-
var coffeeDisplay = argumentParser.shouldSnippetsBeInCoffeeScript();
94-
return coffeeDisplay;
93+
return argumentParser.shouldSnippetsBeInCoffeeScript();
9594
},
9695

9796
shouldSnippetsBeShown: function shouldSnippetsBeShown() {
98-
var snippetsDisplay = argumentParser.shouldSnippetsBeShown();
99-
return snippetsDisplay;
100-
}
97+
return argumentParser.shouldSnippetsBeShown();
98+
},
10199

100+
shouldFilterStackTraces: function shouldFilterStackTraces() {
101+
return argumentParser.shouldFilterStackTraces();
102+
}
102103
};
103104
return self;
104105
}

lib/cucumber/listener/summary_formatter.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,8 @@ function SummaryFormatter(options) {
110110

111111
self.logFailedStepResult = function logFailedStepResult(stepResult) {
112112
var failureMessage = stepResult.getFailureException();
113-
self.log(failureMessage.stack || failureMessage);
113+
if (failureMessage)
114+
self.log(failureMessage.stack || failureMessage);
114115
self.log('\n\n');
115116
};
116117

lib/cucumber/runtime.js

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,14 @@ function Runtime(configuration) {
1616
var features = self.getFeatures();
1717
var supportCodeLibrary = self.getSupportCodeLibrary();
1818
var astTreeWalker = Runtime.AstTreeWalker(features, supportCodeLibrary, listeners, isStrictRequested);
19-
astTreeWalker.walk(callback);
19+
20+
if (configuration.shouldFilterStackTraces())
21+
Runtime.StackTraceFilter.filter();
22+
23+
astTreeWalker.walk(function (result) {
24+
Runtime.StackTraceFilter.unfilter();
25+
callback(result);
26+
});
2027
},
2128

2229
attachListener: function attachListener(listener) {
@@ -48,5 +55,6 @@ Runtime.FailedStepResult = require('./runtime/failed_step_result');
4855
Runtime.SkippedStepResult = require('./runtime/skipped_step_result');
4956
Runtime.UndefinedStepResult = require('./runtime/undefined_step_result');
5057
Runtime.Attachment = require('./runtime/attachment');
58+
Runtime.StackTraceFilter = require('./runtime/stack_trace_filter');
5159

5260
module.exports = Runtime;

lib/cucumber/runtime/ast_tree_walker.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ function AstTreeWalker(features, supportCodeLibrary, listeners, strictMode) {
2828
visitFeatures: function visitFeatures(features, callback) {
2929
var payload = { features: features };
3030
var event = AstTreeWalker.Event(AstTreeWalker.FEATURES_EVENT_NAME, payload);
31-
self.broadcastEventAroundUserFunction (
31+
self.broadcastEventAroundUserFunction(
3232
event,
3333
function (callback) { features.acceptVisitor(self, callback); },
3434
callback
@@ -38,7 +38,7 @@ function AstTreeWalker(features, supportCodeLibrary, listeners, strictMode) {
3838
visitFeature: function visitFeature(feature, callback) {
3939
var payload = { feature: feature };
4040
var event = AstTreeWalker.Event(AstTreeWalker.FEATURE_EVENT_NAME, payload);
41-
self.broadcastEventAroundUserFunction (
41+
self.broadcastEventAroundUserFunction(
4242
event,
4343
function (callback) { feature.acceptVisitor(self, callback); },
4444
callback
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
var path = require('path');
2+
var chain = require('stack-chain');
3+
4+
var currentFilter = null;
5+
6+
function filter() {
7+
currentFilter = chain.filter.attach(function (error, frames) {
8+
return frames.filter(function (frame) {
9+
var f = frame.getFileName() || '';
10+
var ignoredPath = path.join(__dirname, '..');
11+
return f.indexOf(ignoredPath) === -1;
12+
});
13+
});
14+
}
15+
16+
function unfilter() {
17+
chain.filter.deattach(currentFilter);
18+
}
19+
20+
module.exports = {
21+
filter: filter,
22+
unfilter: unfilter
23+
};

lib/cucumber/volatile_configuration.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ function VolatileConfiguration(features, supportCodeInitializer, options) {
55
options = options || {};
66
var strictMode = !!options.strict;
77
var tagGroupStrings = options.tags || [];
8+
var backtrace = !!options.backtrace;
89

910
var self = {
1011
isStrictMode: function isStrictMode() {
@@ -44,6 +45,10 @@ function VolatileConfiguration(features, supportCodeInitializer, options) {
4445
var tagGroup = tagGroupParser.parse();
4546
var rule = Cucumber.Ast.Filter.AnyOfTagsRule(tagGroup);
4647
return rule;
48+
},
49+
50+
shouldFilterStackTraces: function shouldFilterStackTraces() {
51+
return !backtrace;
4752
}
4853
};
4954
return self;

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,8 +78,10 @@
7878
"coffee-script": "1.8.0",
7979
"cucumber-html": "0.2.3",
8080
"gherkin": "2.12.2",
81+
"hide-stack-frames-from": "^1.0.0",
8182
"nopt": "3.0.1",
8283
"pogo": "0.9.4",
84+
"stack-chain": "^1.3.1",
8385
"underscore": "1.7.0",
8486
"underscore.string": "2.3.3",
8587
"walkdir": "0.0.7"

spec/cucumber/cli/argument_parser_spec.js

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,11 @@ describe("Cucumber.Cli.ArgumentParser", function () {
8888
var knownOptionDefinitions = argumentParser.getKnownOptionDefinitions();
8989
expect(knownOptionDefinitions[Cucumber.Cli.ArgumentParser.SNIPPETS_FLAG_NAME]).toEqual(Boolean);
9090
});
91+
92+
it("defines a --backtrace flag", function () {
93+
var knownOptionDefinitions = argumentParser.getKnownOptionDefinitions();
94+
expect(knownOptionDefinitions[Cucumber.Cli.ArgumentParser.BACKTRACE_FLAG_NAME]).toEqual(Boolean);
95+
});
9196
});
9297

9398
describe("getShortenedOptionDefinitions()", function () {
@@ -135,6 +140,14 @@ describe("Cucumber.Cli.ArgumentParser", function () {
135140
var shortenedOptionDefinitions = argumentParser.getShortenedOptionDefinitions();
136141
expect(shortenedOptionDefinitions[aliasName]).toEqual(aliasValue);
137142
});
143+
144+
it("defines an alias to --backtrace as -b", function () {
145+
var optionName = Cucumber.Cli.ArgumentParser.LONG_OPTION_PREFIX + Cucumber.Cli.ArgumentParser.BACKTRACE_FLAG_NAME;
146+
var aliasName = Cucumber.Cli.ArgumentParser.BACKTRACE_FLAG_SHORT_NAME;
147+
var aliasValue = [optionName];
148+
var shortenedOptionDefinitions = argumentParser.getShortenedOptionDefinitions();
149+
expect(shortenedOptionDefinitions[aliasName]).toEqual(aliasValue);
150+
});
138151
});
139152

140153
describe("getFeatureFilePaths()", function () {
@@ -416,6 +429,27 @@ describe("Cucumber.Cli.ArgumentParser", function () {
416429
});
417430
});
418431

432+
describe("shouldFilterStackTraces()", function () {
433+
beforeEach(function () {
434+
spyOn(argumentParser, 'getOptionOrDefault');
435+
});
436+
437+
it("gets the 'backtrace' flag with a falsy default value", function () {
438+
argumentParser.shouldFilterStackTraces();
439+
expect(argumentParser.getOptionOrDefault).toHaveBeenCalledWith("backtrace", false);
440+
});
441+
442+
it("returns true when the backtrace flag isn't set", function () {
443+
argumentParser.getOptionOrDefault.andReturn(false);
444+
expect(argumentParser.shouldFilterStackTraces()).toBeTruthy();
445+
});
446+
447+
it("returns false when the backtrace flag is set", function () {
448+
argumentParser.getOptionOrDefault.andReturn(true);
449+
expect(argumentParser.shouldFilterStackTraces()).toBeFalsy();
450+
});
451+
});
452+
419453
describe("getOptions() [storeOptions()]", function () {
420454
var options;
421455

spec/cucumber/cli/configuration_spec.js

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -337,4 +337,21 @@ describe("Cucumber.Cli.Configuration", function () {
337337
});
338338
});
339339

340+
describe("shouldFilterStackTraces()", function () {
341+
beforeEach(function () {
342+
spyOnStub(argumentParser, 'shouldFilterStackTraces');
343+
});
344+
345+
it("asks the argument parser whether the stack traces are filtered", function () {
346+
configuration.shouldFilterStackTraces();
347+
expect(argumentParser.shouldFilterStackTraces).toHaveBeenCalled();
348+
});
349+
350+
it("tells whether the stack traces are filtered or not", function () {
351+
var shouldStackTracesBeFiltered = createSpy("filter stack traces?");
352+
argumentParser.shouldFilterStackTraces.andReturn(shouldStackTracesBeFiltered);
353+
expect(configuration.shouldFilterStackTraces()).toBe(shouldStackTracesBeFiltered);
354+
});
355+
});
356+
340357
});

spec/cucumber/runtime_spec.js

Lines changed: 46 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,10 @@ describe("Cucumber.Runtime", function () {
1010
beforeEach(function () {
1111
isStrictRequested = false;
1212
listeners = createSpyWithStubs("listener collection", {add: null});
13-
configuration = createSpyWithStubs("configuration", { isStrictRequested: isStrictRequested });
13+
configuration = createSpyWithStubs("configuration", { isStrictRequested: isStrictRequested, shouldFilterStackTraces: true });
1414
spyOn(Cucumber.Type, 'Collection').andReturn(listeners);
15+
spyOn(Cucumber.Runtime.StackTraceFilter, 'filter');
16+
spyOn(Cucumber.Runtime.StackTraceFilter, 'unfilter');
1517
runtime = Cucumber.Runtime(configuration);
1618
});
1719

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

74+
describe("when stack traces should be filtered", function () {
75+
beforeEach(function () {
76+
configuration.shouldFilterStackTraces.andReturn(true);
77+
});
78+
79+
it("activates the stack trace filter", function () {
80+
runtime.start(callback);
81+
expect(Cucumber.Runtime.StackTraceFilter.filter).toHaveBeenCalled();
82+
});
83+
});
84+
85+
describe("when stack traces should be unfiltered", function () {
86+
beforeEach(function () {
87+
configuration.shouldFilterStackTraces.andReturn(false);
88+
});
89+
90+
it("does not activate the stack trace filter", function () {
91+
runtime.start(callback);
92+
expect(Cucumber.Runtime.StackTraceFilter.filter).not.toHaveBeenCalled();
93+
});
94+
});
95+
7296
it("tells the AST tree walker to walk", function () {
7397
runtime.start(callback);
74-
expect(astTreeWalker.walk).toHaveBeenCalledWith(callback);
98+
expect(astTreeWalker.walk).toHaveBeenCalledWithAFunctionAsNthParameter(1);
99+
});
100+
101+
describe("when the AST tree walker is done walking", function () {
102+
var walkCallback, walkResults;
103+
104+
beforeEach(function () {
105+
runtime.start(callback);
106+
walkCallback = astTreeWalker.walk.mostRecentCall.args[0];
107+
walkResults = createSpy("AST tree walker results");
108+
});
109+
110+
it("deactivates the stack trace filter", function () {
111+
walkCallback(walkResults);
112+
expect(Cucumber.Runtime.StackTraceFilter.unfilter).toHaveBeenCalled();
113+
});
114+
115+
it("calls back", function () {
116+
walkCallback(walkResults);
117+
expect(callback).toHaveBeenCalledWith(walkResults);
118+
});
75119
});
76120
});
77121

spec/cucumber/volatile_configuration_spec.js

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,11 +78,12 @@ describe("Cucumber.VolatileConfiguration", function () {
7878

7979
describe("isStrictMode()",function () {
8080
it("is false if strict option is not specified",function () {
81-
configuration = Cucumber.VolatileConfiguration(featureSources, supportCodeInitializer, {});
81+
configuration = Cucumber.VolatileConfiguration(featureSources, supportCodeInitializer, {});
8282
expect(configuration.isStrictMode()).toEqual(false);
8383
});
84+
8485
it("is true if strict option is set",function () {
85-
configuration = Cucumber.VolatileConfiguration(featureSources, supportCodeInitializer, {strict:true});
86+
configuration = Cucumber.VolatileConfiguration(featureSources, supportCodeInitializer, {strict:true});
8687
expect(configuration.isStrictMode()).toEqual(true);
8788
});
8889
});
@@ -151,4 +152,21 @@ describe("Cucumber.VolatileConfiguration", function () {
151152
expect(returned).toBe(rule);
152153
});
153154
});
155+
156+
describe("shouldFilterStackTraces", function () {
157+
it("returns true by default", function () {
158+
configuration = Cucumber.VolatileConfiguration(featureSources, supportCodeInitializer, {});
159+
expect(configuration.shouldFilterStackTraces()).toBeTruthy();
160+
});
161+
162+
it("returns false when the backtrace option is truthy", function () {
163+
configuration = Cucumber.VolatileConfiguration(featureSources, supportCodeInitializer, { backtrace: true });
164+
expect(configuration.shouldFilterStackTraces()).toBeFalsy();
165+
});
166+
167+
it("returns true when the backtrace option is falsy", function () {
168+
configuration = Cucumber.VolatileConfiguration(featureSources, supportCodeInitializer, { backtrace: false});
169+
expect(configuration.shouldFilterStackTraces()).toBeTruthy();
170+
});
171+
});
154172
});

0 commit comments

Comments
 (0)