API Docs for: 0.0.4
Show:

File: index.js

  1. /*!
  2. *
  3. * Copyright (c) 2013 Sebastian Golasch
  4. *
  5. * Permission is hereby granted, free of charge, to any person obtaining a
  6. * copy of this software and associated documentation files (the "Software"),
  7. * to deal in the Software without restriction, including without limitation
  8. * the rights to use, copy, modify, merge, publish, distribute, sublicense,
  9. * and/or sell copies of the Software, and to permit persons to whom the
  10. * Software is furnished to do so, subject to the following conditions:
  11. *
  12. * The above copyright notice and this permission notice shall be included
  13. * in all copies or substantial portions of the Software.
  14. *
  15. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
  16. * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  17. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
  18. * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  19. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  20. * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  21. * DEALINGS IN THE SOFTWARE.
  22. *
  23. */
  24.  
  25. 'use strict';
  26.  
  27. // ext. libs
  28. var Handlebars = require('handlebars');
  29. var stylus = require('stylus');
  30. var fs = require('fs');
  31.  
  32. // int. globals
  33. var reporter = null;
  34.  
  35. /**
  36. * The HTML reporter can produce a set of HTML files with the results of your testrun.
  37. *
  38. * The reporter can be installed with the following command:
  39. *
  40. * ```bash
  41. * $ npm install dalek-reporter-html --save-dev
  42. * ```
  43. *
  44. * By default the files will be written to the `report/dalek/` folder,
  45. * you can change this by adding a config option to the your Dalekfile
  46. *
  47. * ```javascript
  48. * "html-reporter": {
  49. * "dest": "your/folder"
  50. * }
  51. * ```
  52. *
  53. * If you would like to use the reporter (in addition to the std. console reporter),
  54. * you can start dalek with a special command line argument
  55. *
  56. * ```bash
  57. * $ dalek your_test.js -r console,html
  58. * ```
  59. *
  60. * or you can add it to your Dalekfile
  61. *
  62. * ```javascript
  63. * "reporter": ["console", "html"]
  64. * ```
  65. *
  66. * @class Reporter
  67. * @constructor
  68. * @part html
  69. * @api
  70. */
  71.  
  72. function Reporter (opts) {
  73. this.events = opts.events;
  74. this.config = opts.config;
  75. this.temporaryAssertions = [];
  76. this.temp = {};
  77.  
  78. var defaultReportFolder = 'report/dalek';
  79. this.dest = this.config.get('html-reporter') && this.config.get('html-reporter').dest ? this.config.get('html-reporter').dest : defaultReportFolder;
  80.  
  81. this.loadTemplates();
  82. this.initOutputHandlers();
  83. this.startListening();
  84. }
  85.  
  86. /**
  87. * @module Reporter
  88. */
  89.  
  90. module.exports = function (opts) {
  91. if (reporter === null) {
  92. reporter = new Reporter(opts);
  93. }
  94.  
  95. return reporter;
  96. };
  97.  
  98. Reporter.prototype = {
  99.  
  100. /**
  101. * Inits the html buffer objects
  102. *
  103. * @method initOutputHandlers
  104. * @chainable
  105. */
  106.  
  107. initOutputHandlers: function () {
  108. this.output = {};
  109. this.output.test = {};
  110. return this;
  111. },
  112.  
  113. /**
  114. * Loads and prepares all the templates for
  115. * CSS, JS & HTML
  116. *
  117. * @method loadTemplates
  118. * @chainable
  119. */
  120.  
  121. loadTemplates: function () {
  122. // render stylesheets
  123. var precss = fs.readFileSync(__dirname + '/themes/default/styl/default.styl', 'utf8');
  124. stylus.render(precss, { filename: 'default.css' }, function(err, css){
  125. if (err) {
  126. throw err;
  127. }
  128.  
  129. this.styles = css;
  130. }.bind(this));
  131.  
  132. // collect client js (to be inined later)
  133. this.js = fs.readFileSync(__dirname + '/themes/default/js/default.js', 'utf8');
  134.  
  135. // register handlebars helpers
  136. Handlebars.registerHelper('roundNumber', function (number) {
  137. return Math.round(number * Math.pow(10, 2)) / Math.pow(10, 2);
  138. });
  139.  
  140. // collect & compile templates
  141. this.templates = {};
  142. this.templates.test = Handlebars.compile(fs.readFileSync(__dirname + '/themes/default/hbs/test.hbs', 'utf8'));
  143. this.templates.wrapper = Handlebars.compile(fs.readFileSync(__dirname + '/themes/default/hbs/wrapper.hbs', 'utf8'));
  144. this.templates.testresult = Handlebars.compile(fs.readFileSync(__dirname + '/themes/default/hbs/tests.hbs', 'utf8'));
  145. this.templates.banner = Handlebars.compile(fs.readFileSync(__dirname + '/themes/default/hbs/banner.hbs', 'utf8'));
  146. this.templates.detail = Handlebars.compile(fs.readFileSync(__dirname + '/themes/default/hbs/detail.hbs', 'utf8'));
  147.  
  148. return this;
  149. },
  150.  
  151. /**
  152. * Connects to all the event listeners
  153. *
  154. * @method startListening
  155. * @chainable
  156. */
  157.  
  158. startListening: function () {
  159. // index page
  160. this.events.on('report:assertion', this.outputAssertionResult.bind(this));
  161. this.events.on('report:test:finished', this.outputTestFinished.bind(this));
  162. this.events.on('report:runner:finished', this.outputRunnerFinished.bind(this));
  163. this.events.on('report:run:browser', this.outputRunBrowser.bind(this));
  164.  
  165. // detail page
  166. this.events.on('report:test:started', this.startDetailPage.bind(this));
  167. this.events.on('report:action', this.addActionToDetailPage.bind(this));
  168. this.events.on('report:assertion', this.addAssertionToDetailPage.bind(this));
  169. this.events.on('report:test:finished', this.finishDetailPage.bind(this));
  170.  
  171. return this;
  172. },
  173.  
  174. /**
  175. * Prepares the output for a test detail page
  176. *
  177. * @method startDetailPage
  178. * @chainable
  179. */
  180.  
  181. startDetailPage: function () {
  182. this.detailContents = {};
  183. this.detailContents.eventLog = [];
  184. return this;
  185. },
  186.  
  187. /**
  188. * Adds an action output to the detail page
  189. *
  190. * @method addActionToDetailPage
  191. * @param {object} data Event data
  192. * @chainable
  193. */
  194.  
  195. addActionToDetailPage: function (data) {
  196. data.isAction = true;
  197. this.detailContents.eventLog.push(data);
  198. return this;
  199. },
  200.  
  201. /**
  202. * Adds an assertion result to the detail page
  203. *
  204. * @method addAssertionToDetailPage
  205. * @param {object} data Event data
  206. * @chainable
  207. */
  208.  
  209. addAssertionToDetailPage: function (data) {
  210. data.isAssertion = true;
  211. this.detailContents.eventLog.push(data);
  212. return this;
  213. },
  214.  
  215. /**
  216. * Writes a detail page to the file system
  217. *
  218. * @method finishDetailPage
  219. * @param {object} data Event data
  220. * @chainable
  221. */
  222.  
  223. finishDetailPage: function (data) {
  224. this.detailContents.testResult = data;
  225. this.detailContents.styles = this.styles;
  226. this.detailContents.js = this.js;
  227. fs.writeFileSync(this.dest + '/details/' + data.id + '.html', this.templates.detail(this.detailContents), 'utf8');
  228. return this;
  229. },
  230.  
  231. /**
  232. * Stores the current browser name
  233. *
  234. * @method outputRunBrowser
  235. * @param {string} browser Browser name
  236. * @chainable
  237. */
  238.  
  239. outputRunBrowser: function (browser) {
  240. this.temp.browser = browser;
  241. return this;
  242. },
  243.  
  244. /**
  245. * Writes the index page to the filesystem
  246. *
  247. * @method outputRunnerFinished
  248. * @param {object} data Event data
  249. * @chainable
  250. */
  251.  
  252. outputRunnerFinished: function (data) {
  253. var body = '';
  254. var contents = '';
  255. var tests = '';
  256. var banner = '';
  257.  
  258. // add test results
  259. var keys = Object.keys(this.output.test);
  260. keys.forEach(function (key) {
  261. tests += this.output.test[key];
  262. }.bind(this));
  263.  
  264. // compile the test result template
  265. body = this.templates.testresult({result: data, tests: tests});
  266.  
  267. // compile the banner
  268. banner = this.templates.banner({status: data.status});
  269.  
  270. // compile the contents within the wrapper template
  271. contents = this.templates.wrapper({styles: this.styles, js: this.js, banner: banner, body: body});
  272.  
  273. // save the main test output file
  274. this.events.emit('report:written', {type: 'html', dest: this.dest});
  275. this._recursiveMakeDirSync(this.dest + '/details');
  276. fs.writeFileSync(this.dest + '/index.html', contents, 'utf8');
  277. return this;
  278. },
  279.  
  280. /**
  281. * Pushes an assertion result to the index output queue
  282. *
  283. * @method outputAssertionResult
  284. * @param {object} data Event data
  285. * @chainable
  286. */
  287.  
  288. outputAssertionResult: function (data) {
  289. this.temporaryAssertions.push(data);
  290. return this;
  291. },
  292.  
  293. /**
  294. * Pushes an test result to the index output queue
  295. *
  296. * @method outputTestFinished
  297. * @param {object} data Event data
  298. * @chainable
  299. */
  300.  
  301. outputTestFinished: function (data) {
  302. data.assertionInfo = this.temporaryAssertions;
  303. data.browser = this.temp.browser;
  304. this.output.test[data.id] = this.templates.test(data);
  305. this.temporaryAssertions = [];
  306. return this;
  307. },
  308.  
  309. /**
  310. * Helper method to generate deeper nested directory structures
  311. *
  312. * @method _recursiveMakeDirSync
  313. * @param {string} path PAth to create
  314. */
  315.  
  316. _recursiveMakeDirSync: function (path) {
  317. var pathSep = require('path').sep;
  318. var dirs = path.split(pathSep);
  319. var root = '';
  320.  
  321. while (dirs.length > 0) {
  322. var dir = dirs.shift();
  323. if (dir === '') {
  324. root = pathSep;
  325. }
  326. if (!fs.existsSync(root + dir)) {
  327. fs.mkdirSync(root + dir);
  328. }
  329. root += dir + pathSep;
  330. }
  331. }
  332. };
  333.