Skip to content
This repository was archived by the owner on Jul 29, 2024. It is now read-only.

Commit 55a91ea

Browse files
committed
feat(launcher): reorganize launcher + add option to store test results as JSON
1 parent 7240360 commit 55a91ea

12 files changed

+517
-391
lines changed

docs/referenceConf.js

+4
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,10 @@ exports.config = {
212212
}
213213
},
214214

215+
// If set, protractor will save the test output in json format at this path.
216+
// The path is relative to the location of this config.
217+
resultJsonOutputFile: null,
218+
215219
// ---------------------------------------------------------------------------
216220
// ----- The test framework --------------------------------------------------
217221
// ---------------------------------------------------------------------------

lib/cli.js

+1
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ var optimist = require('optimist').
5050
describe('stackTrace', 'Print stack trace on error').
5151
describe('params', 'Param object to be passed to the tests').
5252
describe('framework', 'Test framework to use: jasmine, cucumber or mocha').
53+
describe('resultJsonOutputFile', 'Path to save JSON test result').
5354
alias('browser', 'capabilities.browserName').
5455
alias('name', 'capabilities.name').
5556
alias('platform', 'capabilities.platform').

lib/frameworks/README.md

+13-2
Original file line numberDiff line numberDiff line change
@@ -23,5 +23,16 @@ Requirements
2323

2424
- `runner.getConfig().onComplete` must be called when tests are finished.
2525

26-
- The returned promise must be resolved when tests are finished and it should return a results object.
27-
This object must have a `failedCount` property.
26+
- The returned promise must be resolved when tests are finished and it should return a results object. This object must have a `failedCount` property and optionally a `specResults`
27+
object of the following structure:
28+
```
29+
specResults = [{
30+
description: string,
31+
assertions: [{
32+
passed: boolean,
33+
errorMsg: string,
34+
stackTrace: string
35+
}],
36+
duration: integer
37+
}]
38+
```

lib/frameworks/cucumber.js

+55-7
Original file line numberDiff line numberDiff line change
@@ -58,17 +58,65 @@ exports.run = function(runner, specs) {
5858
}
5959
global.cucumber = Cucumber.Cli(execOptions);
6060

