plugins.js 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", { value: true });
  3. const q = require("q");
  4. const webdriver = require("selenium-webdriver");
  5. const configParser_1 = require("./configParser");
  6. const logger_1 = require("./logger");
  7. const ptor_1 = require("./ptor");
  8. let logger = new logger_1.Logger('plugins');
  9. var 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. */
  21. class 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. }
  224. exports.Plugins = Plugins;
  225. //# sourceMappingURL=plugins.js.map