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

Commit 658902b

Browse files
committed
feat(plugins): add postTest hook for plugins
Additionally, add some tests to make sure that plugins can fail properly. Closes #1842
1 parent 7cc19cf commit 658902b

11 files changed

+176
-18
lines changed

lib/plugins.js

+16-7
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,9 @@ var log_ = function() {
1515
};
1616

1717
/**
18-
* The plugin API for Protractor. Note that this API is extremely unstable
19-
* and current consists of only two functions:
20-
* <plugin>.setup - called before tests
21-
* <plugin>.teardown - called after tests
22-
* <plugin>.postResults - called after test results have been processed
23-
* More information on plugins coming in the future
18+
* The plugin API for Protractor. Note that this API is unstable. See
19+
* plugins/README.md for more information.
20+
*
2421
* @constructor
2522
* @param {Object} config parsed from the config file
2623
*/
@@ -58,8 +55,12 @@ function pluginFunFactory(funName) {
5855
var pluginConf = this.pluginConfs[name];
5956
var pluginObj = this.pluginObjs[name];
6057
names.push(name);
61-
promises.push((pluginObj[funName] || noop)(pluginConf));
58+
promises.push(
59+
(pluginObj[funName] || noop).apply(
60+
pluginObj[funName],
61+
[pluginConf].concat([].slice.call(arguments))));
6262
}
63+
6364
return q.all(promises).then(function(results) {
6465
// Join the results into a single object and output any test results
6566
var ret = {failedCount: 0};
@@ -131,4 +132,12 @@ Plugins.prototype.teardown = pluginFunFactory('teardown');
131132
*/
132133
Plugins.prototype.postResults = pluginFunFactory('postResults');
133134

135+
/**
136+
* Called after each test block completes.
137+
*
138+
* @return {q.Promise} A promise which resolves when the plugins have all been
139+
* torn down.
140+
*/
141+
Plugins.prototype.postTest = pluginFunFactory('postTest');
142+
134143
module.exports = Plugins;

lib/runner.js

+19-1
Original file line numberDiff line numberDiff line change
@@ -311,9 +311,27 @@ Runner.prototype.run = function() {
311311
self.on('testFail', restartDriver);
312312
}
313313

314+
// We need to save these promises to make sure they're run, but we don't
315+
// want to delay starting the next test (because we can't, it's just
316+
// an event emitter).
317+
var pluginPostTestPromises = [];
318+
319+
self.on('testPass', function() {
320+
pluginPostTestPromises.push(plugins.postTest(true));
321+
});
322+
self.on('testFail', function() {
323+
pluginPostTestPromises.push(plugins.postTest(false));
324+
});
325+
314326
return require(frameworkPath).run(self, self.config_.specs).
315327
then(function(testResults) {
316-
return helper.joinTestLogs(pluginSetupResults, testResults);
328+
return q.all(pluginPostTestPromises).then(function(postTestResultList) {
329+
var results = helper.joinTestLogs(pluginSetupResults, testResults);
330+
postTestResultList.forEach(function(postTestResult) {
331+
results = helper.joinTestLogs(results, postTestResult);
332+
});
333+
return results;
334+
});
317335
});
318336
// 5) Teardown plugins
319337
}).then(function(testResults) {

plugins/README.md

+16
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,10 @@ exports.config = {
3636
Writing Plugins
3737
---------------
3838

39+
Plugins are designed to work with any test framework (Jasmine, Mocha, etc),
40+
so they use generic hooks which Protractor provides. Plugins may change
41+
the output of Protractor by returning a results object.
42+
3943
Plugins are node modules which export an object with the following API:
4044

4145
```js
@@ -72,6 +76,18 @@ exports.teardown = function(config) {};
7276
* @return Return values are ignored.
7377
*/
7478
exports.postResults = function(config) {};
79+
80+
/**
81+
* Called after each test block (in Jasmine, this means an `it` block)
82+
* completes.
83+
*
84+
* @param {Object} config The plugin configuration object.
85+
* @param {boolean} passed True if the test passed.
86+
*
87+
* @return Object If an object is returned, it is merged with the Protractor
88+
* result object. May return a promise.
89+
*/
90+
exports.postTest = function(config, passed) {};
7591
```
7692

7793
The protractor results object follows the format specified in

scripts/test.js

+10
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,16 @@ executor.addCommandlineTest('node lib/cli.js spec/errorTest/mochaFailureConf.js'
9797
stacktrace: 'mocha_failure_spec.js:11:20'
9898
}]);
9999

100+
executor.addCommandlineTest('node lib/cli.js spec/errorTest/pluginsFailingConf.js')
101+
.expectExitCode(1)
102+
.expectErrors([
103+
{message: 'Expected true to be false'},
104+
{message: 'from setup'},
105+
{message: 'from postTest passing'},
106+
{message: 'from postTest failing'},
107+
{message: 'from teardown'}
108+
]);
109+
100110
// Check ngHint plugin
101111

102112
executor.addCommandlineTest(

spec/errorTest/pluginsFailingConf.js

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
var env = require('../environment.js');
2+
3+
// A small suite to make sure the full functionality of plugins work
4+
exports.config = {
5+
// seleniumAddress: env.seleniumAddress,
6+
mockSelenium: true,
7+
8+
framework: 'jasmine2',
9+
10+
// Spec patterns are relative to this directory.
11+
specs: [
12+
'../plugins/fail_spec.js'
13+
],
14+
15+
capabilities: env.capabilities,
16+
17+
baseUrl: env.baseUrl,
18+
19+
jasmineNodeOpts: {
20+
isVerbose: true,
21+
realtimeFailure: true
22+
},
23+
24+
// Plugin patterns are relative to this directory.
25+
plugins: [{
26+
path: '../plugins/basic_plugin.js'
27+
}, {
28+
path: '../plugins/failing_plugin.js'
29+
}]
30+
};

spec/plugins/basic_spec.js

+4
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,8 @@ describe('check if plugin setup ran', function() {
22
it('should have set protractor.__BASIC_PLUGIN_RAN', function() {
33
expect(protractor.__BASIC_PLUGIN_RAN).toBe(true);
44
});
5+
6+
it('should run multiple tests', function() {
7+
expect(true).toBe(true);
8+
});
59
});

spec/plugins/fail_spec.js

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
describe('check if plugin setup ran', function() {
2+
it('should have set protractor.__BASIC_PLUGIN_RAN', function() {
3+
expect(protractor.__BASIC_PLUGIN_RAN).toBe(true);
4+
});
5+
6+
it('should run multiple tests which fail', function() {
7+
expect(true).toBe(false);
8+
});
9+
});

spec/plugins/failing_plugin.js

+39
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
var q = require('q');
2+
3+
var failingResult = function(message) {
4+
return {
5+
failedCount: 1,
6+
specResults: [{
7+
description: 'plugin test which fails',
8+
assertions: [{
9+
passed: false,
10+
errorMsg: message,
11+
}],
12+
duration: 4
13+
}]
14+
};
15+
};
16+
17+
module.exports = {
18+
setup: function() {
19+
return q.delay(100).then(function() {
20+
return failingResult('from setup');
21+
});
22+
},
23+
24+
teardown: function() {
25+
return q.delay(100).then(function() {
26+
return failingResult('from teardown');
27+
});
28+
},
29+
30+
postResults: function() {
31+
// This function should cause no failures.
32+
},
33+
34+
postTest: function(config, passed) {
35+
return q.delay(100).then(function() {
36+
return failingResult('from postTest ' + (passed ? 'passing' : 'failing'));
37+
});
38+
}
39+
};

spec/plugins/test_plugin.js

+30-8
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,34 @@
1+
var q = require('q');
2+
3+
var passingResult = {
4+
failedCount: 0,
5+
specResults: [{
6+
description: 'plugin test which passes',
7+
assertions: [],
8+
duration: 4
9+
}]
10+
};
11+
112
module.exports = {
13+
setup: function() {
14+
return q.delay(100).then(function() {
15+
return passingResult;
16+
});
17+
},
18+
219
teardown: function() {
3-
return {
4-
failedCount: 0,
5-
specResults: [{
6-
description: 'This succeeds',
7-
assertions: [],
8-
duration: 1
9-
}]
10-
};
20+
return q.delay(100).then(function() {
21+
return passingResult;
22+
});
23+
},
24+
25+
postResults: function() {
26+
// This function should cause no failures.
27+
},
28+
29+
postTest: function() {
30+
return q.delay(100).then(function() {
31+
return passingResult;
32+
});
1133
}
1234
};

spec/pluginsBasicConf.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ var env = require('./environment.js');
33
// A small suite to make sure the basic functionality of plugins work
44
// Tests the (potential) edge case of exactly one plugin being used
55
exports.config = {
6-
seleniumAddress: env.seleniumAddress,
6+
mockSelenium: true,
77

88
framework: 'jasmine2',
99

spec/pluginsFullConf.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@ var env = require('./environment.js');
22

33
// A small suite to make sure the full functionality of plugins work
44
exports.config = {
5-
seleniumAddress: env.seleniumAddress,
5+
// seleniumAddress: env.seleniumAddress,
6+
mockSelenium: true,
67

78
framework: 'jasmine2',
89

0 commit comments

Comments
 (0)