API Docs for: 0.9.1
Show:

File: lib/container.js

  1. // Copyright 2014, Yahoo! Inc.
  2. // Copyrights licensed under the Mit License. See the accompanying LICENSE file for terms.
  3.  
  4. var Base = require('preceptor-core').Base;
  5. var utils = require('preceptor-core').utils;
  6. var _ = require('underscore');
  7.  
  8. /**
  9. * This class manages the messages received and is used as data-source for the reporter instances.
  10. *
  11. * @class ReportContainer
  12. * @extends Base
  13. *
  14. * @property {object[]} _sequence Sequence of items in the report
  15. * @property {object} _actions Actions for each item keyed by the id - used for direct access into the tree
  16. * @property {object} _tree Root object of reporting tree - uses references from _actions to form tree
  17. */
  18. var ReportContainer = Base.extend(
  19.  
  20. {
  21. /**
  22. * Called when reporting starts
  23. *
  24. * @method start
  25. */
  26. start: function () {
  27. this._sequence = [];
  28. this._actions = {};
  29. this._tree = {
  30. startTime: +(new Date()),
  31. pending: true,
  32. type: 'root',
  33. name: 'Root',
  34. level: 0,
  35. data: {},
  36. messages: [],
  37. children: [],
  38. output: {}
  39. };
  40. },
  41.  
  42. /**
  43. * Called when reporting stops
  44. *
  45. * @method stop
  46. */
  47. stop: function () {
  48.  
  49. this._tree.endTime = +(new Date());
  50. this._tree.duration = this._tree.endTime - this._tree.startTime;
  51. this._tree.pending = false;
  52.  
  53. _.each(_.keys(this._actions), function (id) {
  54.  
  55. var action = this._actions[id];
  56.  
  57. if (action.pending) {
  58. throw new Error("Reporter action '" + action.type + "' for '" + action.name + "' is pending when end was reached.");
  59. }
  60. }, this);
  61. },
  62.  
  63. /**
  64. * Reporting is completed
  65. *
  66. * @method complete
  67. */
  68. complete: function () {
  69. // Nothing here
  70. },
  71.  
  72. /**
  73. * Called when suite starts
  74. *
  75. * @method suiteStart
  76. * @param {string} id
  77. * @param {string} parentId
  78. * @param {string} suiteName
  79. * @param {object} [options]
  80. * @param {int} [options.startTime]
  81. */
  82. suiteStart: function (id, parentId, suiteName, options) {
  83. var parent;
  84.  
  85. options = options || {};
  86.  
  87. this._actions[id] = {
  88. id: id,
  89. startTime: options.startTime || +(new Date()),
  90. pending: true,
  91. type: 'suite',
  92. name: suiteName,
  93. data: {},
  94. messages: [],
  95. parentId: parentId,
  96. children: [],
  97. output: {}
  98. };
  99.  
  100. parent = this.getAction(parentId);
  101. this._actions[id].level = parent.level + 1;
  102.  
  103. this._sequence.push(this._actions[id]);
  104. parent.children.push(this._actions[id]);
  105. },
  106.  
  107. /**
  108. * Called when suite ends
  109. *
  110. * @method suiteEnd
  111. * @param {string} id
  112. * @param {object} [options]
  113. * @param {int} [options.endTime]
  114. */
  115. suiteEnd: function (id, options) {
  116. var action = this.getAction(id);
  117.  
  118. options = options || {};
  119.  
  120. if (action.type !== 'suite') {
  121. throw new Error("Type of reporter action was expected to be 'suite' but was '" + action.type + "'.")
  122. }
  123. if (!action.pending) {
  124. throw new Error("Reporter action for suite was already closed.");
  125. }
  126.  
  127. action.endTime = options.startTime || +(new Date());
  128. action.duration = action.endTime - action.startTime;
  129. action.pending = false;
  130. },
  131.  
  132.  
  133. /**
  134. * Called when any item has custom data
  135. *
  136. * @method itemData
  137. * @param {string} id
  138. * @param {string} json JSON-data
  139. */
  140. itemData: function (id, json) {
  141. var action = this.getAction(id);
  142.  
  143. action.data = utils.deepExtend(action.data, [JSON.parse(json)]);
  144. },
  145.  
  146. /**
  147. * Called when any item has a custom message
  148. *
  149. * @method itemMessage
  150. * @param {string} id
  151. * @param {string} message
  152. */
  153. itemMessage: function (id, message) {
  154. this.getAction(id).messages.push(message);
  155. },
  156.  
  157.  
  158. /**
  159. * Called when test starts
  160. *
  161. * @method testStart
  162. * @param {string} id
  163. * @param {string} parentId
  164. * @param {string} testName
  165. * @param {object} [options]
  166. * @param {int} [options.startTime]
  167. */
  168. testStart: function (id, parentId, testName, options) {
  169. var parent;
  170.  
  171. options = options || {};
  172.  
  173. this._actions[id] = {
  174. id: id,
  175. startTime: options.startTime || +(new Date()),
  176. pending: true,
  177. type: 'test',
  178. name: testName,
  179. data: {},
  180. messages: [],
  181. parentId: parentId,
  182. output: {}
  183. };
  184.  
  185. parent = this.getAction(parentId);
  186. this._actions[id].level = parent.level + 1;
  187.  
  188. this._sequence.push(this._actions[id]);
  189. parent.children.push(this._actions[id]);
  190. },
  191.  
  192.  
  193. /**
  194. * Called when test fails
  195. *
  196. * @method testFailed
  197. * @param {string} id
  198. * @param {string} [message]
  199. * @param {string} [reason]
  200. * @param {object} [options]
  201. * @param {int} [options.endTime]
  202. */
  203. testFailed: function (id, message, reason, options) {
  204. var action = this._completeTestAction(id, options);
  205.  
  206. action.outcome = 'failed';
  207. action.message = message || 'FAILED';
  208. action.reason = reason || action.message;
  209. },
  210.  
  211. /**
  212. * Called when test has an error
  213. *
  214. * @method testError
  215. * @param {string} id
  216. * @param {string} [message]
  217. * @param {string} [reason]
  218. * @param {object} [options]
  219. */
  220. testError: function (id, message, reason, options) {
  221. var action = this._completeTestAction(id, options);
  222.  
  223. action.outcome = 'error';
  224. action.message = message || 'ERROR';
  225. action.reason = reason || action.message;
  226. },
  227.  
  228. /**
  229. * Called when test has passed
  230. *
  231. * @method testPassed
  232. * @param {string} id
  233. * @param {object} [options]
  234. */
  235. testPassed: function (id, options) {
  236. var action = this._completeTestAction(id, options);
  237.  
  238. action.outcome = 'passed';
  239. },
  240.  
  241. /**
  242. * Called when test is undefined
  243. *
  244. * @method testUndefined
  245. * @param {string} id
  246. * @param {object} [options]
  247. */
  248. testUndefined: function (id, options) {
  249. var action = this._completeTestAction(id, options);
  250.  
  251. action.outcome = 'undefined';
  252. },
  253.  
  254. /**
  255. * Called when test is skipped
  256. *
  257. * @method testSkipped
  258. * @param {string} id
  259. * @param {string} [reason]
  260. * @param {object} [options]
  261. */
  262. testSkipped: function (id, reason, options) {
  263. var action = this._completeTestAction(id, options);
  264.  
  265. action.outcome = 'skipped';
  266. action.reason = reason || 'SKIPPED';
  267. },
  268.  
  269. /**
  270. * Called when test is incomplete
  271. *
  272. * @method testIncomplete
  273. * @param {string} id
  274. * @param {object} [options]
  275. */
  276. testIncomplete: function (id, options) {
  277. var action = this._completeTestAction(id, options);
  278.  
  279. action.outcome = 'incomplete';
  280. },
  281.  
  282.  
  283. /**
  284. * Completes a test
  285. *
  286. * @method _completeTestAction
  287. * @param {string} id
  288. * @param {object} [options]
  289. * @param {int} [options.endTime]
  290. * @return {object} Action
  291. * @private
  292. */
  293. _completeTestAction: function (id, options) {
  294. var action = this.getAction(id);
  295.  
  296. options = options || {};
  297.  
  298. if (action.type !== 'test') {
  299. throw new Error("Type of reporter action was expected to be 'test' but was '" + action.type + "'.")
  300. }
  301. if (!action.pending) {
  302. throw new Error("Reporter action for test was already closed.");
  303. }
  304.  
  305. action.endTime = options.endTime || +(new Date());
  306. action.duration = action.endTime - action.startTime;
  307. action.pending = false;
  308.  
  309. return action;
  310. },
  311.  
  312.  
  313. /**
  314. * Gets action by id
  315. *
  316. * @method getAction
  317. * @param {string} id
  318. * @return {object}
  319. */
  320. getAction: function (id) {
  321. var action;
  322.  
  323. if (id === "undefined") {
  324. id = undefined;
  325. }
  326. if (id === "null") {
  327. id = null;
  328. }
  329.  
  330. if ((id === undefined) || (id === null)) {
  331. action = this.getTree();
  332. } else {
  333. action = this._actions[id];
  334.  
  335. if (!action) {
  336. throw new Error("Id for reporter action doesn't exist " + id + ".");
  337. }
  338. }
  339.  
  340. return action;
  341. },
  342.  
  343. /**
  344. * Gets the sequence of actions by the sequential list of ids
  345. *
  346. * @method getSequence
  347. * @return {string[]}
  348. */
  349. getSequence: function () {
  350. return this._sequence;
  351. },
  352.  
  353. /**
  354. * Gets the action tree
  355. *
  356. * @method getTree
  357. * @return {object}
  358. */
  359. getTree: function () {
  360. return this._tree;
  361. },
  362.  
  363.  
  364. /**
  365. * Gathers all test outcomes for a node
  366. *
  367. * @method gatherTestOutcomes
  368. * @param {object} treeNode
  369. * @return {object} Of `{tests: int, failed: int, disabled: int, error: int}`
  370. */
  371. gatherTestOutcomes: function (treeNode) {
  372. var result = {
  373. tests: 0,
  374. failed: 0,
  375. incomplete: 0,
  376. skipped: 0,
  377. error: 0,
  378. passed: 0,
  379. undef: 0
  380. };
  381.  
  382. this._countOutcomes(treeNode, result);
  383.  
  384. return result;
  385. },
  386.  
  387. /**
  388. * Counts a specific outcome downwards from the current tree-point
  389. *
  390. * @method _countOutcomes
  391. * @param {object} treeNode
  392. * @param {object} sumObj
  393. * @private
  394. */
  395. _countOutcomes: function (treeNode, sumObj) {
  396. if (treeNode.type === 'test') {
  397. sumObj.tests++;
  398.  
  399. if (treeNode.outcome === 'undefined') {
  400. sumObj['undef']++;
  401. } else {
  402. sumObj[treeNode.outcome]++;
  403. }
  404. } else {
  405. _.each(treeNode.children || [], function (node) {
  406. this._countOutcomes(node, sumObj);
  407. }, this);
  408. }
  409. },
  410.  
  411. /**
  412. * Gets the full name of an object
  413. *
  414. * @method getFullName
  415. * @param {string} id
  416. * @return {string}
  417. */
  418. getFullName: function (id) {
  419. var action = this.getAction(id),
  420. parentId = action.parentId,
  421. name = action.name;
  422.  
  423. if (parentId !== undefined) {
  424. return [this.getFullName(parentId), name].join(" ");
  425. } else {
  426. return name;
  427. }
  428. }
  429. },
  430.  
  431. {
  432. /**
  433. * @property TYPE
  434. * @type {string}
  435. * @static
  436. */
  437. TYPE: 'ReportContainer'
  438. });
  439.  
  440. module.exports = ReportContainer;
  441.