From 8ac5b267c341cc7282d26edeb63dc95d3c412b29 Mon Sep 17 00:00:00 2001 From: Adam Gruber Date: Fri, 24 Mar 2017 16:34:49 -0400 Subject: [PATCH 1/2] Feature/windows auto open (#19) * add/fix jsdoc comments * wrap file opening in a promise to ensure files are opened before report creation finishes * add autoOpen as a cli option * fix broken test --- .eslintrc | 3 +- bin/src/cli-main.js | 10 +++- bin/src/cli.js | 6 +++ lib/src/main.js | 85 +++++++++++++++++++++------------- package.json | 2 +- test/spec/cli/cli-main.test.js | 1 + test/spec/lib/main.test.js | 16 +++++-- 7 files changed, 84 insertions(+), 39 deletions(-) diff --git a/.eslintrc b/.eslintrc index 160a64e4..161537e8 100644 --- a/.eslintrc +++ b/.eslintrc @@ -62,6 +62,7 @@ "semi": [1, "always"], "global-require": 0, "import/no-unresolved": 0, - "import/no-extraneous-dependencies": 0 + "import/no-extraneous-dependencies": 0, + "valid-jsdoc": ["error", { "requireReturn": false }] } } diff --git a/bin/src/cli-main.js b/bin/src/cli-main.js index ba982fdc..8538ea03 100644 --- a/bin/src/cli-main.js +++ b/bin/src/cli-main.js @@ -18,6 +18,9 @@ const ERRORS = { /** * Validate the data file * + * @param {string} dataInFile Filename of test data + * + * @return {Object} JSON test data if valid, otherwise error object { err: message } */ function validateInFile(dataInFile) { let dataIn; @@ -52,10 +55,13 @@ function validateInFile(dataInFile) { /** * Get options to send to report generator * + * @param {Object} args Arguments passed in + * + * @return {Object} Options to pass to report generator */ function getOptions(args) { const { reportFilename, reportDir, reportTitle, reportPageTitle, - inlineAssets, enableCharts, enableCode, dev } = args; + inlineAssets, enableCharts, enableCode, autoOpen, dev } = args; const filename = `${reportFilename.replace(fileExtRegex, '')}.html`; return { reportHtmlFile: path.join(reportDir, filename), @@ -64,6 +70,7 @@ function getOptions(args) { inlineAssets, enableCharts, enableCode, + autoOpen, dev }; } @@ -71,6 +78,7 @@ function getOptions(args) { /** * Main CLI Program * + * @param {Object} processArgs CLI arguments */ function mareport(processArgs) { const args = processArgs || { _: [] }; diff --git a/bin/src/cli.js b/bin/src/cli.js index 9a63f326..b49750a8 100755 --- a/bin/src/cli.js +++ b/bin/src/cli.js @@ -15,6 +15,7 @@ const mareport = require('./cli-main'); * @property {boolean} inlineAssets Should assets be inlined into HTML file (default: false) * @property {boolean} charts Should charts be enabled (default: true) * @property {boolean} code Should test code output be enabled (default: true) + * @property {boolean} autoOpen Open the report after creation (default: false) * @property {boolean} dev Enable dev mode in the report, * asssets loaded via webpack (default: false) */ @@ -70,6 +71,11 @@ yargs describe: 'Display test code', boolean: true }, + autoOpen: { + default: false, + describe: 'Automatically open the report HTML', + boolean: true + }, dev: { default: false, describe: 'Enable dev mode', diff --git a/lib/src/main.js b/lib/src/main.js index 372e420d..a346e8b5 100644 --- a/lib/src/main.js +++ b/lib/src/main.js @@ -11,31 +11,39 @@ const inlineAssetsDir = path.join(distDir, 'assets', 'inline'); const externalAssetsDir = path.join(distDir, 'assets', 'external'); /** - * Private functions + * Saves a file + * + * @param {string} filename Name of file to save + * @param {string} data Data to be saved * + * @return {Promise} Resolves if file has been successfully saved */ +function saveFile(filename, data) { + return new Promise((resolve, reject) => { + fs.outputFile(filename, data, err => err === null ? resolve(true) : reject(err)); + }); +} /** - * Saves a file + * Opens a file * - * @param {String} filename - * @param {String} data - * @returns {Promise} + * @param {string} filename Name of file to open + * + * @return {Promise} Resolves if file has been successfully opened */ - -function saveFile(filename, data) { +function openFile(filename) { return new Promise((resolve, reject) => { - fs.outputFile(filename, data, err => err === null ? resolve(true) : reject(err)); + opener(filename, null, err => err === null ? resolve(true) : reject(err)); }); } /** * Synchronously loads a file with utf8 encoding * - * @param {String} filename - * @returns {String} + * @param {string} filename Name of file to load + * + * @return {string} File data as string */ - function loadFile(filename) { return fs.readFileSync(filename, 'utf8'); } @@ -44,10 +52,10 @@ function loadFile(filename) { * Get report options by extending base options * with user provided options * - * @param {Object} opts - * @returns {Object} + * @param {Object} opts Report options + * + * @return {Object} User options merged with default options */ - function getOptions(opts) { const userOptions = opts || {}; const baseOptions = { @@ -72,10 +80,8 @@ function getOptions(opts) { /** * Get the report assets for inline use * - * @param {Object} opts - * @returns {Object} + * @return {Object} Object with styles and scripts as strings */ - function getAssets() { return { styles: loadFile(path.join(inlineAssetsDir, 'app.css')), @@ -94,10 +100,9 @@ function getAssets() { * - Asset version is not found -> copy assets * - Asset version differs from current version -> copy assets * - * @param {Object} opts - * @returns {Object} + * @param {Object} opts Report options + * */ - function copyAssets(opts) { const assetsDir = path.join(opts.reportDir, 'assets'); const assetsExist = fs.existsSync(assetsDir); @@ -119,13 +124,13 @@ function copyAssets(opts) { /** * Renders the main report React component * - * @param {JSON} data - * @param {Object} options - * @param {String} styles - * @param {String} scripts - * @returns {String} html + * @param {Object} data JSON test data + * @param {Object} options Report options + * @param {string} styles Inline stylesheet + * @param {string} scripts Inline script + * + * @return {string} Rendered HTML string */ - function renderHtml(data, options, styles, scripts) { return render(React.createElement(MainHTML, { data, options, styles, scripts })); } @@ -134,11 +139,11 @@ function renderHtml(data, options, styles, scripts) { /** * Prepare options, assets, and html for saving * - * @param {String} data - * @param {Object} opts - * @returns {Object} + * @param {string} reportData JSON test data + * @param {Object} opts Report options + * + * @return {Object} Prepared data for saving */ - function prepare(reportData, opts) { // Stringify the data if needed let data = reportData; @@ -168,20 +173,34 @@ function prepare(reportData, opts) { } /** - * Exposed functions + * Create the report + * + * @param {string} data JSON test data + * @param {Object} opts Report options * + * @return {Promise} Resolves if report was created successfully */ - function create(data, opts) { const { html, reportFilename, options } = prepare(data, opts); return saveFile(reportFilename, html) - .then(() => { if (options.autoOpen) opener(reportFilename); }); + .then(() => options.autoOpen && openFile(reportFilename)); } +/** + * Create the report synchronously + * + * @param {string} data JSON test data + * @param {Object} opts Report options + * + */ function createSync(data, opts) { const { html, reportFilename, options } = prepare(data, opts); fs.outputFileSync(reportFilename, html); if (options.autoOpen) opener(reportFilename); } +/** + * Expose create/createSync functions + * + */ module.exports = { create, createSync }; diff --git a/package.json b/package.json index be2a51cc..f8809c9c 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "version": "1.1.1", "description": "Generates gorgeous HTML reports from mochawesome reporter.", "scripts": { - "lint": "eslint bin lib src/js src/components/**/*.js src/components/**/*.jsx test/**/*.js test/**/*.jsx", + "lint": "eslint bin/src lib/src src/js src/components/**/*.js src/components/**/*.jsx test/**/*.js test/**/*.jsx", "test": "cross-env UV_THREADPOOL_SIZE=100 BABEL_DISABLE_CACHE=1 NODE_ENV=test nyc $(npm bin)/mocha --require test/helper.js \"test/spec/**/*.test.+(js|jsx)\"", "test:single": "cross-env UV_THREADPOOL_SIZE=100 BABEL_DISABLE_CACHE=1 NODE_ENV=test nyc $(npm bin)/mocha --require test/helper.js ", "compile:main": "$(npm bin)/babel ./lib/src -d lib/", diff --git a/test/spec/cli/cli-main.test.js b/test/spec/cli/cli-main.test.js index 6c745c6c..4338fb93 100644 --- a/test/spec/cli/cli-main.test.js +++ b/test/spec/cli/cli-main.test.js @@ -20,6 +20,7 @@ const sharedOpts = { inlineAssets: false, enableCharts: true, enableCode: true, + autoOpen: false, dev: true }; diff --git a/test/spec/lib/main.test.js b/test/spec/lib/main.test.js index c1bc89d6..6a234b94 100644 --- a/test/spec/lib/main.test.js +++ b/test/spec/lib/main.test.js @@ -44,12 +44,15 @@ beforeEach(() => { opts = Object.assign({}, baseOpts); }); -afterEach(() => { +beforeEach(() => { outputFileStub.reset(); + outputFileStub.resetBehavior(); outputFileSyncStub.reset(); copySyncStub.reset(); readFileSyncStub.reset(); existsSyncStub.reset(); + openerStub.reset(); + openerStub.resetBehavior(); }); describe('lib/main', () => { @@ -60,6 +63,7 @@ describe('lib/main', () => { it('runs create with autoOpen', () => { opts.autoOpen = true; + openerStub.yields(null); outputFileStub.yields(null); return mareport.create(testData, opts).then(() => { expect(openerStub.called).to.equal(true); @@ -71,6 +75,13 @@ describe('lib/main', () => { return expect(mareport.create(testData, opts)).to.be.rejectedWith('save error'); }); + it('runs create with autoOpen and throws', () => { + opts.autoOpen = true; + openerStub.yields('open error'); + outputFileStub.yields(null); + return expect(mareport.create(testData, opts)).to.be.rejectedWith('open error'); + }); + it('runs createSync', () => { mareport.createSync(testData, opts); expect(outputFileSyncStub.calledWith('mochawesome-report/test.html')).to.equal(true); @@ -86,9 +97,8 @@ describe('lib/main', () => { expect(outputFileSyncStub.calledWith('mochawesome-report/mochawesome.html')).to.equal(true); }); - it('runs create with autoOpen', () => { + it('runs createSync with autoOpen', () => { opts.autoOpen = true; - outputFileStub.yields(null); mareport.createSync(testData, opts); expect(openerStub.called).to.equal(true); }); From dfe5044209144150bdcb4ac0e8a90cad699b2a51 Mon Sep 17 00:00:00 2001 From: Adam Gruber Date: Fri, 24 Mar 2017 16:42:22 -0400 Subject: [PATCH 2/2] docs and build [ci skip] --- CHANGELOG.md | 4 +++ README.md | 1 + bin/cli-main.js | 9 +++++ bin/cli.js | 6 ++++ lib/main.js | 87 ++++++++++++++++++++++++++++++------------------- package.json | 2 +- 6 files changed, 75 insertions(+), 34 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1774ab02..03b87063 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ #Changelog +###1.1.2 +- Fix an issue where autoOpen did not work on Windows [mochawesome #142](https://github.com/adamgruber/mochawesome/issues/144) +- Add `autoOpen` option to CLI + ###1.1.1 - Fix an inconsistency between the diff output in the console and the diff output in the report [mochawesome #142](https://github.com/adamgruber/mochawesome/issues/142) - Fix an issue where the report assets would not get updated after upgrading package version. [mochawesome #138](https://github.com/adamgruber/mochawesome/issues/138) diff --git a/README.md b/README.md index c60bb4b5..3d2628ee 100644 --- a/README.md +++ b/README.md @@ -80,6 +80,7 @@ Flag | Type | Default | Description -i, --inline | boolean | false | Inline report assets (scripts, styles) --charts | boolean | true | Display Suite charts --code | boolean | true | Display test code +--autoOpen | boolean | false | Automatically open the report --dev | boolean | false | Enable dev mode (requires local webpack dev server) -h, --help | | | Show CLI help diff --git a/bin/cli-main.js b/bin/cli-main.js index a029d568..c940e31a 100644 --- a/bin/cli-main.js +++ b/bin/cli-main.js @@ -24,6 +24,9 @@ var ERRORS = { /** * Validate the data file * + * @param {string} dataInFile Filename of test data + * + * @return {Object} JSON test data if valid, otherwise error object { err: message } */ function validateInFile(dataInFile) { var dataIn = void 0; @@ -60,6 +63,9 @@ function validateInFile(dataInFile) { /** * Get options to send to report generator * + * @param {Object} args Arguments passed in + * + * @return {Object} Options to pass to report generator */ function getOptions(args) { var reportFilename = args.reportFilename, @@ -69,6 +75,7 @@ function getOptions(args) { inlineAssets = args.inlineAssets, enableCharts = args.enableCharts, enableCode = args.enableCode, + autoOpen = args.autoOpen, dev = args.dev; var filename = reportFilename.replace(fileExtRegex, '') + '.html'; @@ -79,6 +86,7 @@ function getOptions(args) { inlineAssets: inlineAssets, enableCharts: enableCharts, enableCode: enableCode, + autoOpen: autoOpen, dev: dev }; } @@ -86,6 +94,7 @@ function getOptions(args) { /** * Main CLI Program * + * @param {Object} processArgs CLI arguments */ function mareport(processArgs) { var args = processArgs || { _: [] }; diff --git a/bin/cli.js b/bin/cli.js index 72449087..d5d9d6e4 100755 --- a/bin/cli.js +++ b/bin/cli.js @@ -17,6 +17,7 @@ var mareport = require('./cli-main'); * @property {boolean} inlineAssets Should assets be inlined into HTML file (default: false) * @property {boolean} charts Should charts be enabled (default: true) * @property {boolean} code Should test code output be enabled (default: true) + * @property {boolean} autoOpen Open the report after creation (default: false) * @property {boolean} dev Enable dev mode in the report, * asssets loaded via webpack (default: false) */ @@ -70,6 +71,11 @@ yargs.usage('Usage: $0 [data-in-file] [options]').options({ describe: 'Display test code', boolean: true }, + autoOpen: { + default: false, + describe: 'Automatically open the report HTML', + boolean: true + }, dev: { default: false, describe: 'Enable dev mode', diff --git a/lib/main.js b/lib/main.js index 0b9a0530..f0e5e913 100644 --- a/lib/main.js +++ b/lib/main.js @@ -15,21 +15,31 @@ var inlineAssetsDir = path.join(distDir, 'assets', 'inline'); var externalAssetsDir = path.join(distDir, 'assets', 'external'); /** - * Private functions + * Saves a file + * + * @param {string} filename Name of file to save + * @param {string} data Data to be saved * + * @return {Promise} Resolves if file has been successfully saved */ +function saveFile(filename, data) { + return new Promise(function (resolve, reject) { + fs.outputFile(filename, data, function (err) { + return err === null ? resolve(true) : reject(err); + }); + }); +} /** - * Saves a file + * Opens a file + * + * @param {string} filename Name of file to open * - * @param {String} filename - * @param {String} data - * @returns {Promise} + * @return {Promise} Resolves if file has been successfully opened */ - -function saveFile(filename, data) { +function openFile(filename) { return new Promise(function (resolve, reject) { - fs.outputFile(filename, data, function (err) { + opener(filename, null, function (err) { return err === null ? resolve(true) : reject(err); }); }); @@ -38,10 +48,10 @@ function saveFile(filename, data) { /** * Synchronously loads a file with utf8 encoding * - * @param {String} filename - * @returns {String} + * @param {string} filename Name of file to load + * + * @return {string} File data as string */ - function loadFile(filename) { return fs.readFileSync(filename, 'utf8'); } @@ -50,10 +60,10 @@ function loadFile(filename) { * Get report options by extending base options * with user provided options * - * @param {Object} opts - * @returns {Object} + * @param {Object} opts Report options + * + * @return {Object} User options merged with default options */ - function getOptions(opts) { var userOptions = opts || {}; var baseOptions = { @@ -76,10 +86,8 @@ function getOptions(opts) { /** * Get the report assets for inline use * - * @param {Object} opts - * @returns {Object} + * @return {Object} Object with styles and scripts as strings */ - function getAssets() { return { styles: loadFile(path.join(inlineAssetsDir, 'app.css')), @@ -98,10 +106,9 @@ function getAssets() { * - Asset version is not found -> copy assets * - Asset version differs from current version -> copy assets * - * @param {Object} opts - * @returns {Object} + * @param {Object} opts Report options + * */ - function copyAssets(opts) { var assetsDir = path.join(opts.reportDir, 'assets'); var assetsExist = fs.existsSync(assetsDir); @@ -123,13 +130,13 @@ function copyAssets(opts) { /** * Renders the main report React component * - * @param {JSON} data - * @param {Object} options - * @param {String} styles - * @param {String} scripts - * @returns {String} html + * @param {Object} data JSON test data + * @param {Object} options Report options + * @param {string} styles Inline stylesheet + * @param {string} scripts Inline script + * + * @return {string} Rendered HTML string */ - function renderHtml(data, options, styles, scripts) { return render(React.createElement(MainHTML, { data: data, options: options, styles: styles, scripts: scripts })); } @@ -137,11 +144,11 @@ function renderHtml(data, options, styles, scripts) { /** * Prepare options, assets, and html for saving * - * @param {String} data - * @param {Object} opts - * @returns {Object} + * @param {string} reportData JSON test data + * @param {Object} opts Report options + * + * @return {Object} Prepared data for saving */ - function prepare(reportData, opts) { // Stringify the data if needed var data = reportData; @@ -174,10 +181,13 @@ function prepare(reportData, opts) { } /** - * Exposed functions + * Create the report + * + * @param {string} data JSON test data + * @param {Object} opts Report options * + * @return {Promise} Resolves if report was created successfully */ - function create(data, opts) { var _prepare = prepare(data, opts), html = _prepare.html, @@ -185,10 +195,17 @@ function create(data, opts) { options = _prepare.options; return saveFile(reportFilename, html).then(function () { - if (options.autoOpen) opener(reportFilename); + return options.autoOpen && openFile(reportFilename); }); } +/** + * Create the report synchronously + * + * @param {string} data JSON test data + * @param {Object} opts Report options + * + */ function createSync(data, opts) { var _prepare2 = prepare(data, opts), html = _prepare2.html, @@ -199,4 +216,8 @@ function createSync(data, opts) { if (options.autoOpen) opener(reportFilename); } +/** + * Expose create/createSync functions + * + */ module.exports = { create: create, createSync: createSync }; \ No newline at end of file diff --git a/package.json b/package.json index f8809c9c..9e150bf7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "mochawesome-report-generator", - "version": "1.1.1", + "version": "1.1.2", "description": "Generates gorgeous HTML reports from mochawesome reporter.", "scripts": { "lint": "eslint bin/src lib/src src/js src/components/**/*.js src/components/**/*.jsx test/**/*.js test/**/*.jsx",