61-
return runner.runTestPreparer().then(function () {
62-
return q.promise(function (resolve, reject) {
63-
global.cucumber.run(function (succeeded) {
61+
var testResult = [];
62+
var failedCount = 0;
63+
// Add a listener into cucumber so that protractor can find out which
64+
// steps passed/failed
65+
var addResultListener = function(formatter) {
66+
var originalHandleStepResultEvent = formatter.handleStepResultEvent;
67+
formatter.handleStepResultEvent = function(event, callback) {
68+
69+
var stepResult = event.getPayloadItem('stepResult');
70+
if (stepResult.isSuccessful()) {
71+
runner.emit('testPass');
72+
testResult.push({
73+
description: stepResult.getStep().getName(),
74+
assertions: [{
75+
passed: true
76+
}],
77+
duration: stepResult.getDuration()
78+
});
79+
}
80+
else if (stepResult.isFailed()) {
81+
runner.emit('testFail');
82+
++failedCount;
83+
var failureMessage = stepResult.getFailureException();
84+
testResult.push({
85+
description: stepResult.getStep().getName(),
86+
assertions: [{
87+
passed: false,
88+
errorMsg: failureMessage.message,
89+
stackTrace: failureMessage.stack
90+
}],
91+
duration: stepResult.getDuration()
92+
});
93+
}
94+
95+
if (originalHandleStepResultEvent
96+
&& typeof(originalHandleStepResultEvent) === 'function') {
97+
originalHandleStepResultEvent(event, callback);
98+
} else {
99+
callback();
100+
}
101+
}
102+
};
103+
104+
return runner.runTestPreparer().then(function() {
105+
return q.promise(function(resolve, reject) {
106+
var cucumberConf = Cucumber.Cli.Configuration(execOptions);
107+
var runtime = Cucumber.Runtime(cucumberConf);
108+
var formatter = cucumberConf.getFormatter();
109+
addResultListener(formatter);
110+
runtime.attachListener(formatter);
111+
runtime.start(function(succeeded) {
64112
try {
65113
if (runner.getConfig().onComplete) {
66114
runner.getConfig().onComplete();
67115
}
68-
var resolvedObj = {
69-
failedCount: succeeded ? 0 : 1
70-
};
71-
resolve(resolvedObj);
116+
resolve({
117+
failedCount: failedCount,
118+
specResults: testResult
119+
});
72120
} catch (err) {
73121
reject(err);
74122
}

lib/frameworks/jasmine.js

+23-2
Original file line numberDiff line numberDiff line change
@@ -13,20 +13,38 @@ exports.run = function(runner, specs) {
1313
require('jasminewd');
1414
/* global jasmine */
1515

16+
var testResult = [];
17+
1618
var RunnerReporter = function(emitter) {
1719
this.emitter = emitter;
1820
};
1921

2022
RunnerReporter.prototype.reportRunnerStarting = function() {};
2123
RunnerReporter.prototype.reportRunnerResults = function() {};
2224
RunnerReporter.prototype.reportSuiteResults = function() {};
23-
RunnerReporter.prototype.reportSpecStarting = function() {};
25+
RunnerReporter.prototype.reportSpecStarting = function() {
26+
this.startTime = new Date();
27+
};
2428
RunnerReporter.prototype.reportSpecResults = function(spec) {
2529
if (spec.results().passed()) {
2630
this.emitter.emit('testPass');
2731
} else {
2832
this.emitter.emit('testFail');
2933
}
34+
35+
var entry = {
36+
description: spec.results().description,
37+
assertions: [],
38+
duration: new Date().getTime() - this.startTime.getTime()
39+
};
40+
spec.results().getItems().forEach(function(item) {
41+
entry.assertions.push({
42+
passed: item.passed(),
43+
errorMsg: item.passed() ? undefined : item.message,
44+
stackTrace: item.passed() ? undefined : item.trace.stack
45+
});
46+
});
47+
testResult.push(entry);
3048
};
3149
RunnerReporter.prototype.log = function() {};
3250

@@ -47,7 +65,10 @@ exports.run = function(runner, specs) {
4765
if (originalOnComplete) {
4866
originalOnComplete(jasmineRunner, log);
4967
}
50-
resolve(jasmineRunner.results());
68+
resolve({
69+
failedCount: jasmineRunner.results().failedCount,
70+
specResults: testResult
71+
});
5172
} catch(err) {
5273
reject(err);
5374
}

lib/frameworks/mocha.js

+34-17
Original file line numberDiff line numberDiff line change
@@ -35,19 +35,11 @@ exports.run = function(runner, specs) {
3535
global.it.only = global.iit = originalOnly;
3636

3737
global.it.skip = global.xit = mochaAdapters.xit;
38-
}catch(err){
38+
} catch(err) {
3939
deferred.reject(err);
4040
}
4141
});
4242

43-
mocha.suite.on('pass', function() {
44-
runner.emit('testPass');
45-
});
46-
47-
mocha.suite.on('fail', function() {
48-
runner.emit('testFail');
49-
});
50-
5143
mocha.loadFiles();
5244

5345
runner.runTestPreparer().then(function() {
@@ -56,21 +48,46 @@ exports.run = function(runner, specs) {
5648
mocha.addFile(file);
5749
});
5850

59-
mocha.run(function(failures) {
51+
var testResult = [];
52+
53+
var mochaRunner = mocha.run(function(failures) {
6054
try {
6155
if (runner.getConfig().onComplete) {
6256
runner.getConfig().onComplete();
6357
}
64-
var resolvedObj = {
65-
failedCount: failures
66-
};
67-
68-
deferred.resolve(resolvedObj);
69-
}catch(err){
58+
deferred.resolve({
59+
failedCount: failures,
60+
specResults: testResult
61+
});
62+
} catch(err) {
7063
deferred.reject(err);
7164
}
7265
});
73-
}).catch(function(reason){
66+
67+
mochaRunner.on('pass', function(test) {
68+
runner.emit('testPass');
69+
testResult.push({
70+
description: test.title,
71+
assertions: [{
72+
passed: true
73+
}],
74+
duration: test.duration
75+
});
76+
});
77+
78+
mochaRunner.on('fail', function(test) {
79+
runner.emit('testFail');
80+
testResult.push({
81+
description: test.title,
82+
assertions: [{
83+
passed: false,
84+
errorMsg: test.err.message,
85+
stackTrace: test.err.stack
86+
}],
87+
duration: test.duration
88+
});
89+
});
90+
}).catch(function(reason) {
7491
deferred.reject(reason);
7592
});
7693

0 commit comments

Comments
 (0)