UNPKG

9.38 kBJavaScriptView Raw
1"use strict";
2Object.defineProperty(exports, "__esModule", { value: true });
3const q = require("q");
4const webdriver = require("selenium-webdriver");
5const configParser_1 = require("./configParser");
6const logger_1 = require("./logger");
7const ptor_1 = require("./ptor");
8let logger = new logger_1.Logger('plugins');
9var PromiseType;
10(function (PromiseType) {
11 PromiseType[PromiseType["Q"] = 0] = "Q";
12 PromiseType[PromiseType["WEBDRIVER"] = 1] = "WEBDRIVER";
13})(PromiseType = exports.PromiseType || (exports.PromiseType = {}));
14/**
15 * The plugin API for Protractor. Note that this API is unstable. See
16 * plugins/README.md for more information.
17 *
18 * @constructor
19 * @param {Object} config parsed from the config file
20 */
21class Plugins {
22 constructor(config) {
23 /**
24 * @see docs/plugins.md#writing-plugins for information on these functions
25 */
26 this.setup = this.pluginFunFactory('setup', PromiseType.Q);
27 this.onPrepare = this.pluginFunFactory('onPrepare', PromiseType.Q);
28 this.teardown = this.pluginFunFactory('teardown', PromiseType.Q);
29 this.postResults = this.pluginFunFactory('postResults', PromiseType.Q);
30 this.postTest = this.pluginFunFactory('postTest', PromiseType.Q);
31 this.onPageLoad = this.pluginFunFactory('onPageLoad', PromiseType.WEBDRIVER);
32 this.onPageStable = this.pluginFunFactory('onPageStable', PromiseType.WEBDRIVER);
33 this.waitForPromise = this.pluginFunFactory('waitForPromise', PromiseType.WEBDRIVER);
34 this.waitForCondition = this.pluginFunFactory('waitForCondition', PromiseType.WEBDRIVER, true);
35 this.pluginObjs = [];
36 this.assertions = {};
37 this.resultsReported = false;
38 if (config.plugins) {
39 config.plugins.forEach((pluginConf, i) => {
40 let path;
41 if (pluginConf.path) {
42 path = configParser_1.ConfigParser.resolveFilePatterns(pluginConf.path, true, config.configDir)[0];
43 if (!path) {
44 throw new Error('Invalid path to plugin: ' + pluginConf.path);
45 }
46 }
47 else {
48 path = pluginConf.package;
49 }
50 let pluginObj;
51 if (path) {
52 pluginObj = require(path);
53 }
54 else if (pluginConf.inline) {
55 pluginObj = pluginConf.inline;
56 }
57 else {
58 throw new Error('Plugin configuration did not contain a valid path or ' +
59 'inline definition.');
60 }
61 this.annotatePluginObj(pluginObj, pluginConf, i);
62 logger.debug('Plugin "' + pluginObj.name + '" loaded.');
63 this.pluginObjs.push(pluginObj);
64 });
65 }
66 }
67 ;
68 /**
69 * Adds properties to a plugin's object
70 *
71 * @see docs/plugins.md#provided-properties-and-functions
72 */
73 annotatePluginObj(obj, conf, i) {
74 let addAssertion = (info, passed, message) => {
75 if (this.resultsReported) {
76 throw new Error('Cannot add new tests results, since they were already ' +
77 'reported.');
78 }
79 info = info || {};
80 const specName = info.specName || (obj.name + ' Plugin Tests');
81 const assertion = { passed: passed };
82 if (!passed) {
83 assertion.errorMsg = message;
84 if (info.stackTrace) {
85 assertion.stackTrace = info.stackTrace;
86 }
87 }
88 this.assertions[specName] = this.assertions[specName] || [];
89 this.assertions[specName].push(assertion);
90 };
91 obj.name = obj.name || conf.name || conf.path || conf.package || ('Plugin #' + i);
92 obj.config = conf;
93 obj.addFailure = (message, info) => {
94 addAssertion(info, false, message);
95 };
96 obj.addSuccess = (options) => {
97 addAssertion(options, true);
98 };
99 obj.addWarning = (message, options) => {
100 options = options || {};
101 logger.warn('Warning ' +
102 (options.specName ? 'in ' + options.specName : 'from "' + obj.name + '" plugin') + ': ' +
103 message);
104 };
105 }
106 printPluginResults(specResults) {
107 const green = '\x1b[32m';
108 const red = '\x1b[31m';
109 const normalColor = '\x1b[39m';
110 const printResult = (message, pass) => {
111 logger.info(pass ? green : red, '\t', pass ? 'Pass: ' : 'Fail: ', message, normalColor);
112 };
113 for (const specResult of specResults) {
114 const passed = specResult.assertions.map(x => x.passed).reduce((x, y) => (x && y), true);
115 printResult(specResult.description, passed);
116 if (!passed) {
117 for (const assertion of specResult.assertions) {
118 if (!assertion.passed) {
119 logger.error('\t\t' + assertion.errorMsg);
120 if (assertion.stackTrace) {
121 logger.error('\t\t' + assertion.stackTrace.replace(/\n/g, '\n\t\t'));
122 }
123 }
124 }
125 }
126 }
127 }
128 /**
129 * Gets the tests results generated by any plugins
130 *
131 * @see lib/frameworks/README.md#requirements for a complete description of what
132 * the results object must look like
133 *
134 * @return {Object} The results object
135 */
136 getResults() {
137 const results = { failedCount: 0, specResults: [] };
138 for (const specName in this.assertions) {
139 results.specResults.push({ description: specName, assertions: this.assertions[specName] });
140 results.failedCount +=
141 this.assertions[specName].filter(assertion => !assertion.passed).length;
142 }
143 this.printPluginResults(results.specResults);
144 this.resultsReported = true;
145 return results;
146 }
147 ;
148 /**
149 * Returns true if any loaded plugin has skipAngularStability enabled.
150 *
151 * @return {boolean}
152 */
153 skipAngularStability() {
154 const result = this.pluginObjs.some(pluginObj => pluginObj.skipAngularStability);
155 return result;
156 }
157 ;
158 /**
159 * Calls a function from a plugin safely. If the plugin's function throws an
160 * exception or returns a rejected promise, that failure will be logged as a
161 * failed test result instead of crashing protractor. If the tests results have
162 * already been reported, the failure will be logged to the console.
163 *
164 * @param {Object} pluginObj The plugin object containing the function to be run
165 * @param {string} funName The name of the function we want to run
166 * @param {*[]} args The arguments we want to invoke the function with
167 * @param {PromiseType} promiseType The type of promise (WebDriver or Q) that
168 * should be used
169 * @param {boolean} resultsReported If the results have already been reported
170 * @param {*} failReturnVal The value to return if the function fails
171 *
172 * @return {webdriver.promise.Promise|Q.Promise} A promise which resolves to the
173 * function's return value
174 */
175 safeCallPluginFun(pluginObj, funName, args, promiseType, failReturnVal) {
176 const resolver = (done) => {
177 const logError = (e) => {
178 if (this.resultsReported) {
179 this.printPluginResults([{
180 description: pluginObj.name + ' Runtime',
181 assertions: [{
182 passed: false,
183 errorMsg: 'Failure during ' + funName + ': ' + (e.message || e),
184 stackTrace: e.stack
185 }]
186 }]);
187 }
188 else {
189 pluginObj.addFailure('Failure during ' + funName + ': ' + e.message || e, { stackTrace: e.stack });
190 }
191 done(failReturnVal);
192 };
193 try {
194 const result = pluginObj[funName].apply(pluginObj, args);
195 if (webdriver.promise.isPromise(result)) {
196 result.then(done, logError);
197 }
198 else {
199 done(result);
200 }
201 }
202 catch (e) {
203 logError(e);
204 }
205 };
206 if (promiseType == PromiseType.Q) {
207 return q.Promise(resolver);
208 }
209 else if (ptor_1.protractor.browser.controlFlowIsEnabled()) {
210 return new webdriver.promise.Promise(resolver);
211 }
212 else {
213 return new Promise(resolver);
214 }
215 }
216 pluginFunFactory(funName, promiseType, failReturnVal) {
217 return (...args) => {
218 const promises = this.pluginObjs.filter(pluginObj => typeof pluginObj[funName] === 'function')
219 .map(pluginObj => this.safeCallPluginFun(pluginObj, funName, args, promiseType, failReturnVal));
220 return promiseType == PromiseType.Q ? q.all(promises) : webdriver.promise.all(promises);
221 };
222 }
223}
224exports.Plugins = Plugins;
225//# sourceMappingURL=plugins.js.map
\No newline at end of file