From 83b2e0e0468edc2d72f09a2169c094da45bc2a32 Mon Sep 17 00:00:00 2001 From: Adam Gruber Date: Thu, 30 Nov 2017 10:56:41 -0500 Subject: [PATCH] 3.0 (#72) * Feature/trim data (#43) * types: remove properties to match new trim data structure * components: derive properties from update suite data structure * lib: nicely stringify json data for saving to file * Feature/flexbox (#46) * nav menu: update to use flexbox * suite: update to use flexbox * test: update to use flexbox * navbar/report: use flexbox, remove unnecessary width props * quick-summary: update to use flexbox * store: remove unused props * update tests * Fix lint (#49) * Fix ESLint violations * Fix stylelint deprecation warning * Feature/perf (#51) * Duration: extend from PureComponent * Icon: convert to class and extend from PureComponent * Test: extend from PureComponent / only render body when in expanded state * Suite: convert to class / implement shouldComponentUpdate * eslint: update react rules for PureComponents * enable transform-react-constant-elements for production * remove unnecessary looping and lodash dependency from reportStore * Feature/functional tests (#52) * add functional tests * remove babel-runtime from functional tests * add info about development * Feature/improve options handling (#59) * export yargs options directly * Add saveJson option * Replace getBaseConfig method with getMergedOptions This incorporates options handling which was previously part of the reporter. * Update tests * Feature/react16 (#62) * Update react/enzyme deps * Simplify test scripts * Configure adapter for Enzyme * Update failing tests * Feature/loader (#54) * 2.2.2 [ci skip] * Add ReportBody and Loader components * Add isLoading prop and toggleIsLoading method to store * Wire up new ReportBody component * Fix broken test * Feature/html option (#63) * options: add saveHtml * lib: implement saveHtml option * Feature/long text (#67) * Add test case for long suite/test title * Fix CSS issue with test icon and long test title * Fix test lint * Feature/render perf (#64) * Separate Loader into its own component * Loader: make an observer of reportStore.isLoading * Replace computed suites property with observable filteredSuites - Remove the computed suites property - Add new filteredSuites observable array - Add updateFilteredSuites method * ReportBody: remove loader; add reaction function; call updateFilteredSuites on mount * Report: add Loader component * Update deps (#68) * Update webpack and loader deps * Add string replace loader and add VERSION property to store * Update postcss plugins and fix style calculations * Update stylelint deps and rules * Update deps * Update cross-env and fix npm scripts * Update test deps * Eslint: Update deps and fix new lint issues * Update normalize.css and remove opinionate.css * jsdom: bump minor version * Footer version (#69) * Footer: display package version * Bump version to 3.0.0 * Update documentation (#70) * changelog: change links from releases to compare * Update README * changelog: update with release notes for 3.0 * Create global reference to store as marge (#71) --- .babelrc | 7 +- .eslintrc.json | 5 + .stylelintrc | 6 +- CHANGELOG.md | 72 +- README.md | 76 +- bin/src/cli-main.js | 3 +- bin/src/cli.js | 4 +- bin/src/types.js | 25 +- lib/src/main.js | 41 +- lib/src/options.js | 98 +- package.json | 93 +- .../dropdown-selector/dropdown-selector.css | 2 - src/components/dropdown-selector/index.jsx | 2 +- src/components/duration.jsx | 4 +- src/components/filterStyles.jsx | 22 + src/components/footer/index.jsx | 11 +- src/components/index.jsx | 4 +- src/components/loader/index.jsx | 28 + src/components/loader/loader.css | 50 + src/components/material-icon/index.jsx | 25 +- src/components/nav-menu/nav-menu-item.jsx | 12 +- src/components/nav-menu/nav-menu-list.jsx | 4 +- src/components/nav-menu/nav-menu.css | 23 +- src/components/nav-menu/nav-menu.jsx | 4 +- src/components/navbar/index.jsx | 42 +- src/components/navbar/navbar.css | 22 +- .../quick-summary/quick-summary.css | 17 +- src/components/report-body/index.jsx | 67 ++ src/components/report.jsx | 117 +-- src/components/suite/suite-summary.css | 4 +- src/components/suite/suite.css | 1 + src/components/suite/suite.jsx | 148 +-- src/components/suite/summary.jsx | 28 +- src/components/summary/summary.css | 2 +- src/components/test/code-snippet.jsx | 8 +- src/components/test/list.jsx | 12 +- src/components/test/test.css | 23 +- src/components/test/test.jsx | 24 +- .../toggle-switch/toggle-switch.css | 5 +- src/js/mochawesome.js | 3 + src/js/reportStore.js | 90 +- src/styles/app.global.css | 5 +- src/styles/vars.css | 7 +- test-functional/cases/context.js | 130 +++ test-functional/cases/general.js | 5 + test-functional/cases/longtext.js | 10 + test-functional/cases/nesting.js | 25 + test-functional/cases/request.js | 18 + test-functional/helpers.js | 90 ++ test-functional/index.js | 58 ++ test/helper.js | 9 +- test/mocha.opts | 3 + test/sample-data/hooks-only.json | 17 - test/sample-data/hooks.json | 631 +------------ test/sample-data/invalid.json | 200 +--- test/sample-data/nested.json | 882 +----------------- test/sample-data/single.json | 200 +--- test/sample-data/suite.json | 56 +- test/sample-data/test.json | 200 +--- test/spec/cli/cli-main.test.js | 4 +- .../components/dropdown/dropdown.test.jsx | 7 +- test/spec/components/loader.test.jsx | 36 + test/spec/components/main.test.jsx | 8 +- test/spec/components/material-icon.test.jsx | 9 +- test/spec/components/navbar.test.jsx | 25 +- test/spec/components/report-body.test.jsx | 62 ++ test/spec/components/report.test.jsx | 26 +- test/spec/components/suite/suite.test.jsx | 34 +- .../components/test/code-snippet.test.jsx | 10 +- test/spec/components/test/test.test.jsx | 41 +- test/spec/lib/main.test.js | 72 +- test/spec/lib/options.test.js | 41 +- test/spec/reportStore.test.js | 100 +- webpack/base.config.js | 1 + webpack/dev.config.js | 4 +- webpack/external.config.js | 4 +- webpack/inline.config.js | 4 +- webpack/loaders.js | 13 +- 78 files changed, 1455 insertions(+), 2826 deletions(-) create mode 100644 src/components/filterStyles.jsx create mode 100644 src/components/loader/index.jsx create mode 100644 src/components/loader/loader.css create mode 100644 src/components/report-body/index.jsx create mode 100644 test-functional/cases/context.js create mode 100644 test-functional/cases/general.js create mode 100644 test-functional/cases/longtext.js create mode 100644 test-functional/cases/nesting.js create mode 100644 test-functional/cases/request.js create mode 100644 test-functional/helpers.js create mode 100644 test-functional/index.js create mode 100644 test/mocha.opts create mode 100644 test/spec/components/loader.test.jsx create mode 100644 test/spec/components/report-body.test.jsx diff --git a/.babelrc b/.babelrc index 20d8ec14..d84ff631 100644 --- a/.babelrc +++ b/.babelrc @@ -7,9 +7,10 @@ "plugins": ["transform-decorators-legacy"], "env": { "test": { - "plugins": [ - "istanbul" - ] + "plugins": ["istanbul"] + }, + "production": { + "plugins": ["transform-react-constant-elements"] } } } diff --git a/.eslintrc.json b/.eslintrc.json index 10ccf38f..358e61fe 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -13,6 +13,7 @@ "sinon": true }, "rules": { + "react/default-props-match-prop-types": [2, { "allowRequiredDefaults": true }], "react/jsx-curly-spacing": [2, "always"], "react/jsx-no-duplicate-props": 2, "react/jsx-no-undef": 2, @@ -31,7 +32,10 @@ "react/sort-comp": 0, "react/jsx-no-bind": 1, "react/require-default-props": 0, + "react/prefer-stateless-function": [2, { "ignorePureComponents": true }], + "jsx-a11y/anchor-is-valid": 1, + "jsx-a11y/click-events-have-key-events": 1, "jsx-a11y/no-static-element-interactions": 0, "arrow-parens": [2, "as-needed"], @@ -39,6 +43,7 @@ "jsx-quotes": [2, "prefer-single"], "comma-dangle": [2, "never"], "indent": [2, 2], + "object-curly-newline": 0, "object-curly-spacing": [2, "always"], "array-bracket-spacing": [2, "always"], "no-undef": 2, diff --git a/.stylelintrc b/.stylelintrc index 5dad62d0..306f499d 100644 --- a/.stylelintrc +++ b/.stylelintrc @@ -9,16 +9,18 @@ "declaration-block-no-duplicate-properties": true, "declaration-no-important": true, "font-family-name-quotes": "always-unless-keyword", + "font-family-no-missing-generic-family-keyword": null, "font-weight-notation": "named-where-possible", "max-empty-lines": 2, "max-nesting-depth": 3, + "no-descending-specificity": null, "no-duplicate-selectors": true, "no-unknown-animations": true, "number-max-precision": 3, "order/properties-order": [["composes", "content", "display", "position"], { unspecified: "bottom" }], "property-no-vendor-prefix": true, "selector-max-compound-selectors": 3, - "selector-no-id": true, + "selector-max-id": 0, "selector-no-vendor-prefix": true, "selector-pseudo-class-no-unknown": [ true, { ignorePseudoClasses: ["global"] } ], "selector-pseudo-element-colon-notation": "single", @@ -29,4 +31,4 @@ "unit-whitelist": ["px", "rem", "em", "%", "deg", "s"], "value-no-vendor-prefix": true } -} \ No newline at end of file +} diff --git a/CHANGELOG.md b/CHANGELOG.md index a0bf92d9..76cd80bf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,24 @@ -# Changelog +# mochawesome-report-generator changelog + +## [3.0.0] / 2017-11-30 +### Added +- The report now displays a loading animation when loading and when toggling filters. +- The report version is now shown in the footer. +- Functional tests to make development a little easier + +### Changed +- **BREAKING:** mochawesome v3.0.0 introduces changes to its JSON output that are not backwards-compatible. As such, the report generator will not work with data created in older versions of mochawesome. + +- Options handling and file saving that was previously done in the reporter is now handled here where it makes more sense. In addition, support was added for the `saveJson` and `saveHtml` options. + +- Improved perceived rendering. The report no longer shows just a blank screen when loading a large number of tests. Instead, the navbar stats and footer will be rendered along with a nice loading animation. In addition, the filter toggles are now more responsive when filtering over a large number of suites/tests. + +- Nearly all components have been updated to use flexbox layout. + +- Unnecessary component renders have been significantly reduced. + +- Most dependencies have been updated to their latest versions. + ### [2.3.2] / 2017-11-13 - Fix an issue where long test titles are truncated with no way to see the full title [#65](https://github.com/adamgruber/mochawesome-report-generator/issues/65) @@ -95,30 +115,30 @@ You can still use the `--reportFilename` flag to set the filename of the generat ### [1.0.1] / 2016-12-26 - Better url handling in context -## [1.0.0] / 2016-12-18 +## 1.0.0 / 2016-12-18 - Initial release -[2.3.2]: https://github.com/adamgruber/mochawesome-report-generator/releases/tag/2.3.2 -[2.3.1]: https://github.com/adamgruber/mochawesome-report-generator/releases/tag/2.3.1 -[2.3.0]: https://github.com/adamgruber/mochawesome-report-generator/releases/tag/2.3.0 -[2.2.2]: https://github.com/adamgruber/mochawesome-report-generator/releases/tag/2.2.2 -[2.2.1]: https://github.com/adamgruber/mochawesome-report-generator/releases/tag/2.2.1 -[2.2.0]: https://github.com/adamgruber/mochawesome-report-generator/releases/tag/2.2.0 -[2.1.1]: https://github.com/adamgruber/mochawesome-report-generator/releases/tag/2.1.1 -[2.1.0]: https://github.com/adamgruber/mochawesome-report-generator/releases/tag/2.1.0 -[2.0.3]: https://github.com/adamgruber/mochawesome-report-generator/releases/tag/2.0.3 -[2.0.2]: https://github.com/adamgruber/mochawesome-report-generator/releases/tag/2.0.2 -[2.0.1]: https://github.com/adamgruber/mochawesome-report-generator/releases/tag/2.0.1 -[2.0.0]: https://github.com/adamgruber/mochawesome-report-generator/releases/tag/2.0.0 -[1.1.2]: https://github.com/adamgruber/mochawesome-report-generator/releases/tag/1.1.2 -[1.1.1]: https://github.com/adamgruber/mochawesome-report-generator/releases/tag/1.1.1 -[1.1.0]: https://github.com/adamgruber/mochawesome-report-generator/releases/tag/1.1.0 -[1.0.8]: https://github.com/adamgruber/mochawesome-report-generator/releases/tag/1.0.8 -[1.0.7]: https://github.com/adamgruber/mochawesome-report-generator/releases/tag/1.0.7 -[1.0.6]: https://github.com/adamgruber/mochawesome-report-generator/releases/tag/1.0.6 -[1.0.5]: https://github.com/adamgruber/mochawesome-report-generator/releases/tag/1.0.5 -[1.0.4]: https://github.com/adamgruber/mochawesome-report-generator/releases/tag/1.0.4 -[1.0.3]: https://github.com/adamgruber/mochawesome-report-generator/releases/tag/1.0.3 -[1.0.2]: https://github.com/adamgruber/mochawesome-report-generator/releases/tag/1.0.2 -[1.0.1]: https://github.com/adamgruber/mochawesome-report-generator/releases/tag/1.0.1 -[1.0.0]: https://github.com/adamgruber/mochawesome-report-generator/releases/tag/1.0.0 +[3.0.0]: https://github.com/adamgruber/mochawesome-report-generator/compare/2.3.2...3.0.0 +[2.3.2]: https://github.com/adamgruber/mochawesome-report-generator/compare/2.3.1...2.3.2 +[2.3.1]: https://github.com/adamgruber/mochawesome-report-generator/compare/2.3.0...2.3.1 +[2.3.0]: https://github.com/adamgruber/mochawesome-report-generator/compare/2.2.2...2.3.0 +[2.2.2]: https://github.com/adamgruber/mochawesome-report-generator/compare/2.2.1...2.2.2 +[2.2.1]: https://github.com/adamgruber/mochawesome-report-generator/compare/2.2.0...2.2.1 +[2.2.0]: https://github.com/adamgruber/mochawesome-report-generator/compare/2.1.1...2.2.0 +[2.1.1]: https://github.com/adamgruber/mochawesome-report-generator/compare/2.1.0...2.1.1 +[2.1.0]: https://github.com/adamgruber/mochawesome-report-generator/compare/2.0.3...2.1.0 +[2.0.3]: https://github.com/adamgruber/mochawesome-report-generator/compare/2.0.2...2.0.3 +[2.0.2]: https://github.com/adamgruber/mochawesome-report-generator/compare/2.0.1...2.0.2 +[2.0.1]: https://github.com/adamgruber/mochawesome-report-generator/compare/2.0.0...2.0.1 +[2.0.0]: https://github.com/adamgruber/mochawesome-report-generator/compare/1.1.2...2.0.0 +[1.1.2]: https://github.com/adamgruber/mochawesome-report-generator/compare/1.1.1...1.1.2 +[1.1.1]: https://github.com/adamgruber/mochawesome-report-generator/compare/1.1.0...1.1.1 +[1.1.0]: https://github.com/adamgruber/mochawesome-report-generator/compare/1.0.8...1.1.0 +[1.0.8]: https://github.com/adamgruber/mochawesome-report-generator/compare/1.0.7...1.0.8 +[1.0.7]: https://github.com/adamgruber/mochawesome-report-generator/compare/1.0.6...1.0.7 +[1.0.6]: https://github.com/adamgruber/mochawesome-report-generator/compare/1.0.5...1.0.6 +[1.0.5]: https://github.com/adamgruber/mochawesome-report-generator/compare/1.0.5...1.0.5 +[1.0.4]: https://github.com/adamgruber/mochawesome-report-generator/compare/1.0.3...1.0.4 +[1.0.3]: https://github.com/adamgruber/mochawesome-report-generator/compare/1.0.2...1.0.3 +[1.0.2]: https://github.com/adamgruber/mochawesome-report-generator/compare/1.0.1...1.0.2 +[1.0.1]: https://github.com/adamgruber/mochawesome-report-generator/compare/1.0.0...1.0.1 diff --git a/README.md b/README.md index 2860b31b..5f90e5f3 100644 --- a/README.md +++ b/README.md @@ -4,43 +4,42 @@ mochawesome-report-generator (marge) **marge** (**m**och**a**wesome-**r**eport-**ge**nerator) is the counterpart to [mochawesome][2], a custom reporter for use with the Javascript testing framework, [mocha][1]. Marge takes the JSON output from [mochawesome][2] and generates a full fledged HTML/CSS report that helps visualize your test suites. -## :tada: Latest Changes -- Support for mocha's `--inline-diffs` option -- Show before and after hooks alongside your tests -- New menu option for fine-tuning how hooks display +## Features -See the [CHANGELOG][] for up-to-date changes. +Mochawesome Report -## Features -- All-new redesigned and streamlined report -- At-a-glance stats including pass percentage -- Beautiful charts -- Support for nested `describe`s -- Supports pending tests -- Filter view by test type -- Quick navigation menu +- Simple, clean, and modern design +- Beautiful charts (via ChartJS) +- Support for test and suite nesting +- Displays before and after hooks - Review test code inline - Stack trace for failed tests -- Inline diffs for actual vs expected results +- Support for adding context information to tests +- Filters to display only the tests you want - Responsive and mobile-friendly -- Supports displaying additional test context -- Custom report [options](#cli-options) - Offline viewing +- Support for IE9+ - CLI for generating reports independent of [mochawesome][2] -## Browser Support -Tested to work in Chrome. *Should* work in any modern web browser including IE9+. -**marge** generates a self-contained report that can be viewed offline. +## Usage with mochawesome + +1. Add Mochawesome to your project: + + `npm install --save-dev mochawesome` -## Sample Report +2. Tell mocha to use the Mochawesome reporter: -Mochawesome Report -Mochawesome Report Menu + `mocha testfile.js --reporter mochawesome` +3. If using mocha programatically: -## Usage + ```js + var mocha = new Mocha({ + reporter: 'mochawesome' + }); + ``` -**via CLI** +## CLI Usage Install mochawesome-report-generator package ```bash @@ -52,10 +51,6 @@ Run the command marge [options] data_file [data_file2 ...] ``` -**via Mochawesome reporter** - -See mochawesome [docs][2]. - ## Output **marge** generates the following inside your project directory: ``` @@ -91,6 +86,8 @@ Flag | Type | Default | Description --overwrite | boolean | true | Overwrite existing report files. *See [notes](#overwrite).* --timestamp, --ts | string | | Append timestamp in specified format to report filename. *See [notes](#timestamp).* --showHooks | string | failed | Set the default display mode for hooks +--saveJson | boolean | false |Should report data be saved to JSON file +--saveHtml | boolean | true | Should report be saved to HTML file --dev | boolean | false | Enable dev mode (requires local webpack dev server) -h, --help | | | Show CLI help @@ -113,6 +110,29 @@ colons | null | 17:46:21 | 174621 Further, if you pass the flag with no format string, it will default to `isoDateTime`. +## Development + +To develop locally, clone the repo and install dependencies. In order to test end-to-end you must also clone [mochawesome][2] into a directory at the same level as this repo. + +You can start the dev server with `npm run devserver`. If you are working on the CLI, use `npm run dev:cli` to watch for changes and rebuild. + +### Running Tests + +#### Unit Tests +To run unit tests, simply use `npm run test`. You can also run a single unit test with `npm run test:single path/to/test.js`. + +#### Functional Tests +Functional tests allow you to run real-world test cases in order to debug the output report. First, start up the dev server in one terminal window with `npm run devserver`. Then, in another window, run the tests with `npm run test:functional`. This will generate a report that you can open in the browser and debug. + +If you want to run a specific folder of functional tests: +`npm run test:functional path/to/tests` + +Or if you want to run a single test: +`npm run test:functional path/to/test.js` + +Or mix and match: +`npm run test:functional path/to/some/tests path/to/another/test.js` + [1]: https://mochajs.org/ [2]: https://github.com/adamgruber/mochawesome [dateformat]: https://github.com/felixge/node-dateformat diff --git a/bin/src/cli-main.js b/bin/src/cli-main.js index 1d68a044..5257b3a8 100644 --- a/bin/src/cli-main.js +++ b/bin/src/cli-main.js @@ -108,8 +108,7 @@ function handleResolved(resolvedValues) { logger.info(chalk.red('\n✘ Some files could not be processed:')); logger.info(errors .map(e => `${chalk.underline(e.filename)}\n${chalk.dim(e.err)}`) - .join('\n\n') - ); + .join('\n\n')); process.exitCode = 1; } return resolvedValues; diff --git a/bin/src/cli.js b/bin/src/cli.js index 531eaed9..b2d687fb 100755 --- a/bin/src/cli.js +++ b/bin/src/cli.js @@ -1,13 +1,13 @@ #!/usr/bin/env node const yargs = require('yargs'); -const options = require('../lib/options'); +const { yargsOptions } = require('../lib/options'); const marge = require('./cli-main'); // Setup yargs yargs .usage('Usage: $0 [options] data_file [data_file2 ...]') .demand(1) - .options(options.getYargsOptions()) + .options(yargsOptions) .help('help') .alias('h', 'help') .version() diff --git a/bin/src/types.js b/bin/src/types.js index 4669294d..d9558840 100644 --- a/bin/src/types.js +++ b/bin/src/types.js @@ -32,7 +32,6 @@ Suite.define(t.struct({ title: t.String, suites: t.list(Suite), tests: t.list(Test), - pending: t.list(Test), root: t.Boolean, _timeout: t.Integer, file: t.String, @@ -40,22 +39,10 @@ Suite.define(t.struct({ fullFile: t.String, beforeHooks: t.list(Test), afterHooks: t.list(Test), - passes: t.list(Test), - failures: t.list(Test), - skipped: t.list(Test), - totalTests: t.Integer, - totalPasses: t.Integer, - totalFailures: t.Integer, - totalPending: t.Integer, - totalSkipped: t.Integer, - hasBeforeHooks: t.Boolean, - hasAfterHooks: t.Boolean, - hasTests: t.Boolean, - hasSuites: t.Boolean, - hasPasses: t.Boolean, - hasFailures: t.Boolean, - hasPending: t.Boolean, - hasSkipped: t.Boolean, + passes: t.list(Uuid), + failures: t.list(Uuid), + pending: t.list(Uuid), + skipped: t.list(Uuid), duration: t.Integer, rootEmpty: t.maybe(t.Boolean) })); @@ -82,10 +69,6 @@ const TestReport = t.struct({ context: t.maybe(t.String) }), suites: Suite, - allTests: t.list(Test), - allPending: t.list(Test), - allPasses: t.list(Test), - allFailures: t.list(Test), copyrightYear: t.Integer }); diff --git a/lib/src/main.js b/lib/src/main.js index b6bea9c5..8b14777d 100644 --- a/lib/src/main.js +++ b/lib/src/main.js @@ -7,7 +7,7 @@ const dateFormat = require('dateformat'); const render = require('react-dom/server').renderToStaticMarkup; const MainHTML = require('./main-html'); const pkg = require('../package.json'); -const options = require('./options'); +const { getMergedOptions } = require('./options'); const distDir = path.join(__dirname, '..', 'dist'); const inlineAssetsDir = path.join(distDir, 'assets', 'inline'); @@ -29,7 +29,10 @@ function saveFile(filename, data, overwrite) { .then(() => filename); } else { return new Promise((resolve, reject) => { - fsu.writeFileUnique(filename.replace(fileExtRegex, '{_###}$&'), data, { force: true }, + fsu.writeFileUnique( + filename.replace(fileExtRegex, '{_###}$&'), + data, + { force: true }, (err, savedFile) => err === null ? resolve(savedFile) : reject(err) ); }); @@ -113,14 +116,13 @@ function getFilename({ reportDir, reportFilename = 'mochawesome', timestamp }) { * @return {Object} User options merged with default options */ function getOptions(opts) { - const userOptions = opts || {}; - const baseOptions = options.getBaseConfig(); - const mergedOptions = Object.assign(baseOptions, userOptions); + const mergedOptions = getMergedOptions(opts || {}); // For saving JSON from mochawesome reporter if (mergedOptions.saveJson) { mergedOptions.jsonFile = `${getFilename(mergedOptions)}.json`; } + mergedOptions.htmlFile = `${getFilename(mergedOptions)}.html`; return mergedOptions; } @@ -222,7 +224,7 @@ function prepare(reportData, opts) { // Render basic template to string const { styles, scripts } = assets; const html = `\n${renderHtml(data, reportOptions, styles, scripts)}`; - return { html, reportData: data, reportOptions }; + return { html, reportOptions }; } /** @@ -234,17 +236,26 @@ function prepare(reportData, opts) { * @return {Promise} Resolves if report was created successfully */ function create(data, opts) { - const { html, reportData, reportOptions } = prepare(data, opts); - const { saveJson, autoOpen, overwrite, jsonFile, htmlFile } = reportOptions; + const { html, reportOptions } = prepare(data, opts); + const { + saveJson, + saveHtml, + autoOpen, + overwrite, + jsonFile, + htmlFile + } = reportOptions; - const savePromises = [ - saveFile(htmlFile, html, overwrite) + const savePromises = []; + + savePromises.push(saveHtml !== false + ? saveFile(htmlFile, html, overwrite) .then(savedHtml => (autoOpen && openFile(savedHtml)) || savedHtml) - ]; + : null); - if (saveJson) { - savePromises.push(saveFile(jsonFile, reportData, overwrite)); - } + savePromises.push(saveJson + ? saveFile(jsonFile, JSON.stringify(data, null, 2), overwrite) + : null); return Promise.all(savePromises); } @@ -267,4 +278,4 @@ function createSync(data, opts) { * Expose functions * */ -module.exports = { create, createSync, getBaseConfig: options.getBaseConfig }; +module.exports = { create, createSync }; diff --git a/lib/src/options.js b/lib/src/options.js index 81446fa2..f1f45c1b 100644 --- a/lib/src/options.js +++ b/lib/src/options.js @@ -25,10 +25,12 @@ const isFunction = require('lodash.isfunction'); * - never: do not display hooks * - failed: display only failed hooks (default) * - context: display only hooks with context + * @property {boolean} saveJson Should report data be saved to JSON file (default: false) + * @property {boolean} saveHtml Should report be saved to HTML file (default: true) * @property {boolean} dev Enable dev mode in the report, * asssets loaded via webpack (default: false) */ -const yargsOptions = { +export const yargsOptions = { f: { alias: [ 'reportFilename' ], describe: 'Filename of saved report', @@ -98,6 +100,16 @@ const yargsOptions = { describe: 'Display hooks in the report', choices: [ 'always', 'never', 'failed', 'context' ] }, + saveJson: { + default: false, + describe: 'Save report data to JSON file', + boolean: true + }, + saveHtml: { + default: true, + describe: 'Save report to HTML file', + boolean: true + }, dev: { default: false, describe: 'Enable dev mode', @@ -106,28 +118,49 @@ const yargsOptions = { }; /** - * Return the Yargs options object + * Return parsed user options merged with base config * - * @return {Object} Yargs options - */ -function getYargsOptions() { - return yargsOptions; -} - -/** - * Extract the base configuration object - * from the Yargs options object + * @param {Object} userOptions User-supplied options * - * @return {Object} Base config + * @return {Object} Merged options */ -function getBaseConfig() { - const baseConfig = {}; +export const getMergedOptions = function (userOptions) { + const mergedOptions = {}; - // Helper to create properties and assign values in the baseConfig object. - // `undefined` values will cause the property to be ignored - function assignVal(prop, val) { + /** + * Retrieve the value of a user supplied option. + * Order of precedence + * 1. User-supplied option + * 2. Environment variable + * + * @param {string} optToGet Option name + * @param {boolean} isBool Treat option as Boolean + * + * @return {string|boolean|undefined} Option value + */ + function _getUserOption(optToGet, isBool) { + const envVar = `MOCHAWESOME_${optToGet.toUpperCase()}`; + if (userOptions && typeof userOptions[optToGet] !== 'undefined') { + return (isBool && typeof userOptions[optToGet] === 'string') + ? userOptions[optToGet] === 'true' + : userOptions[optToGet]; + } + if (typeof process.env[envVar] !== 'undefined') { + return isBool + ? process.env[envVar] === 'true' + : process.env[envVar]; + } + return undefined; + } + + /* + * Helper to create properties and assign values in the mergedOptions object. + * Properties with `undefined` values are ignored + */ + function assignVal(prop, userVal, defaultVal) { + const val = userVal !== undefined ? userVal : defaultVal; if (val !== undefined) { - baseConfig[prop] = val; + mergedOptions[prop] = val; } } @@ -135,18 +168,31 @@ function getBaseConfig() { const yargOpt = yargsOptions[optKey]; const aliases = yargOpt.alias; const defaultVal = isFunction(yargOpt.default) ? yargOpt.default() : yargOpt.default; + const isBool = yargOpt.boolean; + let userVal = _getUserOption(optKey, isBool); // Most options are single-letter so we add the aliases as properties if (Array.isArray(aliases) && aliases.length) { - // Handle cases where the main option is not single-letter - if (optKey.length > 1) assignVal(optKey, defaultVal); - aliases.forEach(alias => assignVal(alias, defaultVal)); + // If the main prop does not have a user supplied value + // we need to check the aliases, stopping if we get a user value + if (userVal === undefined) { + for (let i = 0; i < aliases.length; i += 1) { + userVal = _getUserOption(aliases[i], isBool); + if (userVal !== undefined) { + break; + } + } + } + + // Handle cases where the main option is not a single letter + if (optKey.length > 1) assignVal(optKey, userVal, defaultVal); + + // Loop through aliases to set val + aliases.forEach(alias => assignVal(alias, userVal, defaultVal)); } else { // For options without aliases, use the option regardless of length - assignVal(optKey, defaultVal); + assignVal(optKey, userVal, defaultVal); } }); - return baseConfig; -} - -module.exports = { getYargsOptions, getBaseConfig }; + return mergedOptions; +}; diff --git a/package.json b/package.json index 7c86531f..6554af3b 100644 --- a/package.json +++ b/package.json @@ -1,13 +1,15 @@ { "name": "mochawesome-report-generator", - "version": "2.3.2", + "version": "3.0.0", "description": "Generates gorgeous HTML reports from mochawesome reporter.", "scripts": { "lint": "eslint bin/src lib/src src/js src test --ext js,jsx", "stylelint": "stylelint src/**/*.css src/components/**/*.css", "pretest": "npm run lint", - "test": "cross-env UV_THREADPOOL_SIZE=100 BABEL_DISABLE_CACHE=1 NODE_ENV=test nyc 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 mocha --require test/helper.js ", + "test": "cross-env NODE_ENV=test nyc mocha \"test/spec/**/*.test.+(js|jsx)\"", + "tdd": "cross-env NODE_ENV=test nyc mocha --watch \"test/spec/**/*.test.+(js|jsx)\"", + "test:single": "cross-env NODE_ENV=test nyc mocha ", + "test:functional": "cross-env NODE_ENV=test node ./test-functional/index.js", "compile:main": "babel ./lib/src -d lib/", "compile:cli": "babel ./bin/src -d bin/", "compile": "npm run compile:main && npm run compile:cli", @@ -76,51 +78,55 @@ "lines": 95 }, "dependencies": { - "chalk": "^1.1.3", - "dateformat": "^2.0.0", - "fs-extra": "^3.0.0", + "chalk": "^2.3.0", + "dateformat": "^3.0.2", + "fs-extra": "^4.0.2", "fsu": "^1.0.2", "lodash.isfunction": "^3.0.8", "opener": "^1.4.2", "prop-types": "^15.5.8", - "react": "^15.3.2", - "react-dom": "^15.3.2", + "react": "^16.0.0", + "react-dom": "^16.0.0", "tcomb": "^3.2.17", "tcomb-validation": "^3.3.0", - "validator": "^7.0.0", - "yargs": "^7.0.2" + "validator": "^9.1.2", + "yargs": "^10.0.3" }, "devDependencies": { "app-module-path": "^2.1.0", + "axios": "^0.17.1", "babel-cli": "^6.18.0", - "babel-eslint": "^7.0.0", + "babel-eslint": "^8.0.2", "babel-loader": "^7.0.0", "babel-plugin-istanbul": "^4.0.0", "babel-plugin-transform-decorators-legacy": "^1.3.4", + "babel-plugin-transform-react-constant-elements": "^6.23.0", "babel-preset-es2015": "^6.16.0", "babel-preset-react": "^6.16.0", "babel-preset-stage-0": "^6.16.0", "babel-register": "^6.18.0", - "chai": "^3.5.0", - "chai-as-promised": "^6.0.0", - "chai-enzyme": "^0.6.0", + "chai": "^4.1.2", + "chai-as-promised": "^7.1.1", + "chai-enzyme": "^1.0.0-beta.0", "chart.js": "^2.3.0", "classnames": "^2.2.5", - "cross-env": "^4.0.0", + "cross-env": "^5.1.1", "css-loader": "^0.28.0", "css-modules-require-hook": "^4.0.5", - "enzyme": "^2.5.1", - "eslint": "^3.16.0", - "eslint-config-airbnb": "^14.1.0", + "enzyme": "^3.1.0", + "enzyme-adapter-react-16": "^1.0.2", + "eslint": "^4.12.0", + "eslint-config-airbnb": "^16.1.0", "eslint-loader": "^1.7.1", "eslint-plugin-import": "^2.2.0", - "eslint-plugin-jsx-a11y": "^4.0.0", - "eslint-plugin-react": "^6.10.0", - "extract-text-webpack-plugin": "^2.1.0", - "file-loader": "^0.11.1", + "eslint-plugin-jsx-a11y": "^6.0.2", + "eslint-plugin-react": "^7.5.1", + "extract-text-webpack-plugin": "^3.0.2", + "faker": "^4.1.0", + "file-loader": "^1.1.5", "highlight.js": "^9.7.0", - "jsdom": "^9.8.0", - "json-loader": "^0.5.4", + "jsdom": "^9.12.0", + "json-loader": "^0.5.7", "lodash": "^4.16.4", "material-design-icons": "^3.0.1", "mobx": "^3.1.8", @@ -129,26 +135,25 @@ "mocha": "*", "moment": "^2.15.1", "nodemon": "^1.11.0", - "normalize.css": "^6.0.0", - "nyc": "^10.0.0", - "opinionate.css": "^1.0.0", - "postcss": "^5.2.5", - "postcss-cssnext": "^2.8.0", - "postcss-import": "^9.0.0", - "postcss-loader": "^1.0.0", - "postcss-reporter": "^3.0.0", - "postcss-url": "^6.0.1", + "normalize.css": "^7.0.0", + "nyc": "^11.4.0", + "postcss": "^6.0.14", + "postcss-cssnext": "^3.0.2", + "postcss-import": "^11.0.0", + "postcss-loader": "^2.0.9", + "postcss-reporter": "^5.0.0", + "postcss-url": "^7.3.0", "proxyquire": "^1.7.10", - "react-test-renderer": "^15.5.4", - "should": "^11.1.1", - "sinon": "^2.1.0", - "style-loader": "^0.17.0", - "stylelint": "^7.5.0", - "stylelint-config-standard": "^16.0.0", - "stylelint-order": "^0.4.3", - "stylelint-webpack-plugin": "^0.7.0", - "url-loader": "^0.5.7", - "webpack": "^2.3.2", - "webpack-dev-server": "^2.4.2" + "react-test-renderer": "^16.0.0", + "sinon": "^4.1.2", + "string-replace-loader": "^1.3.0", + "style-loader": "^0.19.0", + "stylelint": "^8.3.1", + "stylelint-config-standard": "^18.0.0", + "stylelint-order": "^0.7.0", + "stylelint-webpack-plugin": "^0.9.0", + "url-loader": "^0.6.2", + "webpack": "^3.8.1", + "webpack-dev-server": "^2.9.5" } } diff --git a/src/components/dropdown-selector/dropdown-selector.css b/src/components/dropdown-selector/dropdown-selector.css index f853c7ca..654b75fc 100644 --- a/src/components/dropdown-selector/dropdown-selector.css +++ b/src/components/dropdown-selector/dropdown-selector.css @@ -1,9 +1,7 @@ @import '../../styles/vars'; .dropdown { - position: absolute; right: -8px; - top: 0; } .menu { diff --git a/src/components/dropdown-selector/index.jsx b/src/components/dropdown-selector/index.jsx index 9106b615..bec22ad9 100644 --- a/src/components/dropdown-selector/index.jsx +++ b/src/components/dropdown-selector/index.jsx @@ -12,7 +12,7 @@ const toggleIcon = () => ( function DropdownSelector(props) { const { className, labelClassName, label, icon, iconClassName, onSelect, selections, selected, - ddClassName, ddMenuClassName, ddSelectedClassName } = props; + ddClassName, ddMenuClassName, ddSelectedClassName } = props; const labelCxName = cx('label', { 'with-icon': !!icon }, labelClassName); return (
diff --git a/src/components/duration.jsx b/src/components/duration.jsx index ac33a8c6..baca9723 100644 --- a/src/components/duration.jsx +++ b/src/components/duration.jsx @@ -1,9 +1,9 @@ -import React from 'react'; +import React, { PureComponent } from 'react'; import PropTypes from 'prop-types'; import classNames from 'classnames'; import moment from 'moment'; -class Duration extends React.Component { +class Duration extends PureComponent { static propTypes = { className: PropTypes.string, unitsClassName: PropTypes.string, diff --git a/src/components/filterStyles.jsx b/src/components/filterStyles.jsx new file mode 100644 index 00000000..1bd424bc --- /dev/null +++ b/src/components/filterStyles.jsx @@ -0,0 +1,22 @@ +import React, { Component } from 'react'; +import PropTypes from 'prop-types'; + +class FilterStyles extends Component { + static propTypes = { + filtered: PropTypes.array + } + + componentDidMount() { + console.log(this.node); + } + + render() { + return ( + + ); + } +} + +export default FilterStyles; diff --git a/src/components/footer/index.jsx b/src/components/footer/index.jsx index 8c528ee9..ba34df59 100644 --- a/src/components/footer/index.jsx +++ b/src/components/footer/index.jsx @@ -1,5 +1,6 @@ /* eslint-disable max-len */ import React from 'react'; +import PropTypes from 'prop-types'; import classNames from 'classnames/bind'; import styles from './footer.css'; @@ -10,7 +11,7 @@ const urls = { github: 'https://github.com/adamgruber' }; -const Footer = () => { +const Footer = ({ version }) => { const copyrightYear = new Date().getFullYear(); return (
); }; +Footer.propTypes = { + version: PropTypes.string +}; + export default Footer; diff --git a/src/components/index.jsx b/src/components/index.jsx index 96e9d5e8..cacfa34f 100644 --- a/src/components/index.jsx +++ b/src/components/index.jsx @@ -3,11 +3,13 @@ export { default as DropdownSelector } from './dropdown-selector'; export { default as Duration } from './duration'; export { default as Footer } from './footer'; export { default as Icon } from './material-icon'; +export { default as Loader } from './loader'; export { default as MochawesomeReport } from './report'; export { default as Navbar } from './navbar'; export { default as NavMenu } from './nav-menu'; export { default as QuickSummary } from './quick-summary'; +export { default as RadioButton } from './radio-button'; +export { default as ReportBody } from './report-body'; // export { default as StatusBar } from './status-bar'; // export { default as Summary } from './summary'; -export { default as RadioButton } from './radio-button'; export { default as ToggleSwitch } from './toggle-switch'; diff --git a/src/components/loader/index.jsx b/src/components/loader/index.jsx new file mode 100644 index 00000000..cdffd190 --- /dev/null +++ b/src/components/loader/index.jsx @@ -0,0 +1,28 @@ +import React, { Component } from 'react'; +import PropTypes from 'prop-types'; +import { observer, inject } from 'mobx-react'; +import classNames from 'classnames/bind'; +import styles from './loader.css'; + +const cx = classNames.bind(styles); + +@inject('reportStore') @observer +class Loader extends Component { + static propTypes = { + reportStore: PropTypes.object + }; + + render() { + const { isLoading } = this.props.reportStore; + return isLoading && ( +
+
+
+

Generating Report

+
+
+ ); + } +} + +export default Loader; diff --git a/src/components/loader/loader.css b/src/components/loader/loader.css new file mode 100644 index 00000000..c3f69451 --- /dev/null +++ b/src/components/loader/loader.css @@ -0,0 +1,50 @@ +@import '../../styles/vars'; + +.component { + position: fixed; + top: 0; + height: 100%; + width: 100%; + background-color: color(var(--body-bg) alpha(60%)); + padding-top: var(--navbar-height); +} + +.wrap { + display: flex; + align-items: center; + justify-content: center; + flex-direction: column; + min-height: 200px; +} + +.text { + color: var(--gray-light); + text-align: center; + margin: 1rem 0 0 0; +} + +.spinner { + border-radius: 50%; + width: 42px; + height: 42px; + border: 0.25rem solid var(--gray-medium); + border-top-color: var(--gray); + animation: spin 1s infinite linear; +} + +@keyframes spin { + 0% { + transform: rotate(0deg); + } + + 100% { + transform: rotate(360deg); + } +} + +/* Tablet 768 and up */ +@media (--screen-sm) { + .component { + padding-top: var(--navbar-height-short); + } +} diff --git a/src/components/material-icon/index.jsx b/src/components/material-icon/index.jsx index e9ddf726..34d7842f 100644 --- a/src/components/material-icon/index.jsx +++ b/src/components/material-icon/index.jsx @@ -1,19 +1,22 @@ /* eslint-disable react/no-danger, max-len */ -import React from 'react'; +import React, { PureComponent } from 'react'; import PropTypes from 'prop-types'; import classNames from 'classnames'; import iconmap from './icon-map.json'; -const Icon = ({ className, name, size, foreground }) => { - const iconCode = iconmap[name]; - const cxName = classNames( - 'material-icons', - !!size && `md-${size}`, - !!foreground && `md-${foreground}`, - className - ); - return !!iconCode && ; -}; +class Icon extends PureComponent { + render() { + const { className, name, size, foreground } = this.props; + const iconCode = iconmap[name]; + const cxName = classNames( + 'material-icons', + !!size && `md-${size}`, + !!foreground && `md-${foreground}`, + className + ); + return !!iconCode && ; + } +} Icon.propTypes = { className: PropTypes.string, diff --git a/src/components/nav-menu/nav-menu-item.jsx b/src/components/nav-menu/nav-menu-item.jsx index 0c0bae5f..2d88d3cc 100644 --- a/src/components/nav-menu/nav-menu-item.jsx +++ b/src/components/nav-menu/nav-menu-item.jsx @@ -1,6 +1,7 @@ import React, { Component } from 'react'; import PropTypes from 'prop-types'; import isEqual from 'lodash/isEqual'; +import isEmpty from 'lodash/isEmpty'; import { Icon } from 'components'; import { NavMenuList } from 'components/nav-menu'; import classNames from 'classnames/bind'; @@ -23,10 +24,15 @@ class NavMenuItem extends Component { render() { const { suite, showPassed, showFailed, showPending, showSkipped } = this.props; - const { suites, uuid, title, hasTests, - hasFailures, hasPending, hasSkipped, hasPasses } = suite; + const { suites, uuid, title } = suite; const navItemProps = { showPassed, showFailed, showPending, showSkipped }; + const hasTests = !isEmpty(suite.tests); + const hasPasses = !isEmpty(suite.passes); + const hasFailures = !isEmpty(suite.failures); + const hasPending = !isEmpty(suite.pending); + const hasSkipped = !isEmpty(suite.skipped); + const fail = hasTests && hasFailures; const pend = hasTests && hasPending && !hasFailures; const skip = hasTests && hasSkipped && !hasFailures && !hasPending; @@ -80,7 +86,7 @@ class NavMenuItem extends Component { // Find element to scroll to const suiteEl = document.getElementById(suiteId); // Get its top value - const top = suiteEl.getBoundingClientRect().top; + const { top } = suiteEl.getBoundingClientRect(); // Get the details container and get its top padding const detailsCnt = document.getElementById('details'); let topPad = window.getComputedStyle(detailsCnt).getPropertyValue('padding-top'); diff --git a/src/components/nav-menu/nav-menu-list.jsx b/src/components/nav-menu/nav-menu-list.jsx index a9069f4d..05391329 100644 --- a/src/components/nav-menu/nav-menu-list.jsx +++ b/src/components/nav-menu/nav-menu-list.jsx @@ -29,8 +29,8 @@ class NavMenuList extends Component { { suites.map(subSuite => (
    -
) - ) } + )) + }
); } diff --git a/src/components/nav-menu/nav-menu.css b/src/components/nav-menu/nav-menu.css index 7dd471f7..9fe8a290 100644 --- a/src/components/nav-menu/nav-menu.css +++ b/src/components/nav-menu/nav-menu.css @@ -69,14 +69,17 @@ } .control { + display: flex; position: relative; margin: 8px 0; + align-items: center; } .control-label { @apply --font-regular; display: inline-block; + flex-grow: 1; font-size: 13px; vertical-align: top; line-height: 24px; @@ -133,8 +136,7 @@ .no-tests > .item:not(.has-tests) { /* stylelint-disable-next-line selector-max-compound-selectors */ - & > div > .sub, - & > .link { + & > div > .sub { padding-left: 0; } @@ -149,12 +151,10 @@ } .link { - @apply --link-transition; - @apply --text-overflow; - - display: block; + display: flex; position: relative; - padding: 3px 16px 3px 22px; + align-items: center; + padding: 3px 0; color: var(--gray); &:hover { @@ -167,12 +167,15 @@ outline: none; text-decoration: none; } + + & span { + @apply --link-transition; + @apply --text-overflow; + } } .link-icon { - position: absolute; - top: 4px; - left: 0; + margin-right: 2px; &.pass { color: var(--green500); diff --git a/src/components/nav-menu/nav-menu.jsx b/src/components/nav-menu/nav-menu.jsx index 5cdb189d..1ac05904 100644 --- a/src/components/nav-menu/nav-menu.jsx +++ b/src/components/nav-menu/nav-menu.jsx @@ -82,7 +82,9 @@ const NavMenu = props => { { !!suites && suites.map(suite => (
    + className={ cx('list', 'main', { + 'no-tests': (!suite.tests || suite.tests.length === 0) + }) }> { !!suite.suites && suite.suites.map(subSuite => ( )) } diff --git a/src/components/navbar/index.jsx b/src/components/navbar/index.jsx index e73d6c5d..4e9b257e 100644 --- a/src/components/navbar/index.jsx +++ b/src/components/navbar/index.jsx @@ -7,18 +7,15 @@ import reportStore from '../../js/reportStore'; const cx = classNames.bind(styles); - -const Navbar = ({ reportTitle, stats, qsNodeRef, qsWidth, mobileBreakpoint }) => { - const onClickFn = () => (reportStore.openSideNav()); - +const Navbar = ({ reportTitle, stats }) => { const { passPercent, pendingPercent } = stats; - const failPercent = 100 - passPercent; - const titleCntStyle = (!mobileBreakpoint && qsWidth) ? { paddingRight: qsWidth } : null; + const failPercent = 100 - passPercent; const allPending = pendingPercent === 100; - const showPctBar = passPercent !== null && pendingPercent !== null; + const onClickFn = () => (reportStore.openSideNav()); + const pctBar = (prop, cname, title) => ( return (
    - -
    +
    +

    { reportTitle }

    -
    +
    - { showPctBar &&
    - { allPending && pctBar(pendingPercent, 'pend', 'Pending') } - { !allPending && pctBar(passPercent, 'pass', 'Passing') } - { !allPending && pctBar(failPercent, 'fail', 'Failing') } -
    } + { showPctBar && +
    + { allPending && pctBar(pendingPercent, 'pend', 'Pending') } + { !allPending && pctBar(passPercent, 'pass', 'Passing') } + { !allPending && pctBar(failPercent, 'fail', 'Failing') } +
    + }
    ); }; Navbar.propTypes = { reportTitle: PropTypes.string, - stats: PropTypes.object, - qsNodeRef: PropTypes.func, - qsWidth: PropTypes.number, - mobileBreakpoint: PropTypes.bool + stats: PropTypes.object }; Navbar.displayName = 'Navbar'; diff --git a/src/components/navbar/navbar.css b/src/components/navbar/navbar.css index 973174b1..d733dd89 100644 --- a/src/components/navbar/navbar.css +++ b/src/components/navbar/navbar.css @@ -3,7 +3,9 @@ .component { @apply --clearfix; + display: flex; position: fixed; + flex-direction: column; top: 0; right: 0; left: 0; @@ -16,16 +18,16 @@ } .report-info-cnt { - padding: 0 12px 0 48px; + display: flex; + overflow: hidden; + padding-right: 12px; } .menu-button { @apply --button-base; - position: absolute; padding: 16px 12px; height: 24px; - float: left; color: var(--light-icon-inactive); &:hover { @@ -36,9 +38,10 @@ .report-title { @apply --font-light; + flex-grow: 1; color: #fff; font-size: 18px; - line-height: calc(var(--navbar-height-short) - 4); + line-height: calc(var(--navbar-height-short) - 4px); margin: 0; overflow: hidden; text-overflow: ellipsis; @@ -48,6 +51,7 @@ .pct-bar { @apply --clearfix; + display: flex; position: absolute; left: 0; right: 0; @@ -69,7 +73,6 @@ .pct-bar-segment { height: 4px; - float: left; } /* Tablet 768 and up */ @@ -77,16 +80,11 @@ .component { min-height: var(--navbar-height-short); height: var(--navbar-height-short); + flex-direction: initial; } .report-info-cnt { - padding: 0 530px 0 72px; - } - - .stats { - position: absolute; - top: 0; - right: 0; + flex-grow: 1; } .menu-button { diff --git a/src/components/quick-summary/quick-summary.css b/src/components/quick-summary/quick-summary.css index 1c733d16..c03a2312 100644 --- a/src/components/quick-summary/quick-summary.css +++ b/src/components/quick-summary/quick-summary.css @@ -1,6 +1,8 @@ @import '../../styles/vars'; .cnt { + display: flex; + flex-direction: column; padding: 0 12px; } @@ -8,6 +10,7 @@ @apply --clearfix; @apply --list-unstyled; + display: flex; transition: opacity 0.2s ease-out; margin: 0 0 8px 0; } @@ -15,10 +18,11 @@ .item { @apply --font-light; + display: flex; + align-items: flex-start; color: #fff; font-size: 16px; - float: left; - width: 25%; + flex-basis: 25%; &.tests { color: #fff; @@ -66,7 +70,6 @@ top: 2px; font-size: 18px; margin-right: 4px; - float: left; } .circle-icon { @@ -78,19 +81,17 @@ /* Tablet 768 and up */ @media (--screen-sm) { .cnt { - display: block; - float: left; - padding: 14px 12px 0 24px; + flex-direction: initial; + padding: 14px 12px 0 0; } .list { - display: inline-block; margin: 0; } .item { font-size: 18px; - width: auto; + flex-basis: initial; margin: 0 12px; } diff --git a/src/components/report-body/index.jsx b/src/components/report-body/index.jsx new file mode 100644 index 00000000..20a5635c --- /dev/null +++ b/src/components/report-body/index.jsx @@ -0,0 +1,67 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { reaction } from 'mobx'; +import { inject, observer } from 'mobx-react'; +import { Suite } from 'components/suite'; +import cx from 'classnames'; + +@inject('reportStore') @observer +class ReportBody extends React.Component { + static propTypes = { + reportStore: PropTypes.object + }; + + updateSuites(timeout) { + this.props.reportStore.updateFilteredSuites(timeout); + } + + componentDidMount() { + this.updateSuites(); + this.disposer = reaction( + () => { + const { + showPassed, + showFailed, + showPending, + showSkipped, + showHooks + } = this.props.reportStore; + return { + showPassed, + showFailed, + showPending, + showSkipped, + showHooks + }; + }, + () => this.updateSuites(0), + { delay: 300 } + ); + } + + componentWillUnmount() { + this.disposer(); + } + + render() { + const { + enableCode, + enableChart, + filteredSuites: suites + } = this.props.reportStore; + + return ( +
    + { suites.map(suite => ( + )) + } +
    + ); + } +} + +export default ReportBody; diff --git a/src/components/report.jsx b/src/components/report.jsx index bebde42b..84a4a3ea 100644 --- a/src/components/report.jsx +++ b/src/components/report.jsx @@ -1,79 +1,58 @@ -/* eslint-disable import/no-extraneous-dependencies, no-console */ -import React, { Component } from 'react'; +/* eslint-disable import/no-extraneous-dependencies */ +import React from 'react'; import PropTypes from 'prop-types'; import DevTools from 'mobx-react-devtools'; import { Provider, observer } from 'mobx-react'; -import { Footer, Navbar } from 'components'; +import { Footer, Loader, Navbar, ReportBody } from 'components'; import { NavMenu } from 'components/nav-menu'; -import { Suite } from 'components/suite'; -import cx from 'classnames'; import 'styles/app.global.css'; -@observer -class MochawesomeReport extends Component { - static displayName = 'MochawesomeReport'; - static propTypes = { - store: PropTypes.object +const MochawesomeReport = observer(props => { + const { + reportTitle, + allSuites, + stats, + showPassed, + showFailed, + showPending, + showSkipped, + showHooks, + sideNavOpen, + devMode, + VERSION + } = props.store; + + const navMenuProps = { + reportTitle, + stats, + showPassed, + showFailed, + showPending, + showSkipped, + showHooks, + sideNavOpen }; - componentDidMount() { - window.addEventListener('resize', this.resizeHandler); - this.resizeHandler(); - setTimeout(() => { - const w = this.qsNode.getBoundingClientRect().width; - this.props.store.setQuickSummaryWidth(w); - }, 0); - } - - componentWillUnmount() { - window.removeEventListener('resize', this.resizeHandler); - } - - resizeHandler = () => { - this.props.store.setWindowWidth(window.innerWidth); - } - - render() { - const { reportTitle, suites, allSuites, stats, enableChart, enableCode, - showPassed, showFailed, showPending, showSkipped, showHooks, sideNavOpen, - mobileBreakpoint, quickSummaryWidth, devMode } = this.props.store; - - const navMenuProps = { - reportTitle, - stats, - showPassed, - showFailed, - showPending, - showSkipped, - showHooks, - sideNavOpen - }; - - return ( - -
    - { this.qsNode = node; } } /> -
    - { suites.map(suite => ( - ) - ) } -
    -
    - - { devMode && } -
    -
    - ); - } -} + return ( + +
    + + + +
    + + { devMode && } +
    +
    + ); +}); + +MochawesomeReport.propTypes = { + store: PropTypes.object +}; + +MochawesomeReport.displayName = 'MochawesomeReport'; export default MochawesomeReport; diff --git a/src/components/suite/suite-summary.css b/src/components/suite/suite-summary.css index 061ee414..cb98a912 100644 --- a/src/components/suite/suite-summary.css +++ b/src/components/suite/suite-summary.css @@ -5,6 +5,7 @@ @apply --font-regular; @apply --list-unstyled; + display: flex; font-size: 15px; margin: 16px 0 0 0; @@ -14,7 +15,7 @@ } .summary-item { - float: left; + display: flex; line-height: 18px; margin: 0 8px; color: var(--black54); @@ -46,6 +47,5 @@ } .icon { - float: left; margin-right: 2px; } diff --git a/src/components/suite/suite.css b/src/components/suite/suite.css index 71d3a174..86ab705b 100644 --- a/src/components/suite/suite.css +++ b/src/components/suite/suite.css @@ -8,6 +8,7 @@ margin-bottom: 20px; } +/* stylelint-disable-next-line selector-max-compound-selectors */ .component > .body > div > .component { border: 1px solid var(--grey300); border-right: none; diff --git a/src/components/suite/suite.jsx b/src/components/suite/suite.jsx index 1647385e..ccac2964 100644 --- a/src/components/suite/suite.jsx +++ b/src/components/suite/suite.jsx @@ -1,83 +1,103 @@ -import React from 'react'; +import React, { Component } from 'react'; import PropTypes from 'prop-types'; +import isEmpty from 'lodash/isEmpty'; import { TestList } from 'components/test'; import { SuiteChart, SuiteList, SuiteSummary } from 'components/suite'; import classNames from 'classnames/bind'; +import isEqual from 'lodash/isEqual'; import styles from './suite.css'; const cx = classNames.bind(styles); -const Suite = ({ className, suite, enableChart, enableCode }) => { - const { root, rootEmpty, suites, tests, beforeHooks, afterHooks, uuid, title, file, - hasSuites, hasTests, hasFailures, hasPending, hasSkipped, - hasPasses, duration, totalTests, totalPasses, totalFailures, - totalPending, totalSkipped } = suite; +class Suite extends Component { + shouldComponentUpdate(nextProps) { + return !isEqual(this.props, nextProps); + } - const hasBeforeHooks = beforeHooks && beforeHooks.length > 0; - const hasAfterHooks = afterHooks && afterHooks.length > 0; + render() { + const { className, suite, enableChart, enableCode } = this.props; + const { root, rootEmpty, suites, tests, beforeHooks, afterHooks, + uuid, title, file, duration } = suite; - const subSuites = isMain => hasSuites && ( - - ); + const hasSuites = !isEmpty(suites); + const hasTests = !isEmpty(tests); + const hasPasses = !isEmpty(suite.passes); + const hasFailures = !isEmpty(suite.failures); + const hasPending = !isEmpty(suite.pending); + const hasSkipped = !isEmpty(suite.skipped); + const hasBeforeHooks = !isEmpty(beforeHooks); + const hasAfterHooks = !isEmpty(afterHooks); + const totalTests = hasTests ? tests.length : 0; + const totalPasses = hasPasses ? suite.passes.length : 0; + const totalFailures = hasFailures ? suite.failures.length : 0; + const totalPending = hasPending ? suite.pending.length : 0; + const totalSkipped = hasSkipped ? suite.skipped.length : 0; - const testListComp = () => (hasTests || hasBeforeHooks || hasAfterHooks) && ( - - ); + const subSuites = isMain => hasSuites && ( + + ); - const cxname = cx('component', className, { - 'root-suite': root, - 'has-suites': hasSuites, - 'no-suites': !hasSuites, - 'has-tests': hasTests, - 'no-tests': !hasTests && !hasBeforeHooks && !hasAfterHooks, - 'has-passed': hasPasses, - 'has-failed': hasFailures, - 'has-pending': hasPending, - 'has-skipped': hasSkipped, - 'chart-enabled': enableChart - }); + const testListComp = () => (hasTests || hasBeforeHooks || hasAfterHooks) && ( + + ); - const summaryProps = { - duration, - totalTests, - totalPasses, - totalFailures, - totalPending, - totalSkipped, - className: cx({ 'no-margin': title === '' && file === '' }) - }; - const chartProps = { totalPasses, totalFailures, totalPending, totalSkipped }; + const cxname = cx('component', className, { + 'root-suite': root, + 'has-suites': hasSuites, + 'no-suites': !hasSuites, + 'has-tests': hasTests, + 'no-tests': !hasTests && !hasBeforeHooks && !hasAfterHooks, + 'has-passed': hasPasses, + 'has-failed': hasFailures, + 'has-pending': hasPending, + 'has-skipped': hasSkipped, + 'chart-enabled': enableChart + }); - if (rootEmpty && !hasBeforeHooks && !hasAfterHooks) { - return subSuites(true); - } + const summaryProps = { + duration, + totalTests, + totalPasses, + totalFailures, + totalPending, + totalSkipped, + className: cx({ 'no-margin': title === '' && file === '' }) + }; + const chartProps = { totalPasses, totalFailures, totalPending, totalSkipped }; - const hideHeader = root && !hasTests && (hasBeforeHooks || hasAfterHooks); + if (rootEmpty && !hasBeforeHooks && !hasAfterHooks) { + return subSuites(true); + } - return ( -
    - { !hideHeader &&
    - { title !== '' &&

    { title }

    } - { file !== '' &&
    { file }
    } - { hasTests && enableChart && } - { hasTests && } -
    } -
    - { testListComp() } - { subSuites() } -
    -
    - ); -}; + const hideHeader = root && !hasTests && (hasBeforeHooks || hasAfterHooks); + + return ( +
    + { !hideHeader && +
    + { title !== '' &&

    { title }

    } + { file !== '' &&
    { file }
    } + { hasTests && enableChart && } + { hasTests && } +
    + } +
    + { testListComp() } + { subSuites() } +
    +
    + ); + } +} Suite.propTypes = { suite: PropTypes.object, diff --git a/src/components/suite/summary.jsx b/src/components/suite/summary.jsx index 51324447..3a261d0b 100644 --- a/src/components/suite/summary.jsx +++ b/src/components/suite/summary.jsx @@ -19,18 +19,22 @@ const SuiteSummary = props => {
  • { totalTests }
  • - { !!totalPasses &&
  • - { totalPasses } -
  • } - { !!totalFailures &&
  • - { totalFailures } -
  • } - { !!totalPending &&
  • - { totalPending } -
  • } - { !!totalSkipped &&
  • - { totalSkipped } -
  • } + { !!totalPasses && +
  • + { totalPasses } +
  • } + { !!totalFailures && +
  • + { totalFailures } +
  • } + { !!totalPending && +
  • + { totalPending } +
  • } + { !!totalSkipped && +
  • + { totalSkipped } +
  • }
); }; diff --git a/src/components/summary/summary.css b/src/components/summary/summary.css index 14983e56..5823fa57 100644 --- a/src/components/summary/summary.css +++ b/src/components/summary/summary.css @@ -166,7 +166,7 @@ @media (--screen-sm) { .cnt { position: static; - padding-top: calc(var(--navbar-height) + 4); + padding-top: calc(var(--navbar-height) + 4px); } .col { &:nth-child(-n+3) { diff --git a/src/components/test/code-snippet.jsx b/src/components/test/code-snippet.jsx index 86c1b137..4c8bbd8a 100644 --- a/src/components/test/code-snippet.jsx +++ b/src/components/test/code-snippet.jsx @@ -62,13 +62,13 @@ class CodeSnippet extends Component { }); const renderLegendLeft = () => (isInlineDiff - ? actual - : + expected + ? actual + : + expected ); const renderLegendRight = () => (isInlineDiff - ? {'expected\n\n'} - : {'- actual\n\n'} + ? {'expected\n\n'} + : {'- actual\n\n'} ); const mapInlineDiffCode = ({ added, removed, value }, i) => { diff --git a/src/components/test/list.jsx b/src/components/test/list.jsx index 2ff3bea6..761f3cd8 100644 --- a/src/components/test/list.jsx +++ b/src/components/test/list.jsx @@ -9,14 +9,14 @@ const cx = classNames.bind(styles); const TestList = ({ className, tests, beforeHooks, afterHooks, enableCode }) => (
{ !!beforeHooks && beforeHooks.map(test => ( - ) - ) } + )) + } { !!tests && tests.map(test => ( - ) - ) } + )) + } { !!afterHooks && afterHooks.map(test => ( - ) - ) } + )) + }
); diff --git a/src/components/test/test.css b/src/components/test/test.css index 77aa9b7b..79aa3c4d 100644 --- a/src/components/test/test.css +++ b/src/components/test/test.css @@ -24,6 +24,7 @@ .header { @apply --clearfix; + display: flex; position: relative; padding: 10px 0; margin: 0 16px 0 13px; @@ -33,22 +34,15 @@ } } -.title-wrap { - position: relative; -} - .title { @apply --font-regular; @apply --text-overflow; + flex-grow: 1; font-size: 13px; line-height: 24px; margin: 0; - padding: 0 80px 0 40px; - - @nest .with-context & { - padding-right: 106px; - } + padding-right: 12px; @nest .hook & { color: var(--black54); @@ -62,9 +56,7 @@ } .icon { - position: absolute; - top: 0; - left: 0; + align-self: flex-start; padding: 3px; border-radius: 50%; color: #fff; @@ -101,9 +93,7 @@ } .info { - position: absolute; - top: 10px; - right: 0; + display: flex; } .duration { @@ -111,7 +101,6 @@ line-height: 24px; color: var(--black54); - float: left; @nest .component:hover:not(.pending) & { color: var(--black87); @@ -125,7 +114,6 @@ } .duration-icon { - float: right; margin-left: 4px; /* stylelint-disable-next-line declaration-no-important */ line-height: 24px !important; @@ -142,7 +130,6 @@ .context-icon { position: relative; - float: left; /* stylelint-disable-next-line declaration-no-important */ line-height: 24px !important; color: var(--black38); diff --git a/src/components/test/test.jsx b/src/components/test/test.jsx index 3ff6d254..e1b75145 100644 --- a/src/components/test/test.jsx +++ b/src/components/test/test.jsx @@ -1,5 +1,5 @@ /* eslint-disable max-len */ -import React from 'react'; +import React, { PureComponent } from 'react'; import PropTypes from 'prop-types'; import { Duration, Icon } from 'components'; import { CodeSnippet, TestContext } from 'components/test'; @@ -8,7 +8,7 @@ import styles from './test.css'; const cx = classNames.bind(styles); -class Test extends React.Component { +class Test extends PureComponent { constructor() { super(); this.toggleExpandedState = this.toggleExpandedState.bind(this); @@ -83,10 +83,8 @@ class Test extends React.Component { return (
-
- { testIcon() } -

{ title }

-
+ { testIcon() } +

{ title }

{ !!context && } { !isHook && } @@ -94,12 +92,14 @@ class Test extends React.Component {
{ !!err.message &&

{ err.message }

} -
- { } - { } - { enableCode && } - { !!context && } -
+ { this.state.expanded && +
+ { } + { } + { enableCode && } + { !!context && } +
+ }
); } diff --git a/src/components/toggle-switch/toggle-switch.css b/src/components/toggle-switch/toggle-switch.css index 7557a94e..bf510656 100644 --- a/src/components/toggle-switch/toggle-switch.css +++ b/src/components/toggle-switch/toggle-switch.css @@ -7,10 +7,7 @@ } .switch { - position: absolute; - top: 50%; - right: 0; - margin-top: -6px; + position: relative; width: 32px; height: 12px; border-radius: 12px; diff --git a/src/js/mochawesome.js b/src/js/mochawesome.js index 8d7c6f64..e719eac0 100644 --- a/src/js/mochawesome.js +++ b/src/js/mochawesome.js @@ -8,6 +8,9 @@ const bodyEl = document.querySelector('body'); const data = JSON.parse(bodyEl.getAttribute('data-raw')); const config = JSON.parse(bodyEl.getAttribute('data-config')); +// Add global reference to the store +window.marge = reportStore; + // Register hljs languages hljs.registerLanguage('javascript', require('highlight.js/lib/languages/javascript')); hljs.registerLanguage('diff', require('highlight.js/lib/languages/diff')); diff --git a/src/js/reportStore.js b/src/js/reportStore.js index e843cf1c..e573ad2c 100644 --- a/src/js/reportStore.js +++ b/src/js/reportStore.js @@ -1,32 +1,27 @@ -import { observable, computed, action } from 'mobx'; -import filter from 'lodash/filter'; -import map from 'lodash/map'; -import compact from 'lodash/compact'; +import { observable, action } from 'mobx'; + +const transduce = (items, mapper, reducer, initial) => + items.reduce( + (acc, item, index) => reducer(acc, mapper(item, index), index), + initial + ); class ReportStore { + @observable isLoading = true; @observable sideNavOpen = false; @observable showPassed = true; @observable showFailed = true; @observable showPending = true; @observable showSkipped = false; - @observable quickSummaryWidth = null; - @observable windowWidth = null; @observable showHooks = 'failed'; + @observable.shallow filteredSuites = []; constructor(data = {}) { + this.VERSION = '__VERSION__'; this.data = data; this.showHooksOptions = [ 'failed', 'always', 'never', 'context' ]; } - @computed get suites() { - const derived = compact(map(this.allSuites, this._mapSuites)); - return derived; - } - - @computed get mobileBreakpoint() { - return this.windowWidth < 768; - } - @action openSideNav() { this.sideNavOpen = true; } @@ -36,46 +31,62 @@ class ReportStore { } @action toggleFilter(prop) { + this.toggleIsLoading(true); this[prop] = !this[prop]; } @action setShowHooks(prop) { if (this._isValidShowHookOption(prop)) { + this.toggleIsLoading(true); this.showHooks = prop; } } - @action setQuickSummaryWidth(width) { - this.quickSummaryWidth = width; - } - - @action setWindowWidth(width) { - this.windowWidth = width; + @action toggleIsLoading(isLoading) { + this.isLoading = (isLoading !== undefined) + ? isLoading + : !this.isLoading; } - _filterHook = hook => ( - (this.showHooks === 'always') - || (this.showHooks === 'failed' && hook.fail) - || (this.showHooks === 'context' && hook.context) + _mapHook = hook => ( + ((this.showHooks === 'always') + || (this.showHooks === 'failed' && hook.fail) + || (this.showHooks === 'context' && hook.context)) + && hook ) - _mapSuites = suite => { - const suites = compact(map(suite.suites, this._mapSuites)); - const tests = filter(suite.tests, test => ( - (this.showPassed && test.pass) - || (this.showFailed && test.fail) - || (this.showPending && test.pending) - || (this.showSkipped && test.skipped) - )); + _mapTest = test => ( + ((this.showPassed && test.pass) + || (this.showFailed && test.fail) + || (this.showPending && test.pending) + || (this.showSkipped && test.skipped)) + && test + ) - const beforeHooks = filter(suite.beforeHooks, this._filterHook); - const afterHooks = filter(suite.afterHooks, this._filterHook); + _mapSuite = suite => { + const suites = suite.suites.length + ? this._getFilteredTests(suite.suites) + : []; + const tests = transduce(suite.tests, this._mapTest, this._reduceItem, []); + const beforeHooks = transduce(suite.beforeHooks, this._mapHook, this._reduceItem, []); + const afterHooks = transduce(suite.afterHooks, this._mapHook, this._reduceItem, []); return (beforeHooks.length || afterHooks.length || tests.length || suites.length) ? Object.assign({}, suite, { suites, beforeHooks, afterHooks, tests }) : null; } + _reduceItem = (acc, item) => { + if (item) { + acc.push(item); + } + return acc; + } + + _getFilteredTests = suite => ( + transduce(suite, this._mapSuite, this._reduceItem, []) + ) + _isValidOption = (property, options, selection) => { const isValid = options.indexOf(selection) >= 0; if (!isValid) { @@ -103,12 +114,19 @@ class ReportStore { this.allSuites = [ data.suites ]; this.stats = data.stats; this.enableChart = !!config.enableCharts; + this.initialLoadTimeout = 300; this.devMode = !!config.dev; } + + updateFilteredSuites(timeout = this.initialLoadTimeout) { + setTimeout(() => { + this.toggleIsLoading(false); + this.filteredSuites = this._getFilteredTests(this.allSuites); + }, timeout); + } } const reportStore = new ReportStore(); -window.reportStore = reportStore; export default reportStore; export { ReportStore }; diff --git a/src/styles/app.global.css b/src/styles/app.global.css index b9376580..0f1b1025 100644 --- a/src/styles/app.global.css +++ b/src/styles/app.global.css @@ -1,5 +1,4 @@ @import 'normalize.css'; -@import 'opinionate.css'; @import 'vars'; @import 'type'; @import '../../node_modules/highlight.js/styles/solarized-light'; @@ -138,7 +137,7 @@ pre { } .details { - padding-top: calc(var(--navbar-height) + 24); + padding-top: calc(var(--navbar-height) + 24px); } /* Z-levels */ @@ -177,7 +176,7 @@ pre { } .details { - padding-top: calc(var(--navbar-height-short) + 24); + padding-top: calc(var(--navbar-height-short) + 24px); } } diff --git a/src/styles/vars.css b/src/styles/vars.css index 49485fbc..63122566 100644 --- a/src/styles/vars.css +++ b/src/styles/vars.css @@ -3,9 +3,9 @@ --screen-md-min: 992px; --screen-lg-min: 1200px; --grid-gutter-width: 30px; - --container-sm: calc(720 + var(--grid-gutter-width)); - --container-md: calc(940 + var(--grid-gutter-width)); - --container-lg: calc(1140 + var(--grid-gutter-width)); + --container-sm: calc(720px + var(--grid-gutter-width)); + --container-md: calc(940px + var(--grid-gutter-width)); + --container-lg: calc(1140px + var(--grid-gutter-width)); --navbar-height: 122px; --navbar-height-short: 56px; --summary-height-stacked: 82px; @@ -25,7 +25,6 @@ --gray-lighter: color(var(--gray-base) tint(93.5%)); /* #eee */ --gray-lighter-faded: color(var(--gray-lighter) alpha(95%)); --gray-border: color(var(--gray-base) tint(80%)); /* #ccc */ - --grey50: #eceff1; --grey100: #f5f5f5; --grey300: #e0e0e0; diff --git a/test-functional/cases/context.js b/test-functional/cases/context.js new file mode 100644 index 00000000..b29dd7b4 --- /dev/null +++ b/test-functional/cases/context.js @@ -0,0 +1,130 @@ +const { addContext } = require('../helpers'); + +function retObj() { + return { + employees: { + employee: [ + { + id: '1', + firstName: 'Tom', + lastName: 'Cruise' + }, + { + id: '2', + firstName: 'Maria', + lastName: 'Sharapova' + }, + { + id: '3', + firstName: 'James', + lastName: 'Bond' + } + ] + } + }; +} + +describe('Master Test Suite', () => { + before('Main Before Hoook', function () { + addContext(this, 'this context is before all tests'); + }); + + after(function () { + addContext(this, 'this context is after all tests'); + }); + + describe('Test Suite with Context', () => { + it('should have text context', function (done) { + (1 + 1).should.equal(2); + addContext(this, 'this is the test context'); + done(); + }); + it('should have url context, no protocol', function (done) { + (1 + 1).should.equal(2); + addContext(this, 'www.apple.com'); + done(); + }); + it('should have url context, with protocol', function (done) { + (1 + 1).should.equal(2); + addContext(this, 'http://www.apple.com'); + done(); + }); + it('should have url context, ftp', function (done) { + (1 + 1).should.equal(2); + addContext(this, 'ftp://www.apple.com'); + done(); + }); + it('should have url context, with title', function (done) { + (1 + 1).should.equal(2); + addContext(this, { + title: 'this is a link', + value: 'www.apple.com' + }); + done(); + }); + it('should have json context', function (done) { + addContext(this, { + title: 'sample return object', + value: { employees: [] } + }); + const o = retObj(); + o.should.eql({}); + done(); + }); + it('should have array context', function (done) { + addContext(this, { + title: 'sample screenshot', + value: 'http://shushi168.com/data/out/193/37127382-random-image.png' + }); + addContext(this, { + title: 'sample return', + value: { employees: [] } + }); + (1 + 1).should.equal(2); + done(); + }); + it('should have text context - image', function (done) { + addContext(this, 'http://shushi168.com/data/out/193/37127382-random-image.png'); + (1 + 1).should.equal(2); + done(); + }); + it('should not have context', () => { + addContext(this, 'http://shushi168.com/data/out/193/37127382-random-image.png'); + (1 + 1).should.equal(2); + }); + }); + + describe('beforeEach Context', () => { + beforeEach(function () { + addContext(this, 'this is the beforeEach context'); + }); + + it('should have text context beforeEach context', function (done) { + (1 + 1).should.equal(2); + addContext(this, 'this is the test context'); + done(); + }); + it('should have url context, no protocol and beforeEach context', function (done) { + (1 + 1).should.equal(2); + addContext(this, 'www.apple.com'); + done(); + }); + }); + + describe('afterEach Context', () => { + beforeEach(function () { + addContext(this, 'this is the afterEach context'); + }); + + it('should have text context afterEach context', function (done) { + (1 + 1).should.equal(2); + addContext(this, 'this is the test context'); + done(); + }); + it('should have url context, no protocol and afterEach context', function (done) { + (1 + 1).should.equal(2); + addContext(this, 'www.apple.com'); + done(); + }); + }); +}); diff --git a/test-functional/cases/general.js b/test-functional/cases/general.js new file mode 100644 index 00000000..c90fd140 --- /dev/null +++ b/test-functional/cases/general.js @@ -0,0 +1,5 @@ +const { createSuite } = require('../helpers'); + +describe('Main Suite', () => { + createSuite(1); +}); diff --git a/test-functional/cases/longtext.js b/test-functional/cases/longtext.js new file mode 100644 index 00000000..067619b8 --- /dev/null +++ b/test-functional/cases/longtext.js @@ -0,0 +1,10 @@ +/* eslint-disable max-len */ +const chai = require('chai'); + +chai.should(); + +describe('Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam nec quam a erat volutpat rutrum. In a dapibus magna. Suspendisse interdum fermentum erat vitae varius.', () => { + it('lentesque faucibus malesuada lectus ut fringilla. Aliquam sagittis scelerisque malesuada. Fusce finibus nulla sit amet risus interdum gravida. Etiam volutpat egestas elit vel consequat.', () => { + (1 + 1).should.eql(2); + }); +}); diff --git a/test-functional/cases/nesting.js b/test-functional/cases/nesting.js new file mode 100644 index 00000000..0096c754 --- /dev/null +++ b/test-functional/cases/nesting.js @@ -0,0 +1,25 @@ +const { createTest, createBefore } = require('../helpers'); + +describe('Nesting Suites', () => { + createTest(1, { forcePass: true, forceRun: true, forceContext: true }); + + describe('Child Nested Suite', () => { + describe('Grandchild Nested Suite', () => { + createTest(2, { forceRun: true }); + }); + }); + + describe('Child Nested Suite', () => { + createTest(1, { forcePass: true, forceRun: true }); + createTest(1, { forcePend: true }); + + describe('Grandchild Nested Suite', () => { + createTest(2, { forceRun: true }); + }); + }); + + describe('Child Nested Suite', () => { + createBefore(true); + createTest(2, { forceRun: true }); + }); +}); diff --git a/test-functional/cases/request.js b/test-functional/cases/request.js new file mode 100644 index 00000000..d235b591 --- /dev/null +++ b/test-functional/cases/request.js @@ -0,0 +1,18 @@ +const axios = require('axios'); +const { addContext } = require('../helpers'); + +describe('Request', () => { + it('has request as context', function () { + return axios.get('http://ip.jsontest.com') + .then(res => { + addContext(this, { + title: 'HTTP Request', + value: res.request._header + }); + addContext(this, { + title: 'HTTP Response', + value: res.data + }); + }); + }); +}); diff --git a/test-functional/helpers.js b/test-functional/helpers.js new file mode 100644 index 00000000..aee70cb5 --- /dev/null +++ b/test-functional/helpers.js @@ -0,0 +1,90 @@ +/* eslint-disable no-console, no-undef */ +const chai = require('chai'); +const faker = require('faker'); +const addContext = require('../../mochawesome/addContext'); + +chai.should(); + +const exp = { + foo: true, + bar: false, + baz: 1 +}; + +const expWrong = { + foo: true, + bar: true, + baz: 1 +}; + +const chance = (pct, pass, fail = null) => Math.random() < pct ? pass : fail; + +const createTest = (n = 1, opts = {}) => { + const { forceContext, forceRun, forcePass, forcePend } = opts; + for (let i = 0; i < n; i += 1) { + const bool = forcePass || faker.random.boolean(); + const pend = forcePend || (forceRun ? false : chance(0.12, true, false)); + const shouldAddContext = forceContext || faker.random.boolean(); + const testMethod = pend ? xit : it; + + testMethod(`should be ${bool}`, function () { + // true.should.eql(bool); + exp.should.eql(bool ? exp : expWrong); + shouldAddContext && addContext(this, 'context'); + }); + } +}; + +const createSuite = (n, nest) => { + for (let i = 0; i < n; i += 1) { + describe('Nested Suite', () => { + createTest(faker.random.number({ min: 2, max: 5 })); + const shouldNest = chance(0.7, true, false); + if (nest || shouldNest) { + createSuite({ min: 1, max: 3 }); + } + }); + } +}; + +const createBefore = (shouldFail, title = '') => { + before(`${title}`, () => { + shouldFail + ? console.log(a) + : console.log('This is the before hook.'); + }); +}; + +const createBeforeEach = (shouldFail, title = '') => { + beforeEach(`${title}`, () => { + shouldFail + ? console.log(a) + : console.log('This is the before each hook.'); + }); +}; + +const createAfter = (shouldFail, title = '') => { + after(`${title}`, () => { + shouldFail + ? console.log(a) + : console.log('This is the after hook.'); + }); +}; + +const createAfterEach = (shouldFail, title = '') => { + afterEach(`${title}`, () => { + shouldFail + ? console.log(a) + : console.log('This is the after each hook.'); + }); +}; + +module.exports = { + addContext, + createTest, + createSuite, + createBefore, + createBeforeEach, + createAfter, + createAfterEach +}; diff --git a/test-functional/index.js b/test-functional/index.js new file mode 100644 index 00000000..ef39d1e4 --- /dev/null +++ b/test-functional/index.js @@ -0,0 +1,58 @@ +/* eslint-disable no-console */ +const fs = require('fs'); +const path = require('path'); +const Mocha = require('mocha'); + +// Instantiate a Mocha instance with mochawesome reporter +const mocha = new Mocha({ + reporter: '../mochawesome', // path relative to cwd + reporterOptions: { + dev: true, + overwrite: true + } +}); + +const testCasesDir = path.join(__dirname, 'cases'); +const args = Array.prototype.slice.call(process.argv, 2); + +const isJSFile = file => file.substr(-3) === '.js'; + +const testPaths = args.length ? args : [ testCasesDir ]; + +const addDirectoryToMocha = dir => { + fs.readdirSync(dir) + .filter(isJSFile) + .forEach(file => { + mocha.addFile( + path.join(dir, file) + ); + }); +}; + +const addFileToMocha = file => mocha.addFile(path.resolve(process.cwd(), file)); + +testPaths.forEach(testPath => { + let stats; + try { + stats = fs.statSync(testPath); + } catch (err) { + console.error(err.message); + } + + if (stats && stats.isDirectory()) { + addDirectoryToMocha(testPath); + } + if (stats && stats.isFile() && isJSFile(testPath)) { + addFileToMocha(testPath); + } +}); + +// Run the tests. +if (mocha.files.length) { + mocha.run(failures => { + // exit with non-zero status if there were failures + process.on('exit', () => process.exit(failures)); + }); +} else { + console.log('No valid test files found. Aborting test run.'); +} diff --git a/test/helper.js b/test/helper.js index 472755f6..ecb70609 100644 --- a/test/helper.js +++ b/test/helper.js @@ -1,11 +1,14 @@ +// Setup Enzyme Adapter +const enzyme = require('enzyme'); +const Adapter = require('enzyme-adapter-react-16'); + +enzyme.configure({ adapter: new Adapter() }); + // Setup path lookups const path = require('path'); require('app-module-path').addPath(__dirname); require('app-module-path').addPath(path.join(__dirname, '..', 'src')); -// Babel Runtime -require('babel-register'); - // CSS Modules Runtime const cssHook = require('css-modules-require-hook'); diff --git a/test/mocha.opts b/test/mocha.opts new file mode 100644 index 00000000..762dbea0 --- /dev/null +++ b/test/mocha.opts @@ -0,0 +1,3 @@ +--watch-extensions jsx +--require babel-register +--require test/helper.js diff --git a/test/sample-data/hooks-only.json b/test/sample-data/hooks-only.json index f90a468d..dc358ecd 100644 --- a/test/sample-data/hooks-only.json +++ b/test/sample-data/hooks-only.json @@ -67,25 +67,8 @@ "passes": [], "failures": [], "skipped": [], - "hasBeforeHooks": true, - "hasAfterHooks": true, - "hasTests": false, - "hasSuites": false, - "totalTests": 0, - "totalPasses": 0, - "totalFailures": 0, - "totalPending": 0, - "totalSkipped": 0, - "hasPasses": false, - "hasFailures": false, - "hasPending": false, - "hasSkipped": false, "duration": 0, "rootEmpty": true }, - "allTests": [], - "allPending": [], - "allPasses": [], - "allFailures": [], "copyrightYear": 2017 } \ No newline at end of file diff --git a/test/sample-data/hooks.json b/test/sample-data/hooks.json index 86e01b42..7bcbb59c 100644 --- a/test/sample-data/hooks.json +++ b/test/sample-data/hooks.json @@ -108,53 +108,7 @@ "fullFile": "/Users/adamgruber/Sites/ma-test/cases/hooks-failing.js", "passes": [], "failures": [], - "skipped": [ - { - "title": "should be true", - "fullTitle": "Hooks Nested Failed Before should be true", - "timedOut": false, - "duration": 0, - "pass": false, - "fail": false, - "pending": false, - "code": "// true.should.eql(bool);\nexp.should.eql(bool ? exp : expWrong);\nshouldAddContext && addContext(this, 'context');", - "err": {}, - "isRoot": false, - "uuid": "853afd74-a7d0-4040-af5f-42bcb5062278", - "parentUUID": "6d2c79d2-1873-4414-a704-65e3fbaf86ba", - "isHook": false, - "skipped": true - }, - { - "title": "should be true", - "fullTitle": "Hooks Nested Failed Before should be true", - "timedOut": false, - "duration": 0, - "pass": false, - "fail": false, - "pending": false, - "code": "// true.should.eql(bool);\nexp.should.eql(bool ? exp : expWrong);\nshouldAddContext && addContext(this, 'context');", - "err": {}, - "isRoot": false, - "uuid": "452ce072-d0dc-40b6-9a7c-db7fbd2b1553", - "parentUUID": "6d2c79d2-1873-4414-a704-65e3fbaf86ba", - "isHook": false, - "skipped": true - } - ], - "hasBeforeHooks": true, - "hasAfterHooks": true, - "hasTests": true, - "hasSuites": false, - "totalTests": 2, - "totalPasses": 0, - "totalFailures": 0, - "totalPending": 0, - "totalSkipped": 2, - "hasPasses": false, - "hasFailures": false, - "hasPending": false, - "hasSkipped": true, + "skipped": ["853afd74-a7d0-4040-af5f-42bcb5062278", "452ce072-d0dc-40b6-9a7c-db7fbd2b1553"], "duration": 0, "rootEmpty": false }, @@ -234,24 +188,7 @@ "skipped": false } ], - "pending": [ - { - "title": "should be false", - "fullTitle": "Hooks Nested Failed After should be false", - "timedOut": false, - "duration": 0, - "pass": false, - "fail": false, - "pending": true, - "code": "", - "err": {}, - "isRoot": false, - "uuid": "97bb36b3-e9ac-40cf-a8b0-8a82990fbf02", - "parentUUID": "df1fd56b-25be-403c-bb54-99cd41d1defd", - "isHook": false, - "skipped": false - } - ], + "pending": ["97bb36b3-e9ac-40cf-a8b0-8a82990fbf02"], "root": false, "_timeout": 2000, "file": "/cases/hooks-failing.js", @@ -297,77 +234,8 @@ ], "fullFile": "/Users/adamgruber/Sites/ma-test/cases/hooks-failing.js", "passes": [], - "failures": [ - { - "title": "should be false", - "fullTitle": "Hooks Nested Failed After should be false", - "timedOut": false, - "duration": 0, - "state": "failed", - "pass": false, - "fail": true, - "pending": false, - "code": "// true.should.eql(bool);\nexp.should.eql(bool ? exp : expWrong);\nshouldAddContext && addContext(this, 'context');", - "err": { - "operator": "to equal", - "expected": "{\n \"bar\": true\n \"baz\": 1\n \"foo\": true\n}", - "details": "at bar, A has false and B has true", - "showDiff": true, - "actual": "{\n \"bar\": false\n \"baz\": 1\n \"foo\": true\n}", - "negate": false, - "assertion": { - "obj": { - "foo": true, - "bar": false, - "baz": 1 - }, - "anyOne": false, - "negate": false, - "params": { - "operator": "to equal", - "expected": { - "foo": true, - "bar": true, - "baz": 1 - }, - "details": "at bar, A has false and B has true", - "showDiff": true, - "actual": { - "foo": true, - "bar": false, - "baz": 1 - }, - "negate": false, - "assertion": "[Circular ~.suites.suites.0.suites.1.failures.0.err.assertion]" - }, - "light": false - }, - "_message": "expected Object { foo: true, bar: false, baz: 1 } to equal Object { foo: true, bar: true, baz: 1 } (at bar, A has false and B has true)", - "generatedMessage": true, - "estack": "AssertionError: expected Object { foo: true, bar: false, baz: 1 } to equal Object { foo: true, bar: true, baz: 1 } (at bar, A has false and B has true)\n at Assertion.fail (node_modules/should/cjs/should.js:258:17)\n at Assertion.value (node_modules/should/cjs/should.js:335:19)\n at Context. (helpers.js:32:20)", - "diff": " {\n- \"bar\": false\n+ \"bar\": true\n \"baz\": 1\n \"foo\": true\n }\n" - }, - "isRoot": false, - "uuid": "181368f3-849e-47d5-a221-fb78d3eb0055", - "parentUUID": "df1fd56b-25be-403c-bb54-99cd41d1defd", - "isHook": false, - "skipped": false - } - ], + "failures": ["181368f3-849e-47d5-a221-fb78d3eb0055"], "skipped": [], - "hasBeforeHooks": true, - "hasAfterHooks": true, - "hasTests": true, - "hasSuites": false, - "totalTests": 2, - "totalPasses": 0, - "totalFailures": 1, - "totalPending": 1, - "totalSkipped": 0, - "hasPasses": false, - "hasFailures": true, - "hasPending": true, - "hasSkipped": false, "duration": 0, "rootEmpty": false } @@ -464,24 +332,7 @@ "skipped": false } ], - "pending": [ - { - "title": "should be false", - "fullTitle": "Hooks should be false", - "timedOut": false, - "duration": 0, - "pass": false, - "fail": false, - "pending": true, - "code": "", - "err": {}, - "isRoot": false, - "uuid": "842e53e0-88d3-4e03-874d-f88db310a48d", - "parentUUID": "8c5b98de-eff6-4967-98ab-1a4d1ab44164", - "isHook": false, - "skipped": false - } - ], + "pending": ["842e53e0-88d3-4e03-874d-f88db310a48d"], "root": false, "_timeout": 2000, "file": "/cases/hooks-failing.js", @@ -555,98 +406,9 @@ } ], "fullFile": "/Users/adamgruber/Sites/ma-test/cases/hooks-failing.js", - "passes": [ - { - "title": "should be true", - "fullTitle": "Hooks should be true", - "timedOut": false, - "duration": 2, - "state": "passed", - "speed": "fast", - "pass": true, - "fail": false, - "pending": false, - "context": "\"context\"", - "code": "// true.should.eql(bool);\nexp.should.eql(bool ? exp : expWrong);\nshouldAddContext && addContext(this, 'context');", - "err": {}, - "isRoot": false, - "uuid": "b4e6a7c9-1daa-4f74-9872-30a03464eab4", - "parentUUID": "8c5b98de-eff6-4967-98ab-1a4d1ab44164", - "isHook": false, - "skipped": false - } - ], - "failures": [ - { - "title": "should be false", - "fullTitle": "Hooks should be false", - "timedOut": false, - "duration": 2, - "state": "failed", - "pass": false, - "fail": true, - "pending": false, - "code": "// true.should.eql(bool);\nexp.should.eql(bool ? exp : expWrong);\nshouldAddContext && addContext(this, 'context');", - "err": { - "operator": "to equal", - "expected": "{\n \"bar\": true\n \"baz\": 1\n \"foo\": true\n}", - "details": "at bar, A has false and B has true", - "showDiff": true, - "actual": "{\n \"bar\": false\n \"baz\": 1\n \"foo\": true\n}", - "negate": false, - "assertion": { - "obj": { - "foo": true, - "bar": false, - "baz": 1 - }, - "anyOne": false, - "negate": false, - "params": { - "operator": "to equal", - "expected": { - "foo": true, - "bar": true, - "baz": 1 - }, - "details": "at bar, A has false and B has true", - "showDiff": true, - "actual": { - "foo": true, - "bar": false, - "baz": 1 - }, - "negate": false, - "assertion": "[Circular ~.suites.suites.0.failures.0.err.assertion]" - }, - "light": false - }, - "_message": "expected Object { foo: true, bar: false, baz: 1 } to equal Object { foo: true, bar: true, baz: 1 } (at bar, A has false and B has true)", - "generatedMessage": true, - "estack": "AssertionError: expected Object { foo: true, bar: false, baz: 1 } to equal Object { foo: true, bar: true, baz: 1 } (at bar, A has false and B has true)\n at Assertion.fail (node_modules/should/cjs/should.js:258:17)\n at Assertion.value (node_modules/should/cjs/should.js:335:19)\n at Context. (helpers.js:32:20)", - "diff": " {\n- \"bar\": false\n+ \"bar\": true\n \"baz\": 1\n \"foo\": true\n }\n" - }, - "isRoot": false, - "uuid": "dee59e51-592d-4b90-a48f-8cd4051bb7c4", - "parentUUID": "8c5b98de-eff6-4967-98ab-1a4d1ab44164", - "isHook": false, - "skipped": false - } - ], + "passes": ["b4e6a7c9-1daa-4f74-9872-30a03464eab4"], + "failures": ["dee59e51-592d-4b90-a48f-8cd4051bb7c4"], "skipped": [], - "hasBeforeHooks": true, - "hasAfterHooks": true, - "hasTests": true, - "hasSuites": true, - "totalTests": 3, - "totalPasses": 1, - "totalFailures": 1, - "totalPending": 1, - "totalSkipped": 0, - "hasPasses": true, - "hasFailures": true, - "hasPending": true, - "hasSkipped": false, "duration": 4, "rootEmpty": false } @@ -663,389 +425,8 @@ "passes": [], "failures": [], "skipped": [], - "hasBeforeHooks": false, - "hasAfterHooks": false, - "hasTests": false, - "hasSuites": true, - "totalTests": 0, - "totalPasses": 0, - "totalFailures": 0, - "totalPending": 0, - "totalSkipped": 0, - "hasPasses": false, - "hasFailures": false, - "hasPending": false, - "hasSkipped": false, "duration": 0, "rootEmpty": true }, - "allTests": [ - { - "title": "should be false", - "fullTitle": "Hooks should be false", - "timedOut": false, - "duration": 0, - "pass": false, - "fail": false, - "pending": true, - "code": "", - "err": {}, - "isRoot": false, - "uuid": "842e53e0-88d3-4e03-874d-f88db310a48d", - "parentUUID": "8c5b98de-eff6-4967-98ab-1a4d1ab44164", - "isHook": false, - "skipped": false - }, - { - "title": "should be true", - "fullTitle": "Hooks should be true", - "timedOut": false, - "duration": 2, - "state": "passed", - "speed": "fast", - "pass": true, - "fail": false, - "pending": false, - "context": "\"context\"", - "code": "// true.should.eql(bool);\nexp.should.eql(bool ? exp : expWrong);\nshouldAddContext && addContext(this, 'context');", - "err": {}, - "isRoot": false, - "uuid": "b4e6a7c9-1daa-4f74-9872-30a03464eab4", - "parentUUID": "8c5b98de-eff6-4967-98ab-1a4d1ab44164", - "isHook": false, - "skipped": false - }, - { - "title": "should be false", - "fullTitle": "Hooks should be false", - "timedOut": false, - "duration": 2, - "state": "failed", - "pass": false, - "fail": true, - "pending": false, - "code": "// true.should.eql(bool);\nexp.should.eql(bool ? exp : expWrong);\nshouldAddContext && addContext(this, 'context');", - "err": { - "operator": "to equal", - "expected": "{\n \"bar\": true\n \"baz\": 1\n \"foo\": true\n}", - "details": "at bar, A has false and B has true", - "showDiff": true, - "actual": "{\n \"bar\": false\n \"baz\": 1\n \"foo\": true\n}", - "negate": false, - "assertion": { - "obj": { - "foo": true, - "bar": false, - "baz": 1 - }, - "anyOne": false, - "negate": false, - "params": { - "operator": "to equal", - "expected": { - "foo": true, - "bar": true, - "baz": 1 - }, - "details": "at bar, A has false and B has true", - "showDiff": true, - "actual": { - "foo": true, - "bar": false, - "baz": 1 - }, - "negate": false, - "assertion": "[Circular ~.allTests.2.err.assertion]" - }, - "light": false - }, - "_message": "expected Object { foo: true, bar: false, baz: 1 } to equal Object { foo: true, bar: true, baz: 1 } (at bar, A has false and B has true)", - "generatedMessage": true, - "estack": "AssertionError: expected Object { foo: true, bar: false, baz: 1 } to equal Object { foo: true, bar: true, baz: 1 } (at bar, A has false and B has true)\n at Assertion.fail (node_modules/should/cjs/should.js:258:17)\n at Assertion.value (node_modules/should/cjs/should.js:335:19)\n at Context. (helpers.js:32:20)", - "diff": " {\n- \"bar\": false\n+ \"bar\": true\n \"baz\": 1\n \"foo\": true\n }\n" - }, - "isRoot": false, - "uuid": "dee59e51-592d-4b90-a48f-8cd4051bb7c4", - "parentUUID": "8c5b98de-eff6-4967-98ab-1a4d1ab44164", - "isHook": false, - "skipped": false - }, - { - "title": "should be false", - "fullTitle": "Nested Failed After should be false", - "timedOut": false, - "duration": 0, - "pass": false, - "fail": false, - "pending": true, - "code": "", - "err": {}, - "isRoot": false, - "uuid": "97bb36b3-e9ac-40cf-a8b0-8a82990fbf02", - "parentUUID": "df1fd56b-25be-403c-bb54-99cd41d1defd", - "isHook": false, - "skipped": false - }, - { - "title": "should be false", - "fullTitle": "Nested Failed After should be false", - "timedOut": false, - "duration": 0, - "state": "failed", - "pass": false, - "fail": true, - "pending": false, - "code": "// true.should.eql(bool);\nexp.should.eql(bool ? exp : expWrong);\nshouldAddContext && addContext(this, 'context');", - "err": { - "operator": "to equal", - "expected": "{\n \"bar\": true\n \"baz\": 1\n \"foo\": true\n}", - "details": "at bar, A has false and B has true", - "showDiff": true, - "actual": "{\n \"bar\": false\n \"baz\": 1\n \"foo\": true\n}", - "negate": false, - "assertion": { - "obj": { - "foo": true, - "bar": false, - "baz": 1 - }, - "anyOne": false, - "negate": false, - "params": { - "operator": "to equal", - "expected": { - "foo": true, - "bar": true, - "baz": 1 - }, - "details": "at bar, A has false and B has true", - "showDiff": true, - "actual": { - "foo": true, - "bar": false, - "baz": 1 - }, - "negate": false, - "assertion": "[Circular ~.allTests.4.err.assertion]" - }, - "light": false - }, - "_message": "expected Object { foo: true, bar: false, baz: 1 } to equal Object { foo: true, bar: true, baz: 1 } (at bar, A has false and B has true)", - "generatedMessage": true, - "estack": "AssertionError: expected Object { foo: true, bar: false, baz: 1 } to equal Object { foo: true, bar: true, baz: 1 } (at bar, A has false and B has true)\n at Assertion.fail (node_modules/should/cjs/should.js:258:17)\n at Assertion.value (node_modules/should/cjs/should.js:335:19)\n at Context. (helpers.js:32:20)", - "diff": " {\n- \"bar\": false\n+ \"bar\": true\n \"baz\": 1\n \"foo\": true\n }\n" - }, - "isRoot": false, - "uuid": "181368f3-849e-47d5-a221-fb78d3eb0055", - "parentUUID": "df1fd56b-25be-403c-bb54-99cd41d1defd", - "isHook": false, - "skipped": false - } - ], - "allPending": [ - { - "title": "should be false", - "fullTitle": "Hooks should be false", - "timedOut": false, - "duration": 0, - "pass": false, - "fail": false, - "pending": true, - "code": "", - "err": {}, - "isRoot": false, - "uuid": "842e53e0-88d3-4e03-874d-f88db310a48d", - "parentUUID": "8c5b98de-eff6-4967-98ab-1a4d1ab44164", - "isHook": false, - "skipped": false - }, - { - "title": "should be false", - "fullTitle": "Nested Failed After should be false", - "timedOut": false, - "duration": 0, - "pass": false, - "fail": false, - "pending": true, - "code": "", - "err": {}, - "isRoot": false, - "uuid": "97bb36b3-e9ac-40cf-a8b0-8a82990fbf02", - "parentUUID": "df1fd56b-25be-403c-bb54-99cd41d1defd", - "isHook": false, - "skipped": false - } - ], - "allPasses": [ - { - "title": "should be true", - "fullTitle": "Hooks should be true", - "timedOut": false, - "duration": 2, - "state": "passed", - "speed": "fast", - "pass": true, - "fail": false, - "pending": false, - "context": "\"context\"", - "code": "// true.should.eql(bool);\nexp.should.eql(bool ? exp : expWrong);\nshouldAddContext && addContext(this, 'context');", - "err": {}, - "isRoot": false, - "uuid": "b4e6a7c9-1daa-4f74-9872-30a03464eab4", - "parentUUID": "8c5b98de-eff6-4967-98ab-1a4d1ab44164", - "isHook": false, - "skipped": false - } - ], - "allFailures": [ - { - "title": "should be false", - "fullTitle": "Hooks should be false", - "timedOut": false, - "duration": 2, - "state": "failed", - "pass": false, - "fail": true, - "pending": false, - "code": "// true.should.eql(bool);\nexp.should.eql(bool ? exp : expWrong);\nshouldAddContext && addContext(this, 'context');", - "err": { - "operator": "to equal", - "expected": "{\n \"bar\": true\n \"baz\": 1\n \"foo\": true\n}", - "details": "at bar, A has false and B has true", - "showDiff": true, - "actual": "{\n \"bar\": false\n \"baz\": 1\n \"foo\": true\n}", - "negate": false, - "assertion": { - "obj": { - "foo": true, - "bar": false, - "baz": 1 - }, - "anyOne": false, - "negate": false, - "params": { - "operator": "to equal", - "expected": { - "foo": true, - "bar": true, - "baz": 1 - }, - "details": "at bar, A has false and B has true", - "showDiff": true, - "actual": { - "foo": true, - "bar": false, - "baz": 1 - }, - "negate": false, - "assertion": "[Circular ~.allFailures.0.err.assertion]" - }, - "light": false - }, - "_message": "expected Object { foo: true, bar: false, baz: 1 } to equal Object { foo: true, bar: true, baz: 1 } (at bar, A has false and B has true)", - "generatedMessage": true, - "estack": "AssertionError: expected Object { foo: true, bar: false, baz: 1 } to equal Object { foo: true, bar: true, baz: 1 } (at bar, A has false and B has true)\n at Assertion.fail (node_modules/should/cjs/should.js:258:17)\n at Assertion.value (node_modules/should/cjs/should.js:335:19)\n at Context. (helpers.js:32:20)", - "diff": " {\n- \"bar\": false\n+ \"bar\": true\n \"baz\": 1\n \"foo\": true\n }\n" - }, - "isRoot": false, - "uuid": "dee59e51-592d-4b90-a48f-8cd4051bb7c4", - "parentUUID": "8c5b98de-eff6-4967-98ab-1a4d1ab44164", - "isHook": false, - "skipped": false - }, - { - "title": "\"before all\" hook for \"should be false\"", - "fullTitle": "Nested Failed Before \"before all\" hook for \"should be false\"", - "timedOut": false, - "duration": 0, - "state": "failed", - "pass": false, - "fail": true, - "pending": false, - "code": "shouldFail\n ? console.log(a)\n : console.log('This is the before hook.');", - "err": { - "estack": "ReferenceError: a is not defined\n at Context.before (helpers.js:42:21)" - }, - "isRoot": false, - "uuid": "1e445313-1f42-413a-9518-0d970b8179f0", - "parentUUID": "6d2c79d2-1873-4414-a704-65e3fbaf86ba", - "isHook": true, - "skipped": false - }, - { - "title": "should be false", - "fullTitle": "Nested Failed After should be false", - "timedOut": false, - "duration": 0, - "state": "failed", - "pass": false, - "fail": true, - "pending": false, - "code": "// true.should.eql(bool);\nexp.should.eql(bool ? exp : expWrong);\nshouldAddContext && addContext(this, 'context');", - "err": { - "operator": "to equal", - "expected": "{\n \"bar\": true\n \"baz\": 1\n \"foo\": true\n}", - "details": "at bar, A has false and B has true", - "showDiff": true, - "actual": "{\n \"bar\": false\n \"baz\": 1\n \"foo\": true\n}", - "negate": false, - "assertion": { - "obj": { - "foo": true, - "bar": false, - "baz": 1 - }, - "anyOne": false, - "negate": false, - "params": { - "operator": "to equal", - "expected": { - "foo": true, - "bar": true, - "baz": 1 - }, - "details": "at bar, A has false and B has true", - "showDiff": true, - "actual": { - "foo": true, - "bar": false, - "baz": 1 - }, - "negate": false, - "assertion": "[Circular ~.allFailures.2.err.assertion]" - }, - "light": false - }, - "_message": "expected Object { foo: true, bar: false, baz: 1 } to equal Object { foo: true, bar: true, baz: 1 } (at bar, A has false and B has true)", - "generatedMessage": true, - "estack": "AssertionError: expected Object { foo: true, bar: false, baz: 1 } to equal Object { foo: true, bar: true, baz: 1 } (at bar, A has false and B has true)\n at Assertion.fail (node_modules/should/cjs/should.js:258:17)\n at Assertion.value (node_modules/should/cjs/should.js:335:19)\n at Context. (helpers.js:32:20)", - "diff": " {\n- \"bar\": false\n+ \"bar\": true\n \"baz\": 1\n \"foo\": true\n }\n" - }, - "isRoot": false, - "uuid": "181368f3-849e-47d5-a221-fb78d3eb0055", - "parentUUID": "df1fd56b-25be-403c-bb54-99cd41d1defd", - "isHook": false, - "skipped": false - }, - { - "title": "\"after all\" hook", - "fullTitle": "Nested Failed After \"after all\" hook", - "timedOut": false, - "duration": 0, - "state": "failed", - "pass": false, - "fail": true, - "pending": false, - "code": "shouldFail\n ? console.log(a)\n : console.log('This is the after hook.');", - "err": { - "estack": "ReferenceError: a is not defined\n at Context.after (helpers.js:58:21)" - }, - "isRoot": false, - "uuid": "a30b636f-ac1d-48a6-a992-a2ad0091484e", - "parentUUID": "df1fd56b-25be-403c-bb54-99cd41d1defd", - "isHook": true, - "skipped": false - } - ], "copyrightYear": 2017 } \ No newline at end of file diff --git a/test/sample-data/invalid.json b/test/sample-data/invalid.json index e85a7fba..0afbafb4 100644 --- a/test/sample-data/invalid.json +++ b/test/sample-data/invalid.json @@ -89,77 +89,8 @@ "afterHooks": [], "fullFile": "/Users/adamgruber/Sites/ma-test/cases/test.js", "passes": [], - "failures": [ - { - "title": "should be true", - "fullTitle": "Main Suite should be true", - "timedOut": false, - "duration": 3, - "state": "failed", - "pass": false, - "fail": true, - "pending": false, - "code": "// true.should.eql(bool);\nexp.should.eql({\n foo: true,\n bar: true,\n baz: 1\n});\nshouldAddContext && addContext(this, 'context');", - "err": { - "operator": "to equal", - "expected": "{\n \"bar\": true\n \"baz\": 1\n \"foo\": true\n}", - "details": "at bar, A has false and B has true", - "showDiff": true, - "actual": "{\n \"bar\": false\n \"baz\": 1\n \"foo\": true\n}", - "negate": false, - "assertion": { - "obj": { - "foo": true, - "bar": false, - "baz": 1 - }, - "anyOne": false, - "negate": false, - "params": { - "operator": "to equal", - "expected": { - "foo": true, - "bar": true, - "baz": 1 - }, - "details": "at bar, A has false and B has true", - "showDiff": true, - "actual": { - "foo": true, - "bar": false, - "baz": 1 - }, - "negate": false, - "assertion": "[Circular ~.suites.suites.0.failures.0.err.assertion]" - }, - "light": false - }, - "_message": "expected Object { foo: true, bar: false, baz: 1 } to equal Object { foo: true, bar: true, baz: 1 } (at bar, A has false and B has true)", - "generatedMessage": true, - "estack": "AssertionError: expected Object { foo: true, bar: false, baz: 1 } to equal Object { foo: true, bar: true, baz: 1 } (at bar, A has false and B has true)\n at Assertion.fail (node_modules/should/cjs/should.js:258:17)\n at Assertion.value (node_modules/should/cjs/should.js:335:19)\n at Context. (helpers.js:26:20)", - "diff": " {\n- \"bar\": false\n+ \"bar\": true\n \"baz\": 1\n \"foo\": true\n }\n" - }, - "isRoot": false, - "uuid": "b88d1dce-1332-4bd9-bc1a-9ce3bceb89e4", - "parentUUID": "571df514-f199-464d-aa1e-aed923a241ec", - "isHook": false, - "skipped": false - } - ], + "failures": ["b88d1dce-1332-4bd9-bc1a-9ce3bceb89e4"], "skipped": [], - "hasBeforeHooks": false, - "hasAfterHooks": false, - "hasTests": true, - "hasSuites": false, - "totalTests": 1, - "totalPasses": 0, - "totalFailures": 1, - "totalPending": 0, - "totalSkipped": 0, - "hasPasses": false, - "hasFailures": true, - "hasPending": false, - "hasSkipped": false, "duration": 3, "rootEmpty": false } @@ -176,137 +107,8 @@ "passes": [], "failures": [], "skipped": [], - "hasBeforeHooks": false, - "hasAfterHooks": false, - "hasTests": false, - "hasSuites": true, - "totalTests": 0, - "totalPasses": 0, - "totalFailures": 0, - "totalPending": 0, - "totalSkipped": 0, - "hasPasses": false, - "hasFailures": false, - "hasPending": false, - "hasSkipped": false, "duration": 0, "rootEmpty": true }, - "allTests": [ - { - "title": "should be true", - "fullTitle": "Main Suite should be true", - "timedOut": false, - "duration": 3, - "state": "failed", - "pass": false, - "fail": true, - "pending": false, - "code": "// true.should.eql(bool);\nexp.should.eql({\n foo: true,\n bar: true,\n baz: 1\n});\nshouldAddContext && addContext(this, 'context');", - "err": { - "operator": "to equal", - "expected": "{\n \"bar\": true\n \"baz\": 1\n \"foo\": true\n}", - "details": "at bar, A has false and B has true", - "showDiff": true, - "actual": "{\n \"bar\": false\n \"baz\": 1\n \"foo\": true\n}", - "negate": false, - "assertion": { - "obj": { - "foo": true, - "bar": false, - "baz": 1 - }, - "anyOne": false, - "negate": false, - "params": { - "operator": "to equal", - "expected": { - "foo": true, - "bar": true, - "baz": 1 - }, - "details": "at bar, A has false and B has true", - "showDiff": true, - "actual": { - "foo": true, - "bar": false, - "baz": 1 - }, - "negate": false, - "assertion": "[Circular ~.allTests.0.err.assertion]" - }, - "light": false - }, - "_message": "expected Object { foo: true, bar: false, baz: 1 } to equal Object { foo: true, bar: true, baz: 1 } (at bar, A has false and B has true)", - "generatedMessage": true, - "estack": "AssertionError: expected Object { foo: true, bar: false, baz: 1 } to equal Object { foo: true, bar: true, baz: 1 } (at bar, A has false and B has true)\n at Assertion.fail (node_modules/should/cjs/should.js:258:17)\n at Assertion.value (node_modules/should/cjs/should.js:335:19)\n at Context. (helpers.js:26:20)", - "diff": " {\n- \"bar\": false\n+ \"bar\": true\n \"baz\": 1\n \"foo\": true\n }\n" - }, - "isRoot": false, - "uuid": "b88d1dce-1332-4bd9-bc1a-9ce3bceb89e4", - "parentUUID": "571df514-f199-464d-aa1e-aed923a241ec", - "isHook": false, - "skipped": false - } - ], - "allPending": [], - "allPasses": [], - "allFailures": [ - { - "title": "should be true", - "fullTitle": "Main Suite should be true", - "timedOut": false, - "duration": 3, - "state": "failed", - "pass": false, - "fail": true, - "pending": false, - "code": "// true.should.eql(bool);\nexp.should.eql({\n foo: true,\n bar: true,\n baz: 1\n});\nshouldAddContext && addContext(this, 'context');", - "err": { - "operator": "to equal", - "expected": "{\n \"bar\": true\n \"baz\": 1\n \"foo\": true\n}", - "details": "at bar, A has false and B has true", - "showDiff": true, - "actual": "{\n \"bar\": false\n \"baz\": 1\n \"foo\": true\n}", - "negate": false, - "assertion": { - "obj": { - "foo": true, - "bar": false, - "baz": 1 - }, - "anyOne": false, - "negate": false, - "params": { - "operator": "to equal", - "expected": { - "foo": true, - "bar": true, - "baz": 1 - }, - "details": "at bar, A has false and B has true", - "showDiff": true, - "actual": { - "foo": true, - "bar": false, - "baz": 1 - }, - "negate": false, - "assertion": "[Circular ~.allFailures.0.err.assertion]" - }, - "light": false - }, - "_message": "expected Object { foo: true, bar: false, baz: 1 } to equal Object { foo: true, bar: true, baz: 1 } (at bar, A has false and B has true)", - "generatedMessage": true, - "estack": "AssertionError: expected Object { foo: true, bar: false, baz: 1 } to equal Object { foo: true, bar: true, baz: 1 } (at bar, A has false and B has true)\n at Assertion.fail (node_modules/should/cjs/should.js:258:17)\n at Assertion.value (node_modules/should/cjs/should.js:335:19)\n at Context. (helpers.js:26:20)", - "diff": " {\n- \"bar\": false\n+ \"bar\": true\n \"baz\": 1\n \"foo\": true\n }\n" - }, - "isRoot": false, - "uuid": "b88d1dce-1332-4bd9-bc1a-9ce3bceb89e4", - "parentUUID": "571df514-f199-464d-aa1e-aed923a241ec", - "isHook": false, - "skipped": false - } - ], "copyrightYear": 2017 } \ No newline at end of file diff --git a/test/sample-data/nested.json b/test/sample-data/nested.json index 4c624a23..cf36e959 100644 --- a/test/sample-data/nested.json +++ b/test/sample-data/nested.json @@ -114,98 +114,9 @@ "beforeHooks": [], "afterHooks": [], "fullFile": "/Users/adamgruber/Sites/ma-test/cases/nesting.js", - "passes": [ - { - "title": "should be true", - "fullTitle": "Child Nested Suite Grandchild Nested Suite should be true", - "timedOut": false, - "duration": 1, - "state": "passed", - "speed": "fast", - "pass": true, - "fail": false, - "pending": false, - "context": "\"context\"", - "code": "// true.should.eql(bool);\nexp.should.eql(bool ? exp : expWrong);\nshouldAddContext && addContext(this, 'context');", - "err": {}, - "isRoot": false, - "uuid": "b2aeb6b7-54a4-4464-8b42-fbc90d310147", - "parentUUID": "127c6acd-1fdc-44f8-8e2c-d2a26e92395e", - "isHook": false, - "skipped": false - } - ], - "failures": [ - { - "title": "should be false", - "fullTitle": "Child Nested Suite Grandchild Nested Suite should be false", - "timedOut": false, - "duration": 2, - "state": "failed", - "pass": false, - "fail": true, - "pending": false, - "code": "// true.should.eql(bool);\nexp.should.eql(bool ? exp : expWrong);\nshouldAddContext && addContext(this, 'context');", - "err": { - "operator": "to equal", - "expected": "{\n \"bar\": true\n \"baz\": 1\n \"foo\": true\n}", - "details": "at bar, A has false and B has true", - "showDiff": true, - "actual": "{\n \"bar\": false\n \"baz\": 1\n \"foo\": true\n}", - "negate": false, - "assertion": { - "obj": { - "foo": true, - "bar": false, - "baz": 1 - }, - "anyOne": false, - "negate": false, - "params": { - "operator": "to equal", - "expected": { - "foo": true, - "bar": true, - "baz": 1 - }, - "details": "at bar, A has false and B has true", - "showDiff": true, - "actual": { - "foo": true, - "bar": false, - "baz": 1 - }, - "negate": false, - "assertion": "[Circular ~.suites.suites.0.suites.0.suites.0.failures.0.err.assertion]" - }, - "light": false - }, - "_message": "expected Object { foo: true, bar: false, baz: 1 } to equal Object { foo: true, bar: true, baz: 1 } (at bar, A has false and B has true)", - "generatedMessage": true, - "estack": "AssertionError: expected Object { foo: true, bar: false, baz: 1 } to equal Object { foo: true, bar: true, baz: 1 } (at bar, A has false and B has true)\n at Assertion.fail (node_modules/should/cjs/should.js:258:17)\n at Assertion.value (node_modules/should/cjs/should.js:335:19)\n at Context. (helpers.js:32:20)", - "diff": " {\n- \"bar\": false\n+ \"bar\": true\n \"baz\": 1\n \"foo\": true\n }\n" - }, - "isRoot": false, - "uuid": "29aa4dfc-7ec9-40ad-aea6-3c65d5c94f52", - "parentUUID": "127c6acd-1fdc-44f8-8e2c-d2a26e92395e", - "isHook": false, - "skipped": false - } - ], + "passes": ["b2aeb6b7-54a4-4464-8b42-fbc90d310147"], + "failures": ["29aa4dfc-7ec9-40ad-aea6-3c65d5c94f52"], "skipped": [], - "hasBeforeHooks": false, - "hasAfterHooks": false, - "hasTests": true, - "hasSuites": false, - "totalTests": 2, - "totalPasses": 1, - "totalFailures": 1, - "totalPending": 0, - "totalSkipped": 0, - "hasPasses": true, - "hasFailures": true, - "hasPending": false, - "hasSkipped": false, "duration": 3, "rootEmpty": false } @@ -222,19 +133,6 @@ "passes": [], "failures": [], "skipped": [], - "hasBeforeHooks": false, - "hasAfterHooks": false, - "hasTests": false, - "hasSuites": true, - "totalTests": 0, - "totalPasses": 0, - "totalFailures": 0, - "totalPending": 0, - "totalSkipped": 0, - "hasPasses": false, - "hasFailures": false, - "hasPending": false, - "hasSkipped": false, "duration": 0, "rootEmpty": false }, @@ -365,132 +263,8 @@ "afterHooks": [], "fullFile": "/Users/adamgruber/Sites/ma-test/cases/nesting.js", "passes": [], - "failures": [ - { - "title": "should be false", - "fullTitle": "Child Nested Suite Grandchild Nested Suite should be false", - "timedOut": false, - "duration": 1, - "state": "failed", - "pass": false, - "fail": true, - "pending": false, - "code": "// true.should.eql(bool);\nexp.should.eql(bool ? exp : expWrong);\nshouldAddContext && addContext(this, 'context');", - "err": { - "operator": "to equal", - "expected": "{\n \"bar\": true\n \"baz\": 1\n \"foo\": true\n}", - "details": "at bar, A has false and B has true", - "showDiff": true, - "actual": "{\n \"bar\": false\n \"baz\": 1\n \"foo\": true\n}", - "negate": false, - "assertion": { - "obj": { - "foo": true, - "bar": false, - "baz": 1 - }, - "anyOne": false, - "negate": false, - "params": { - "operator": "to equal", - "expected": { - "foo": true, - "bar": true, - "baz": 1 - }, - "details": "at bar, A has false and B has true", - "showDiff": true, - "actual": { - "foo": true, - "bar": false, - "baz": 1 - }, - "negate": false, - "assertion": "[Circular ~.suites.suites.0.suites.1.suites.0.failures.0.err.assertion]" - }, - "light": false - }, - "_message": "expected Object { foo: true, bar: false, baz: 1 } to equal Object { foo: true, bar: true, baz: 1 } (at bar, A has false and B has true)", - "generatedMessage": true, - "estack": "AssertionError: expected Object { foo: true, bar: false, baz: 1 } to equal Object { foo: true, bar: true, baz: 1 } (at bar, A has false and B has true)\n at Assertion.fail (node_modules/should/cjs/should.js:258:17)\n at Assertion.value (node_modules/should/cjs/should.js:335:19)\n at Context. (helpers.js:32:20)", - "diff": " {\n- \"bar\": false\n+ \"bar\": true\n \"baz\": 1\n \"foo\": true\n }\n" - }, - "isRoot": false, - "uuid": "513a1606-965e-418e-a23b-aeb0e21e920f", - "parentUUID": "a44034bd-9968-404f-9136-0023575d142e", - "isHook": false, - "skipped": false - }, - { - "title": "should be false", - "fullTitle": "Child Nested Suite Grandchild Nested Suite should be false", - "timedOut": false, - "duration": 1, - "state": "failed", - "pass": false, - "fail": true, - "pending": false, - "code": "// true.should.eql(bool);\nexp.should.eql(bool ? exp : expWrong);\nshouldAddContext && addContext(this, 'context');", - "err": { - "operator": "to equal", - "expected": "{\n \"bar\": true\n \"baz\": 1\n \"foo\": true\n}", - "details": "at bar, A has false and B has true", - "showDiff": true, - "actual": "{\n \"bar\": false\n \"baz\": 1\n \"foo\": true\n}", - "negate": false, - "assertion": { - "obj": { - "foo": true, - "bar": false, - "baz": 1 - }, - "anyOne": false, - "negate": false, - "params": { - "operator": "to equal", - "expected": { - "foo": true, - "bar": true, - "baz": 1 - }, - "details": "at bar, A has false and B has true", - "showDiff": true, - "actual": { - "foo": true, - "bar": false, - "baz": 1 - }, - "negate": false, - "assertion": "[Circular ~.suites.suites.0.suites.1.suites.0.failures.1.err.assertion]" - }, - "light": false - }, - "_message": "expected Object { foo: true, bar: false, baz: 1 } to equal Object { foo: true, bar: true, baz: 1 } (at bar, A has false and B has true)", - "generatedMessage": true, - "estack": "AssertionError: expected Object { foo: true, bar: false, baz: 1 } to equal Object { foo: true, bar: true, baz: 1 } (at bar, A has false and B has true)\n at Assertion.fail (node_modules/should/cjs/should.js:258:17)\n at Assertion.value (node_modules/should/cjs/should.js:335:19)\n at Context. (helpers.js:32:20)", - "diff": " {\n- \"bar\": false\n+ \"bar\": true\n \"baz\": 1\n \"foo\": true\n }\n" - }, - "isRoot": false, - "uuid": "3426ab30-4c8f-48f0-ab0b-b48498b30834", - "parentUUID": "a44034bd-9968-404f-9136-0023575d142e", - "isHook": false, - "skipped": false - } - ], + "failures": ["513a1606-965e-418e-a23b-aeb0e21e920f", "3426ab30-4c8f-48f0-ab0b-b48498b30834"], "skipped": [], - "hasBeforeHooks": false, - "hasAfterHooks": false, - "hasTests": true, - "hasSuites": false, - "totalTests": 2, - "totalPasses": 0, - "totalFailures": 2, - "totalPending": 0, - "totalSkipped": 0, - "hasPasses": false, - "hasFailures": true, - "hasPending": false, - "hasSkipped": false, "duration": 2, "rootEmpty": false } @@ -531,24 +305,7 @@ "skipped": false } ], - "pending": [ - { - "title": "should be false", - "fullTitle": "Nesting Suites Child Nested Suite should be false", - "timedOut": false, - "duration": 0, - "pass": false, - "fail": false, - "pending": true, - "code": "", - "err": {}, - "isRoot": false, - "uuid": "e0a79fe3-1c89-4ff4-ab68-f218fbeac074", - "parentUUID": "4e116cd1-6a77-4c20-b1fd-307f79bbd029", - "isHook": false, - "skipped": false - } - ], + "pending": ["e0a79fe3-1c89-4ff4-ab68-f218fbeac074"], "root": false, "_timeout": 2000, "file": "/cases/nesting.js", @@ -556,41 +313,9 @@ "beforeHooks": [], "afterHooks": [], "fullFile": "/Users/adamgruber/Sites/ma-test/cases/nesting.js", - "passes": [ - { - "title": "should be true", - "fullTitle": "Nesting Suites Child Nested Suite should be true", - "timedOut": false, - "duration": 0, - "state": "passed", - "speed": "fast", - "pass": true, - "fail": false, - "pending": false, - "code": "// true.should.eql(bool);\nexp.should.eql(bool ? exp : expWrong);\nshouldAddContext && addContext(this, 'context');", - "err": {}, - "isRoot": false, - "uuid": "e92d7f04-13a1-4064-a503-4f64c9de894e", - "parentUUID": "4e116cd1-6a77-4c20-b1fd-307f79bbd029", - "isHook": false, - "skipped": false - } - ], + "passes": ["e92d7f04-13a1-4064-a503-4f64c9de894e"], "failures": [], "skipped": [], - "hasBeforeHooks": false, - "hasAfterHooks": false, - "hasTests": true, - "hasSuites": true, - "totalTests": 2, - "totalPasses": 1, - "totalFailures": 0, - "totalPending": 1, - "totalSkipped": 0, - "hasPasses": true, - "hasFailures": false, - "hasPending": true, - "hasSkipped": false, "duration": 0, "rootEmpty": false }, @@ -661,53 +386,7 @@ "fullFile": "/Users/adamgruber/Sites/ma-test/cases/nesting.js", "passes": [], "failures": [], - "skipped": [ - { - "title": "should be false", - "fullTitle": "Nesting Suites Child Nested Suite should be false", - "timedOut": false, - "duration": 0, - "pass": false, - "fail": false, - "pending": false, - "code": "// true.should.eql(bool);\nexp.should.eql(bool ? exp : expWrong);\nshouldAddContext && addContext(this, 'context');", - "err": {}, - "isRoot": false, - "uuid": "4db9d988-6c05-46b0-be9e-4d6ac89b35d7", - "parentUUID": "c50b051c-e8d5-43b7-a0bc-e374e41c4890", - "isHook": false, - "skipped": true - }, - { - "title": "should be false", - "fullTitle": "Nesting Suites Child Nested Suite should be false", - "timedOut": false, - "duration": 0, - "pass": false, - "fail": false, - "pending": false, - "code": "// true.should.eql(bool);\nexp.should.eql(bool ? exp : expWrong);\nshouldAddContext && addContext(this, 'context');", - "err": {}, - "isRoot": false, - "uuid": "81809172-7935-4bb0-82bb-9c6cec2068c5", - "parentUUID": "c50b051c-e8d5-43b7-a0bc-e374e41c4890", - "isHook": false, - "skipped": true - } - ], - "hasBeforeHooks": true, - "hasAfterHooks": false, - "hasTests": true, - "hasSuites": false, - "totalTests": 2, - "totalPasses": 0, - "totalFailures": 0, - "totalPending": 0, - "totalSkipped": 2, - "hasPasses": false, - "hasFailures": false, - "hasPending": false, - "hasSkipped": true, + "skipped": ["4db9d988-6c05-46b0-be9e-4d6ac89b35d7", "81809172-7935-4bb0-82bb-9c6cec2068c5"], "duration": 0, "rootEmpty": false } @@ -741,42 +420,9 @@ "beforeHooks": [], "afterHooks": [], "fullFile": "/Users/adamgruber/Sites/ma-test/cases/nesting.js", - "passes": [ - { - "title": "should be true", - "fullTitle": "Nesting Suites should be true", - "timedOut": false, - "duration": 2, - "state": "passed", - "speed": "fast", - "pass": true, - "fail": false, - "pending": false, - "context": "\"context\"", - "code": "// true.should.eql(bool);\nexp.should.eql(bool ? exp : expWrong);\nshouldAddContext && addContext(this, 'context');", - "err": {}, - "isRoot": false, - "uuid": "09e8a37e-8a7f-42da-a263-bd408b51a159", - "parentUUID": "1c7a4f0b-e73f-4cec-849f-b3343e047d36", - "isHook": false, - "skipped": false - } - ], + "passes": ["09e8a37e-8a7f-42da-a263-bd408b51a159"], "failures": [], "skipped": [], - "hasBeforeHooks": false, - "hasAfterHooks": false, - "hasTests": true, - "hasSuites": true, - "totalTests": 1, - "totalPasses": 1, - "totalFailures": 0, - "totalPending": 0, - "totalSkipped": 0, - "hasPasses": true, - "hasFailures": false, - "hasPending": false, - "hasSkipped": false, "duration": 2, "rootEmpty": false } @@ -793,522 +439,8 @@ "passes": [], "failures": [], "skipped": [], - "hasBeforeHooks": false, - "hasAfterHooks": false, - "hasTests": false, - "hasSuites": true, - "totalTests": 0, - "totalPasses": 0, - "totalFailures": 0, - "totalPending": 0, - "totalSkipped": 0, - "hasPasses": false, - "hasFailures": false, - "hasPending": false, - "hasSkipped": false, "duration": 0, "rootEmpty": true }, - "allTests": [ - { - "title": "should be true", - "fullTitle": "Nesting Suites should be true", - "timedOut": false, - "duration": 2, - "state": "passed", - "speed": "fast", - "pass": true, - "fail": false, - "pending": false, - "context": "\"context\"", - "code": "// true.should.eql(bool);\nexp.should.eql(bool ? exp : expWrong);\nshouldAddContext && addContext(this, 'context');", - "err": {}, - "isRoot": false, - "uuid": "09e8a37e-8a7f-42da-a263-bd408b51a159", - "parentUUID": "1c7a4f0b-e73f-4cec-849f-b3343e047d36", - "isHook": false, - "skipped": false - }, - { - "title": "should be true", - "fullTitle": "Grandchild Nested Suite should be true", - "timedOut": false, - "duration": 1, - "state": "passed", - "speed": "fast", - "pass": true, - "fail": false, - "pending": false, - "context": "\"context\"", - "code": "// true.should.eql(bool);\nexp.should.eql(bool ? exp : expWrong);\nshouldAddContext && addContext(this, 'context');", - "err": {}, - "isRoot": false, - "uuid": "b2aeb6b7-54a4-4464-8b42-fbc90d310147", - "parentUUID": "127c6acd-1fdc-44f8-8e2c-d2a26e92395e", - "isHook": false, - "skipped": false - }, - { - "title": "should be false", - "fullTitle": "Grandchild Nested Suite should be false", - "timedOut": false, - "duration": 2, - "state": "failed", - "pass": false, - "fail": true, - "pending": false, - "code": "// true.should.eql(bool);\nexp.should.eql(bool ? exp : expWrong);\nshouldAddContext && addContext(this, 'context');", - "err": { - "operator": "to equal", - "expected": "{\n \"bar\": true\n \"baz\": 1\n \"foo\": true\n}", - "details": "at bar, A has false and B has true", - "showDiff": true, - "actual": "{\n \"bar\": false\n \"baz\": 1\n \"foo\": true\n}", - "negate": false, - "assertion": { - "obj": { - "foo": true, - "bar": false, - "baz": 1 - }, - "anyOne": false, - "negate": false, - "params": { - "operator": "to equal", - "expected": { - "foo": true, - "bar": true, - "baz": 1 - }, - "details": "at bar, A has false and B has true", - "showDiff": true, - "actual": { - "foo": true, - "bar": false, - "baz": 1 - }, - "negate": false, - "assertion": "[Circular ~.allTests.2.err.assertion]" - }, - "light": false - }, - "_message": "expected Object { foo: true, bar: false, baz: 1 } to equal Object { foo: true, bar: true, baz: 1 } (at bar, A has false and B has true)", - "generatedMessage": true, - "estack": "AssertionError: expected Object { foo: true, bar: false, baz: 1 } to equal Object { foo: true, bar: true, baz: 1 } (at bar, A has false and B has true)\n at Assertion.fail (node_modules/should/cjs/should.js:258:17)\n at Assertion.value (node_modules/should/cjs/should.js:335:19)\n at Context. (helpers.js:32:20)", - "diff": " {\n- \"bar\": false\n+ \"bar\": true\n \"baz\": 1\n \"foo\": true\n }\n" - }, - "isRoot": false, - "uuid": "29aa4dfc-7ec9-40ad-aea6-3c65d5c94f52", - "parentUUID": "127c6acd-1fdc-44f8-8e2c-d2a26e92395e", - "isHook": false, - "skipped": false - }, - { - "title": "should be true", - "fullTitle": "Child Nested Suite should be true", - "timedOut": false, - "duration": 0, - "state": "passed", - "speed": "fast", - "pass": true, - "fail": false, - "pending": false, - "code": "// true.should.eql(bool);\nexp.should.eql(bool ? exp : expWrong);\nshouldAddContext && addContext(this, 'context');", - "err": {}, - "isRoot": false, - "uuid": "e92d7f04-13a1-4064-a503-4f64c9de894e", - "parentUUID": "4e116cd1-6a77-4c20-b1fd-307f79bbd029", - "isHook": false, - "skipped": false - }, - { - "title": "should be false", - "fullTitle": "Child Nested Suite should be false", - "timedOut": false, - "duration": 0, - "pass": false, - "fail": false, - "pending": true, - "code": "", - "err": {}, - "isRoot": false, - "uuid": "e0a79fe3-1c89-4ff4-ab68-f218fbeac074", - "parentUUID": "4e116cd1-6a77-4c20-b1fd-307f79bbd029", - "isHook": false, - "skipped": false - }, - { - "title": "should be false", - "fullTitle": "Grandchild Nested Suite should be false", - "timedOut": false, - "duration": 1, - "state": "failed", - "pass": false, - "fail": true, - "pending": false, - "code": "// true.should.eql(bool);\nexp.should.eql(bool ? exp : expWrong);\nshouldAddContext && addContext(this, 'context');", - "err": { - "operator": "to equal", - "expected": "{\n \"bar\": true\n \"baz\": 1\n \"foo\": true\n}", - "details": "at bar, A has false and B has true", - "showDiff": true, - "actual": "{\n \"bar\": false\n \"baz\": 1\n \"foo\": true\n}", - "negate": false, - "assertion": { - "obj": { - "foo": true, - "bar": false, - "baz": 1 - }, - "anyOne": false, - "negate": false, - "params": { - "operator": "to equal", - "expected": { - "foo": true, - "bar": true, - "baz": 1 - }, - "details": "at bar, A has false and B has true", - "showDiff": true, - "actual": { - "foo": true, - "bar": false, - "baz": 1 - }, - "negate": false, - "assertion": "[Circular ~.allTests.5.err.assertion]" - }, - "light": false - }, - "_message": "expected Object { foo: true, bar: false, baz: 1 } to equal Object { foo: true, bar: true, baz: 1 } (at bar, A has false and B has true)", - "generatedMessage": true, - "estack": "AssertionError: expected Object { foo: true, bar: false, baz: 1 } to equal Object { foo: true, bar: true, baz: 1 } (at bar, A has false and B has true)\n at Assertion.fail (node_modules/should/cjs/should.js:258:17)\n at Assertion.value (node_modules/should/cjs/should.js:335:19)\n at Context. (helpers.js:32:20)", - "diff": " {\n- \"bar\": false\n+ \"bar\": true\n \"baz\": 1\n \"foo\": true\n }\n" - }, - "isRoot": false, - "uuid": "513a1606-965e-418e-a23b-aeb0e21e920f", - "parentUUID": "a44034bd-9968-404f-9136-0023575d142e", - "isHook": false, - "skipped": false - }, - { - "title": "should be false", - "fullTitle": "Grandchild Nested Suite should be false", - "timedOut": false, - "duration": 1, - "state": "failed", - "pass": false, - "fail": true, - "pending": false, - "code": "// true.should.eql(bool);\nexp.should.eql(bool ? exp : expWrong);\nshouldAddContext && addContext(this, 'context');", - "err": { - "operator": "to equal", - "expected": "{\n \"bar\": true\n \"baz\": 1\n \"foo\": true\n}", - "details": "at bar, A has false and B has true", - "showDiff": true, - "actual": "{\n \"bar\": false\n \"baz\": 1\n \"foo\": true\n}", - "negate": false, - "assertion": { - "obj": { - "foo": true, - "bar": false, - "baz": 1 - }, - "anyOne": false, - "negate": false, - "params": { - "operator": "to equal", - "expected": { - "foo": true, - "bar": true, - "baz": 1 - }, - "details": "at bar, A has false and B has true", - "showDiff": true, - "actual": { - "foo": true, - "bar": false, - "baz": 1 - }, - "negate": false, - "assertion": "[Circular ~.allTests.6.err.assertion]" - }, - "light": false - }, - "_message": "expected Object { foo: true, bar: false, baz: 1 } to equal Object { foo: true, bar: true, baz: 1 } (at bar, A has false and B has true)", - "generatedMessage": true, - "estack": "AssertionError: expected Object { foo: true, bar: false, baz: 1 } to equal Object { foo: true, bar: true, baz: 1 } (at bar, A has false and B has true)\n at Assertion.fail (node_modules/should/cjs/should.js:258:17)\n at Assertion.value (node_modules/should/cjs/should.js:335:19)\n at Context. (helpers.js:32:20)", - "diff": " {\n- \"bar\": false\n+ \"bar\": true\n \"baz\": 1\n \"foo\": true\n }\n" - }, - "isRoot": false, - "uuid": "3426ab30-4c8f-48f0-ab0b-b48498b30834", - "parentUUID": "a44034bd-9968-404f-9136-0023575d142e", - "isHook": false, - "skipped": false - } - ], - "allPending": [ - { - "title": "should be false", - "fullTitle": "Child Nested Suite should be false", - "timedOut": false, - "duration": 0, - "pass": false, - "fail": false, - "pending": true, - "code": "", - "err": {}, - "isRoot": false, - "uuid": "e0a79fe3-1c89-4ff4-ab68-f218fbeac074", - "parentUUID": "4e116cd1-6a77-4c20-b1fd-307f79bbd029", - "isHook": false, - "skipped": false - } - ], - "allPasses": [ - { - "title": "should be true", - "fullTitle": "Nesting Suites should be true", - "timedOut": false, - "duration": 2, - "state": "passed", - "speed": "fast", - "pass": true, - "fail": false, - "pending": false, - "context": "\"context\"", - "code": "// true.should.eql(bool);\nexp.should.eql(bool ? exp : expWrong);\nshouldAddContext && addContext(this, 'context');", - "err": {}, - "isRoot": false, - "uuid": "09e8a37e-8a7f-42da-a263-bd408b51a159", - "parentUUID": "1c7a4f0b-e73f-4cec-849f-b3343e047d36", - "isHook": false, - "skipped": false - }, - { - "title": "should be true", - "fullTitle": "Grandchild Nested Suite should be true", - "timedOut": false, - "duration": 1, - "state": "passed", - "speed": "fast", - "pass": true, - "fail": false, - "pending": false, - "context": "\"context\"", - "code": "// true.should.eql(bool);\nexp.should.eql(bool ? exp : expWrong);\nshouldAddContext && addContext(this, 'context');", - "err": {}, - "isRoot": false, - "uuid": "b2aeb6b7-54a4-4464-8b42-fbc90d310147", - "parentUUID": "127c6acd-1fdc-44f8-8e2c-d2a26e92395e", - "isHook": false, - "skipped": false - }, - { - "title": "should be true", - "fullTitle": "Child Nested Suite should be true", - "timedOut": false, - "duration": 0, - "state": "passed", - "speed": "fast", - "pass": true, - "fail": false, - "pending": false, - "code": "// true.should.eql(bool);\nexp.should.eql(bool ? exp : expWrong);\nshouldAddContext && addContext(this, 'context');", - "err": {}, - "isRoot": false, - "uuid": "e92d7f04-13a1-4064-a503-4f64c9de894e", - "parentUUID": "4e116cd1-6a77-4c20-b1fd-307f79bbd029", - "isHook": false, - "skipped": false - } - ], - "allFailures": [ - { - "title": "should be false", - "fullTitle": "Grandchild Nested Suite should be false", - "timedOut": false, - "duration": 2, - "state": "failed", - "pass": false, - "fail": true, - "pending": false, - "code": "// true.should.eql(bool);\nexp.should.eql(bool ? exp : expWrong);\nshouldAddContext && addContext(this, 'context');", - "err": { - "operator": "to equal", - "expected": "{\n \"bar\": true\n \"baz\": 1\n \"foo\": true\n}", - "details": "at bar, A has false and B has true", - "showDiff": true, - "actual": "{\n \"bar\": false\n \"baz\": 1\n \"foo\": true\n}", - "negate": false, - "assertion": { - "obj": { - "foo": true, - "bar": false, - "baz": 1 - }, - "anyOne": false, - "negate": false, - "params": { - "operator": "to equal", - "expected": { - "foo": true, - "bar": true, - "baz": 1 - }, - "details": "at bar, A has false and B has true", - "showDiff": true, - "actual": { - "foo": true, - "bar": false, - "baz": 1 - }, - "negate": false, - "assertion": "[Circular ~.allFailures.0.err.assertion]" - }, - "light": false - }, - "_message": "expected Object { foo: true, bar: false, baz: 1 } to equal Object { foo: true, bar: true, baz: 1 } (at bar, A has false and B has true)", - "generatedMessage": true, - "estack": "AssertionError: expected Object { foo: true, bar: false, baz: 1 } to equal Object { foo: true, bar: true, baz: 1 } (at bar, A has false and B has true)\n at Assertion.fail (node_modules/should/cjs/should.js:258:17)\n at Assertion.value (node_modules/should/cjs/should.js:335:19)\n at Context. (helpers.js:32:20)", - "diff": " {\n- \"bar\": false\n+ \"bar\": true\n \"baz\": 1\n \"foo\": true\n }\n" - }, - "isRoot": false, - "uuid": "29aa4dfc-7ec9-40ad-aea6-3c65d5c94f52", - "parentUUID": "127c6acd-1fdc-44f8-8e2c-d2a26e92395e", - "isHook": false, - "skipped": false - }, - { - "title": "should be false", - "fullTitle": "Grandchild Nested Suite should be false", - "timedOut": false, - "duration": 1, - "state": "failed", - "pass": false, - "fail": true, - "pending": false, - "code": "// true.should.eql(bool);\nexp.should.eql(bool ? exp : expWrong);\nshouldAddContext && addContext(this, 'context');", - "err": { - "operator": "to equal", - "expected": "{\n \"bar\": true\n \"baz\": 1\n \"foo\": true\n}", - "details": "at bar, A has false and B has true", - "showDiff": true, - "actual": "{\n \"bar\": false\n \"baz\": 1\n \"foo\": true\n}", - "negate": false, - "assertion": { - "obj": { - "foo": true, - "bar": false, - "baz": 1 - }, - "anyOne": false, - "negate": false, - "params": { - "operator": "to equal", - "expected": { - "foo": true, - "bar": true, - "baz": 1 - }, - "details": "at bar, A has false and B has true", - "showDiff": true, - "actual": { - "foo": true, - "bar": false, - "baz": 1 - }, - "negate": false, - "assertion": "[Circular ~.allFailures.1.err.assertion]" - }, - "light": false - }, - "_message": "expected Object { foo: true, bar: false, baz: 1 } to equal Object { foo: true, bar: true, baz: 1 } (at bar, A has false and B has true)", - "generatedMessage": true, - "estack": "AssertionError: expected Object { foo: true, bar: false, baz: 1 } to equal Object { foo: true, bar: true, baz: 1 } (at bar, A has false and B has true)\n at Assertion.fail (node_modules/should/cjs/should.js:258:17)\n at Assertion.value (node_modules/should/cjs/should.js:335:19)\n at Context. (helpers.js:32:20)", - "diff": " {\n- \"bar\": false\n+ \"bar\": true\n \"baz\": 1\n \"foo\": true\n }\n" - }, - "isRoot": false, - "uuid": "513a1606-965e-418e-a23b-aeb0e21e920f", - "parentUUID": "a44034bd-9968-404f-9136-0023575d142e", - "isHook": false, - "skipped": false - }, - { - "title": "should be false", - "fullTitle": "Grandchild Nested Suite should be false", - "timedOut": false, - "duration": 1, - "state": "failed", - "pass": false, - "fail": true, - "pending": false, - "code": "// true.should.eql(bool);\nexp.should.eql(bool ? exp : expWrong);\nshouldAddContext && addContext(this, 'context');", - "err": { - "operator": "to equal", - "expected": "{\n \"bar\": true\n \"baz\": 1\n \"foo\": true\n}", - "details": "at bar, A has false and B has true", - "showDiff": true, - "actual": "{\n \"bar\": false\n \"baz\": 1\n \"foo\": true\n}", - "negate": false, - "assertion": { - "obj": { - "foo": true, - "bar": false, - "baz": 1 - }, - "anyOne": false, - "negate": false, - "params": { - "operator": "to equal", - "expected": { - "foo": true, - "bar": true, - "baz": 1 - }, - "details": "at bar, A has false and B has true", - "showDiff": true, - "actual": { - "foo": true, - "bar": false, - "baz": 1 - }, - "negate": false, - "assertion": "[Circular ~.allFailures.2.err.assertion]" - }, - "light": false - }, - "_message": "expected Object { foo: true, bar: false, baz: 1 } to equal Object { foo: true, bar: true, baz: 1 } (at bar, A has false and B has true)", - "generatedMessage": true, - "estack": "AssertionError: expected Object { foo: true, bar: false, baz: 1 } to equal Object { foo: true, bar: true, baz: 1 } (at bar, A has false and B has true)\n at Assertion.fail (node_modules/should/cjs/should.js:258:17)\n at Assertion.value (node_modules/should/cjs/should.js:335:19)\n at Context. (helpers.js:32:20)", - "diff": " {\n- \"bar\": false\n+ \"bar\": true\n \"baz\": 1\n \"foo\": true\n }\n" - }, - "isRoot": false, - "uuid": "3426ab30-4c8f-48f0-ab0b-b48498b30834", - "parentUUID": "a44034bd-9968-404f-9136-0023575d142e", - "isHook": false, - "skipped": false - }, - { - "title": "\"before all\" hook", - "fullTitle": "Child Nested Suite \"before all\" hook", - "timedOut": false, - "duration": 0, - "state": "failed", - "pass": false, - "fail": true, - "pending": false, - "code": "shouldFail\n ? console.log(a)\n : console.log('This is the before hook.');", - "err": { - "estack": "ReferenceError: a is not defined\n at Context.before (helpers.js:42:21)" - }, - "isRoot": false, - "uuid": "3508ce3c-5511-450c-8585-04f501d3014e", - "parentUUID": "c50b051c-e8d5-43b7-a0bc-e374e41c4890", - "isHook": true, - "skipped": false - } - ], "copyrightYear": 2017 } \ No newline at end of file diff --git a/test/sample-data/single.json b/test/sample-data/single.json index 5c34d74d..506fbc45 100644 --- a/test/sample-data/single.json +++ b/test/sample-data/single.json @@ -90,77 +90,8 @@ "afterHooks": [], "fullFile": "/Users/adamgruber/Sites/ma-test/cases/test.js", "passes": [], - "failures": [ - { - "title": "should be false", - "fullTitle": "Main Suite should be false", - "timedOut": false, - "duration": 4, - "state": "failed", - "pass": false, - "fail": true, - "pending": false, - "code": "// true.should.eql(bool);\nexp.should.eql({\n foo: true,\n bar: true,\n baz: 1\n});\nshouldAddContext && addContext(this, 'context');", - "err": { - "operator": "to equal", - "expected": "{\n \"bar\": true\n \"baz\": 1\n \"foo\": true\n}", - "details": "at bar, A has false and B has true", - "showDiff": true, - "actual": "{\n \"bar\": false\n \"baz\": 1\n \"foo\": true\n}", - "negate": false, - "assertion": { - "obj": { - "foo": true, - "bar": false, - "baz": 1 - }, - "anyOne": false, - "negate": false, - "params": { - "operator": "to equal", - "expected": { - "foo": true, - "bar": true, - "baz": 1 - }, - "details": "at bar, A has false and B has true", - "showDiff": true, - "actual": { - "foo": true, - "bar": false, - "baz": 1 - }, - "negate": false, - "assertion": "[Circular ~.suites.suites.0.failures.0.err.assertion]" - }, - "light": false - }, - "_message": "expected Object { foo: true, bar: false, baz: 1 } to equal Object { foo: true, bar: true, baz: 1 } (at bar, A has false and B has true)", - "generatedMessage": true, - "estack": "AssertionError: expected Object { foo: true, bar: false, baz: 1 } to equal Object { foo: true, bar: true, baz: 1 } (at bar, A has false and B has true)\n at Assertion.fail (node_modules/should/cjs/should.js:258:17)\n at Assertion.value (node_modules/should/cjs/should.js:335:19)\n at Context. (helpers.js:26:20)", - "diff": " {\n- \"bar\": false\n+ \"bar\": true\n \"baz\": 1\n \"foo\": true\n }\n" - }, - "isRoot": false, - "uuid": "65ef9d51-2e7b-4155-9d46-af6ad7d0e2d4", - "parentUUID": "37685726-1c47-4e1f-a7d4-6c88ebf842f9", - "isHook": false, - "skipped": false - } - ], + "failures": ["65ef9d51-2e7b-4155-9d46-af6ad7d0e2d4"], "skipped": [], - "hasBeforeHooks": false, - "hasAfterHooks": false, - "hasTests": true, - "hasSuites": false, - "totalTests": 1, - "totalPasses": 0, - "totalFailures": 1, - "totalPending": 0, - "totalSkipped": 0, - "hasPasses": false, - "hasFailures": true, - "hasPending": false, - "hasSkipped": false, "duration": 4, "rootEmpty": false } @@ -177,137 +108,8 @@ "passes": [], "failures": [], "skipped": [], - "hasBeforeHooks": false, - "hasAfterHooks": false, - "hasTests": false, - "hasSuites": true, - "totalTests": 0, - "totalPasses": 0, - "totalFailures": 0, - "totalPending": 0, - "totalSkipped": 0, - "hasPasses": false, - "hasFailures": false, - "hasPending": false, - "hasSkipped": false, "duration": 0, "rootEmpty": true }, - "allTests": [ - { - "title": "should be false", - "fullTitle": "Main Suite should be false", - "timedOut": false, - "duration": 4, - "state": "failed", - "pass": false, - "fail": true, - "pending": false, - "code": "// true.should.eql(bool);\nexp.should.eql({\n foo: true,\n bar: true,\n baz: 1\n});\nshouldAddContext && addContext(this, 'context');", - "err": { - "operator": "to equal", - "expected": "{\n \"bar\": true\n \"baz\": 1\n \"foo\": true\n}", - "details": "at bar, A has false and B has true", - "showDiff": true, - "actual": "{\n \"bar\": false\n \"baz\": 1\n \"foo\": true\n}", - "negate": false, - "assertion": { - "obj": { - "foo": true, - "bar": false, - "baz": 1 - }, - "anyOne": false, - "negate": false, - "params": { - "operator": "to equal", - "expected": { - "foo": true, - "bar": true, - "baz": 1 - }, - "details": "at bar, A has false and B has true", - "showDiff": true, - "actual": { - "foo": true, - "bar": false, - "baz": 1 - }, - "negate": false, - "assertion": "[Circular ~.allTests.0.err.assertion]" - }, - "light": false - }, - "_message": "expected Object { foo: true, bar: false, baz: 1 } to equal Object { foo: true, bar: true, baz: 1 } (at bar, A has false and B has true)", - "generatedMessage": true, - "estack": "AssertionError: expected Object { foo: true, bar: false, baz: 1 } to equal Object { foo: true, bar: true, baz: 1 } (at bar, A has false and B has true)\n at Assertion.fail (node_modules/should/cjs/should.js:258:17)\n at Assertion.value (node_modules/should/cjs/should.js:335:19)\n at Context. (helpers.js:26:20)", - "diff": " {\n- \"bar\": false\n+ \"bar\": true\n \"baz\": 1\n \"foo\": true\n }\n" - }, - "isRoot": false, - "uuid": "65ef9d51-2e7b-4155-9d46-af6ad7d0e2d4", - "parentUUID": "37685726-1c47-4e1f-a7d4-6c88ebf842f9", - "isHook": false, - "skipped": false - } - ], - "allPending": [], - "allPasses": [], - "allFailures": [ - { - "title": "should be false", - "fullTitle": "Main Suite should be false", - "timedOut": false, - "duration": 4, - "state": "failed", - "pass": false, - "fail": true, - "pending": false, - "code": "// true.should.eql(bool);\nexp.should.eql({\n foo: true,\n bar: true,\n baz: 1\n});\nshouldAddContext && addContext(this, 'context');", - "err": { - "operator": "to equal", - "expected": "{\n \"bar\": true\n \"baz\": 1\n \"foo\": true\n}", - "details": "at bar, A has false and B has true", - "showDiff": true, - "actual": "{\n \"bar\": false\n \"baz\": 1\n \"foo\": true\n}", - "negate": false, - "assertion": { - "obj": { - "foo": true, - "bar": false, - "baz": 1 - }, - "anyOne": false, - "negate": false, - "params": { - "operator": "to equal", - "expected": { - "foo": true, - "bar": true, - "baz": 1 - }, - "details": "at bar, A has false and B has true", - "showDiff": true, - "actual": { - "foo": true, - "bar": false, - "baz": 1 - }, - "negate": false, - "assertion": "[Circular ~.allFailures.0.err.assertion]" - }, - "light": false - }, - "_message": "expected Object { foo: true, bar: false, baz: 1 } to equal Object { foo: true, bar: true, baz: 1 } (at bar, A has false and B has true)", - "generatedMessage": true, - "estack": "AssertionError: expected Object { foo: true, bar: false, baz: 1 } to equal Object { foo: true, bar: true, baz: 1 } (at bar, A has false and B has true)\n at Assertion.fail (node_modules/should/cjs/should.js:258:17)\n at Assertion.value (node_modules/should/cjs/should.js:335:19)\n at Context. (helpers.js:26:20)", - "diff": " {\n- \"bar\": false\n+ \"bar\": true\n \"baz\": 1\n \"foo\": true\n }\n" - }, - "isRoot": false, - "uuid": "65ef9d51-2e7b-4155-9d46-af6ad7d0e2d4", - "parentUUID": "37685726-1c47-4e1f-a7d4-6c88ebf842f9", - "isHook": false, - "skipped": false - } - ], "copyrightYear": 2017 } \ No newline at end of file diff --git a/test/sample-data/suite.json b/test/sample-data/suite.json index edceb6b4..5aa0bd0b 100644 --- a/test/sample-data/suite.json +++ b/test/sample-data/suite.json @@ -49,60 +49,8 @@ "file": "/test-functional/test.js", "uuid": "17bc6546-127d-4fc2-84b7-4aa033a8d2d3", "fullFile": "/Users/adamgruber/Sites/mochawesome/test-functional/test.js", - "passes": [{ - "title": "should be passing test if true is not false", - "fullTitle": "Master Test Suite Test Suite - Basic should be passing test if true is not false", - "timedOut": false, - "duration": 1, - "state": "passed", - "speed": "medium", - "pass": true, - "fail": false, - "pending": false, - "code": "true.should.be.ok;\ndone();", - "err": {}, - "isRoot": false, - "uuid": "5c3ec47f-307f-4a35-b77a-d0d218acbc4d", - "parentUUID": "17bc6546-127d-4fc2-84b7-4aa033a8d2d3", - "skipped": false - }], - "failures": [{ - "title": "should fail when returned object does not match expected object", - "fullTitle": "Master Test Suite Test Suite - Basic should fail when returned object does not match expected object", - "timedOut": false, - "duration": 11, - "state": "failed", - "pass": false, - "fail": true, - "pending": false, - "code": "var o = retObj();\no.should.eql({});\ndone();", - "err": { - "name": "AssertionError", - "actual": "{\n \"employees\": {\n \"employee\": [\n {\n \"firstName\": \"Tom\"\n \"id\": \"1\"\n \"lastName\": \"Cruise\"\n }\n {\n \"firstName\": \"Maria\"\n \"id\": \"2\"\n \"lastName\": \"Sharapova\"\n }\n {\n \"firstName\": \"James\"\n \"id\": \"3\"\n \"lastName\": \"Bond\"\n }\n ]\n }\n}", - "expected": "{}", - "operator": "to equal", - "message": "expected { employees: \n { employee: \n [ { id: '1', firstName: 'Tom', lastName: 'Cruise' },\n { id: '2', firstName: 'Maria', lastName: 'Sharapova' },\n { id: '3', firstName: 'James', lastName: 'Bond' } ] } } to equal {}", - "generatedMessage": true, - "showDiff": true, - "estack": "AssertionError: expected { employees: \n { employee: \n [ { id: '1', firstName: 'Tom', lastName: 'Cruise' },\n { id: '2', firstName: 'Maria', lastName: 'Sharapova' },\n { id: '3', firstName: 'James', lastName: 'Bond' } ] } } to equal {}\n at Assertion.prop.(anonymous function) (node_modules/should/lib/should.js:61:14)\n at Context. (test-functional/test.js:38:16)", - "diff": "- {\n- \"employees\": {\n- \"employee\": [\n- {\n- \"firstName\": \"Tom\"\n- \"id\": \"1\"\n- \"lastName\": \"Cruise\"\n- }\n- {\n- \"firstName\": \"Maria\"\n- \"id\": \"2\"\n- \"lastName\": \"Sharapova\"\n- }\n- {\n- \"firstName\": \"James\"\n- \"id\": \"3\"\n- \"lastName\": \"Bond\"\n- }\n- ]\n- }\n- }\n+ {}\n" - }, - "isRoot": false, - "uuid": "9624c695-05c7-4042-9cda-e251d27be2fe", - "parentUUID": "17bc6546-127d-4fc2-84b7-4aa033a8d2d3", - "skipped": false - }], + "passes": ["5c3ec47f-307f-4a35-b77a-d0d218acbc4d"], + "failures": ["9624c695-05c7-4042-9cda-e251d27be2fe"], "skipped": [], - "hasTests": true, - "hasSuites": false, - "totalTests": 2, - "totalPasses": 1, - "totalFailures": 1, - "totalPending": 0, - "totalSkipped": 0, - "hasPasses": true, - "hasFailures": true, - "hasPending": false, - "hasSkipped": false, "duration": 12 } \ No newline at end of file diff --git a/test/sample-data/test.json b/test/sample-data/test.json index d97a8826..efe178d3 100644 --- a/test/sample-data/test.json +++ b/test/sample-data/test.json @@ -90,77 +90,8 @@ "afterHooks": [], "fullFile": "/Users/adamgruber/Sites/ma-test/cases/test.js", "passes": [], - "failures": [ - { - "title": "should be true", - "fullTitle": "Main Suite should be true", - "timedOut": false, - "duration": 3, - "state": "failed", - "pass": false, - "fail": true, - "pending": false, - "code": "// true.should.eql(bool);\nexp.should.eql({\n foo: true,\n bar: true,\n baz: 1\n});\nshouldAddContext && addContext(this, 'context');", - "err": { - "operator": "to equal", - "expected": "{\n \"bar\": true\n \"baz\": 1\n \"foo\": true\n}", - "details": "at bar, A has false and B has true", - "showDiff": true, - "actual": "{\n \"bar\": false\n \"baz\": 1\n \"foo\": true\n}", - "negate": false, - "assertion": { - "obj": { - "foo": true, - "bar": false, - "baz": 1 - }, - "anyOne": false, - "negate": false, - "params": { - "operator": "to equal", - "expected": { - "foo": true, - "bar": true, - "baz": 1 - }, - "details": "at bar, A has false and B has true", - "showDiff": true, - "actual": { - "foo": true, - "bar": false, - "baz": 1 - }, - "negate": false, - "assertion": "[Circular ~.suites.suites.0.failures.0.err.assertion]" - }, - "light": false - }, - "_message": "expected Object { foo: true, bar: false, baz: 1 } to equal Object { foo: true, bar: true, baz: 1 } (at bar, A has false and B has true)", - "generatedMessage": true, - "estack": "AssertionError: expected Object { foo: true, bar: false, baz: 1 } to equal Object { foo: true, bar: true, baz: 1 } (at bar, A has false and B has true)\n at Assertion.fail (node_modules/should/cjs/should.js:258:17)\n at Assertion.value (node_modules/should/cjs/should.js:335:19)\n at Context. (helpers.js:26:20)", - "diff": " {\n- \"bar\": false\n+ \"bar\": true\n \"baz\": 1\n \"foo\": true\n }\n" - }, - "isRoot": false, - "uuid": "b88d1dce-1332-4bd9-bc1a-9ce3bceb89e4", - "parentUUID": "571df514-f199-464d-aa1e-aed923a241ec", - "isHook": false, - "skipped": false - } - ], + "failures": ["b88d1dce-1332-4bd9-bc1a-9ce3bceb89e4"], "skipped": [], - "hasBeforeHooks": false, - "hasAfterHooks": false, - "hasTests": true, - "hasSuites": false, - "totalTests": 1, - "totalPasses": 0, - "totalFailures": 1, - "totalPending": 0, - "totalSkipped": 0, - "hasPasses": false, - "hasFailures": true, - "hasPending": false, - "hasSkipped": false, "duration": 3, "rootEmpty": false } @@ -177,137 +108,8 @@ "passes": [], "failures": [], "skipped": [], - "hasBeforeHooks": false, - "hasAfterHooks": false, - "hasTests": false, - "hasSuites": true, - "totalTests": 0, - "totalPasses": 0, - "totalFailures": 0, - "totalPending": 0, - "totalSkipped": 0, - "hasPasses": false, - "hasFailures": false, - "hasPending": false, - "hasSkipped": false, "duration": 0, "rootEmpty": true }, - "allTests": [ - { - "title": "should be true", - "fullTitle": "Main Suite should be true", - "timedOut": false, - "duration": 3, - "state": "failed", - "pass": false, - "fail": true, - "pending": false, - "code": "// true.should.eql(bool);\nexp.should.eql({\n foo: true,\n bar: true,\n baz: 1\n});\nshouldAddContext && addContext(this, 'context');", - "err": { - "operator": "to equal", - "expected": "{\n \"bar\": true\n \"baz\": 1\n \"foo\": true\n}", - "details": "at bar, A has false and B has true", - "showDiff": true, - "actual": "{\n \"bar\": false\n \"baz\": 1\n \"foo\": true\n}", - "negate": false, - "assertion": { - "obj": { - "foo": true, - "bar": false, - "baz": 1 - }, - "anyOne": false, - "negate": false, - "params": { - "operator": "to equal", - "expected": { - "foo": true, - "bar": true, - "baz": 1 - }, - "details": "at bar, A has false and B has true", - "showDiff": true, - "actual": { - "foo": true, - "bar": false, - "baz": 1 - }, - "negate": false, - "assertion": "[Circular ~.allTests.0.err.assertion]" - }, - "light": false - }, - "_message": "expected Object { foo: true, bar: false, baz: 1 } to equal Object { foo: true, bar: true, baz: 1 } (at bar, A has false and B has true)", - "generatedMessage": true, - "estack": "AssertionError: expected Object { foo: true, bar: false, baz: 1 } to equal Object { foo: true, bar: true, baz: 1 } (at bar, A has false and B has true)\n at Assertion.fail (node_modules/should/cjs/should.js:258:17)\n at Assertion.value (node_modules/should/cjs/should.js:335:19)\n at Context. (helpers.js:26:20)", - "diff": " {\n- \"bar\": false\n+ \"bar\": true\n \"baz\": 1\n \"foo\": true\n }\n" - }, - "isRoot": false, - "uuid": "b88d1dce-1332-4bd9-bc1a-9ce3bceb89e4", - "parentUUID": "571df514-f199-464d-aa1e-aed923a241ec", - "isHook": false, - "skipped": false - } - ], - "allPending": [], - "allPasses": [], - "allFailures": [ - { - "title": "should be true", - "fullTitle": "Main Suite should be true", - "timedOut": false, - "duration": 3, - "state": "failed", - "pass": false, - "fail": true, - "pending": false, - "code": "// true.should.eql(bool);\nexp.should.eql({\n foo: true,\n bar: true,\n baz: 1\n});\nshouldAddContext && addContext(this, 'context');", - "err": { - "operator": "to equal", - "expected": "{\n \"bar\": true\n \"baz\": 1\n \"foo\": true\n}", - "details": "at bar, A has false and B has true", - "showDiff": true, - "actual": "{\n \"bar\": false\n \"baz\": 1\n \"foo\": true\n}", - "negate": false, - "assertion": { - "obj": { - "foo": true, - "bar": false, - "baz": 1 - }, - "anyOne": false, - "negate": false, - "params": { - "operator": "to equal", - "expected": { - "foo": true, - "bar": true, - "baz": 1 - }, - "details": "at bar, A has false and B has true", - "showDiff": true, - "actual": { - "foo": true, - "bar": false, - "baz": 1 - }, - "negate": false, - "assertion": "[Circular ~.allFailures.0.err.assertion]" - }, - "light": false - }, - "_message": "expected Object { foo: true, bar: false, baz: 1 } to equal Object { foo: true, bar: true, baz: 1 } (at bar, A has false and B has true)", - "generatedMessage": true, - "estack": "AssertionError: expected Object { foo: true, bar: false, baz: 1 } to equal Object { foo: true, bar: true, baz: 1 } (at bar, A has false and B has true)\n at Assertion.fail (node_modules/should/cjs/should.js:258:17)\n at Assertion.value (node_modules/should/cjs/should.js:335:19)\n at Context. (helpers.js:26:20)", - "diff": " {\n- \"bar\": false\n+ \"bar\": true\n \"baz\": 1\n \"foo\": true\n }\n" - }, - "isRoot": false, - "uuid": "b88d1dce-1332-4bd9-bc1a-9ce3bceb89e4", - "parentUUID": "571df514-f199-464d-aa1e-aed923a241ec", - "isHook": false, - "skipped": false - } - ], "copyrightYear": 2017 } \ No newline at end of file diff --git a/test/spec/cli/cli-main.test.js b/test/spec/cli/cli-main.test.js index a0f4d307..b2f30111 100644 --- a/test/spec/cli/cli-main.test.js +++ b/test/spec/cli/cli-main.test.js @@ -180,9 +180,7 @@ describe('bin/cli', () => { }); it('should default to filename when option not provided', () => { - const args = getArgs( - [ 'test/sample-data/test.json' ], - ); + const args = getArgs([ 'test/sample-data/test.json' ]); return cli(args).then(() => { expect(createStub.args[0][1]).to.have.property('reportFilename', 'test'); diff --git a/test/spec/components/dropdown/dropdown.test.jsx b/test/spec/components/dropdown/dropdown.test.jsx index 25e6979f..4c669cd4 100644 --- a/test/spec/components/dropdown/dropdown.test.jsx +++ b/test/spec/components/dropdown/dropdown.test.jsx @@ -84,11 +84,12 @@ describe('', () => { }); it('handles toggling the menu', () => { - const { menu, toggle } = getInstance(props); + const { wrapper, toggle } = getInstance(props); toggle.simulate('click'); - expect(menu.hasClass('dropdown-open')).to.equal(true); + expect(wrapper.find('ul').hasClass('dropdown-open')).to.equal(true); document.body.click(); - expect(menu.hasClass('dropdown-open')).to.equal(false); + wrapper.update(); + expect(wrapper.find('ul').hasClass('dropdown-open')).to.equal(false); }); it('handles selecting an item', () => { diff --git a/test/spec/components/loader.test.jsx b/test/spec/components/loader.test.jsx new file mode 100644 index 00000000..9b0c8dbe --- /dev/null +++ b/test/spec/components/loader.test.jsx @@ -0,0 +1,36 @@ +import React from 'react'; +import { shallow } from 'enzyme'; +import chai, { expect } from 'chai'; +import chaiEnzyme from 'chai-enzyme'; + +import Loader from 'components/loader'; + +chai.use(chaiEnzyme()); + +describe('', () => { + let props; + + const getInstance = instanceProps => { + const wrapper = shallow(); + return { wrapper: wrapper.dive() }; + }; + + beforeEach(() => { + props = { + reportStore: { + isLoading: true + } + }; + }); + + it('renders when isLoading is true', () => { + const { wrapper } = getInstance(props); + expect(wrapper.find('.loader-text')).to.have.lengthOf(1); + }); + + it('does NOT render when isLoading is true', () => { + props.reportStore.isLoading = false; + const { wrapper } = getInstance(props); + expect(wrapper.isEmptyRender()).to.equal(true); + }); +}); diff --git a/test/spec/components/main.test.jsx b/test/spec/components/main.test.jsx index 5e83205f..2c1c5b29 100644 --- a/test/spec/components/main.test.jsx +++ b/test/spec/components/main.test.jsx @@ -44,9 +44,9 @@ describe('', () => { scripts: 'function noop(){return;}' }; const wrapper = shallow(
); - expect(wrapper.find('style')).to.have.html(''); - expect(wrapper.find('script')).to.have.html( - '' - ); + expect(wrapper.find('style')) + .to.have.html(''); + expect(wrapper.find('script')) + .to.have.html(''); }); }); diff --git a/test/spec/components/material-icon.test.jsx b/test/spec/components/material-icon.test.jsx index 84bcecd5..dfda6002 100644 --- a/test/spec/components/material-icon.test.jsx +++ b/test/spec/components/material-icon.test.jsx @@ -14,9 +14,12 @@ describe('', () => { }); it('renders icon with options', () => { - const wrapper = shallow( - - ); + const opts = { + size: 18, + foreground: 'dark', + className: 'icontest' + }; + const wrapper = shallow(); expect(wrapper).to.have.html(''); }); diff --git a/test/spec/components/navbar.test.jsx b/test/spec/components/navbar.test.jsx index f0e4e02a..3faa2663 100644 --- a/test/spec/components/navbar.test.jsx +++ b/test/spec/components/navbar.test.jsx @@ -28,7 +28,6 @@ describe('', () => { return { wrapper, title: wrapper.find('.navbar-report-title'), - infoCnt: wrapper.find('.navbar-report-info-cnt'), menuBtn: wrapper.find('.navbar-menu-button'), pctBar: wrapper.find('.navbar-pct-bar') }; @@ -36,27 +35,17 @@ describe('', () => { beforeEach(() => { props = { - qsNodeRef: () => {}, reportTitle: 'test', - stats: testData.stats, - qsWidth: 500, - mobileBreakpoint: false + stats: testData.stats }; }); it('renders', () => { - const { wrapper, title, menuBtn, pctBar, infoCnt } = getInstance(props); + const { wrapper, title, menuBtn, pctBar } = getInstance(props); expect(title.text()).to.equal('test'); expect(wrapper.find(QuickSummary)).to.have.lengthOf(1); expect(menuBtn).to.have.lengthOf(1); expect(pctBar.find('span')).to.have.lengthOf(2); - expect(infoCnt).to.have.style('padding-right', '500px'); - }); - - it('renders on mobile', () => { - props.mobileBreakpoint = true; - const { infoCnt } = getInstance(props); - expect(infoCnt).to.not.have.attr('style'); }); it('opens side nav', () => { @@ -68,11 +57,8 @@ describe('', () => { describe('when pendingPercent is 100', () => { beforeEach(() => { props = { - qsNodeRef: () => {}, reportTitle: 'test', - stats: { passPercent: 0, pendingPercent: 100 }, - qsWidth: 500, - mobileBreakpoint: false + stats: { passPercent: 0, pendingPercent: 100 } }; }); @@ -86,11 +72,8 @@ describe('', () => { describe('when passPercent and pendingPercent are null', () => { beforeEach(() => { props = { - qsNodeRef: () => {}, reportTitle: 'test', - stats: { passPercent: null, pendingPercent: null }, - qsWidth: 500, - mobileBreakpoint: false + stats: { passPercent: null, pendingPercent: null } }; }); diff --git a/test/spec/components/report-body.test.jsx b/test/spec/components/report-body.test.jsx new file mode 100644 index 00000000..5f98d463 --- /dev/null +++ b/test/spec/components/report-body.test.jsx @@ -0,0 +1,62 @@ +import React from 'react'; +import { shallow } from 'enzyme'; +import chai, { expect } from 'chai'; +import chaiEnzyme from 'chai-enzyme'; +import sinon from 'sinon'; +import proxyquire from 'proxyquire'; + +import Suite from 'components/suite/suite'; +import basicSuite from 'sample-data/suite.json'; + +proxyquire.noCallThru(); + +chai.use(chaiEnzyme()); + +const disposerSpy = sinon.spy(); +const reactionSpy = sinon.stub().callsFake((fn1, fn2) => { + fn2(); + return disposerSpy; +}); + +const ReportBody = proxyquire('components/report-body', { + mobx: { + reaction: reactionSpy + } +}).default; + +describe('', () => { + let props; + + const getInstance = instanceProps => { + const wrapper = shallow(, { + lifecycleExperimental: true + }); + return { + wrapper: wrapper.dive(), + suites: wrapper.dive().find(Suite) + }; + }; + + beforeEach(() => { + props = { + reportStore: { + enableCode: true, + initialLoadTimeout: 0, + filteredSuites: [ basicSuite ], + updateFilteredSuites: () => [ basicSuite ], + toggleIsLoading: sinon.spy() + } + }; + }); + + it('renders Suites', () => { + const { suites } = getInstance(props); + expect(suites).to.have.lengthOf(1); + }); + + it('calls disposer on unmount', () => { + const { wrapper } = getInstance(props); + wrapper.unmount(); + expect(disposerSpy.called).to.equal(true); + }); +}); diff --git a/test/spec/components/report.test.jsx b/test/spec/components/report.test.jsx index 1c4076b5..e78f0a37 100644 --- a/test/spec/components/report.test.jsx +++ b/test/spec/components/report.test.jsx @@ -15,6 +15,7 @@ chai.use(chaiEnzyme()); describe('', () => { let reportStore; let props; + let clock; const getInstance = (instanceProps, opts) => { const wrapper = mount(, opts); @@ -27,22 +28,18 @@ describe('', () => { beforeEach(() => { reportStore = new ReportStore(); props = { store: reportStore }; + clock = sinon.useFakeTimers(); }); - it('should render', done => { + afterEach(() => { + clock.restore(); + }); + + it('should render', () => { reportStore.setInitialData({ data: testData, config: {} }); - sinon.spy(Report.prototype, 'componentDidMount'); - sinon.spy(Report.prototype, 'componentWillUnmount'); const { wrapper } = getInstance(props); expect(wrapper.find(DevTools)).to.have.lengthOf(0); - expect(Report.prototype.componentDidMount.calledOnce).to.equal(true); - - setTimeout(() => { - wrapper.unmount(); - expect(Report.prototype.componentWillUnmount.calledOnce).to.equal(true); - done(); - }, 0); }); it('should render in dev mode', () => { @@ -52,16 +49,19 @@ describe('', () => { }); it('should scroll to a suite', () => { - reportStore.setInitialData({ data: testData, config: {} }); + reportStore.setInitialData({ + data: testData, + config: { enableCharts: true } + }); + const node = document.createElement('div'); node.setAttribute('id', 'app'); document.body.appendChild(node); - const { wrapper } = getInstance(props, { attachTo: node }); + clock.next(); expect(window.scrollTop).to.equal(0); wrapper.find('.nav-menu-link').at(3).simulate('click'); - document.getElementById('app').remove(); }); }); diff --git a/test/spec/components/suite/suite.test.jsx b/test/spec/components/suite/suite.test.jsx index bbe421af..34791f3c 100644 --- a/test/spec/components/suite/suite.test.jsx +++ b/test/spec/components/suite/suite.test.jsx @@ -2,6 +2,7 @@ import React from 'react'; import { shallow } from 'enzyme'; import chai, { expect } from 'chai'; import chaiEnzyme from 'chai-enzyme'; +import sinon from 'sinon'; import Suite from 'components/suite/suite'; import SuiteSummary from 'components/suite/summary'; @@ -108,7 +109,7 @@ describe('', () => { it('renders root suite with tests', () => { const suite = Object.assign({}, nestedSuite.suites); suite.rootEmpty = false; - suite.hasTests = true; + suite.tests = nestedSuite.suites.suites[0].tests; const instProps = Object.assign({}, props, { suite }); const { chart, summary, testList, suiteList, header } = getInstance(instProps); expect(chart).to.have.lengthOf(1); @@ -129,4 +130,35 @@ describe('', () => { expect(suiteList).to.have.lengthOf(1); expect(header).to.have.lengthOf(0); }); + + describe('shouldComponentUpdate', () => { + let scuSpy; + beforeEach(() => { + scuSpy = sinon.spy(Suite.prototype, 'shouldComponentUpdate'); + }); + + afterEach(() => { + scuSpy.restore(); + }); + + it('returns true when next props do not equal current props', () => { + const instProps = Object.assign({}, props, { + suite: basicSuite + }); + const { wrapper } = getInstance(instProps); + wrapper.setProps({ suite: nestedSuite.suites }); + expect(scuSpy.calledOnce).to.equal(true); + expect(scuSpy.returned(true)).to.equal(true); + }); + + it('returns false when next props equal current props', () => { + const instProps = Object.assign({}, props, { + suite: basicSuite + }); + const { wrapper } = getInstance(instProps); + wrapper.setProps({ suite: basicSuite }); + expect(scuSpy.calledOnce).to.equal(true); + expect(scuSpy.returned(false)).to.equal(true); + }); + }); }); diff --git a/test/spec/components/test/code-snippet.test.jsx b/test/spec/components/test/code-snippet.test.jsx index 73609c40..414c8844 100644 --- a/test/spec/components/test/code-snippet.test.jsx +++ b/test/spec/components/test/code-snippet.test.jsx @@ -35,7 +35,7 @@ describe('', () => { code: 'function(){console.log(\'sample code\');}' }; const wrapper = getInstance(props); - expect(wrapper.hasClass('javascript')).to.equal(true); + expect(wrapper.find('pre').hasClass('javascript')).to.equal(true); expect(document.querySelectorAll('.hljs-keyword').length).to.equal(1); expect(document.querySelectorAll('.test-code-diff-expected').length).to.equal(0); expect(document.querySelectorAll('.test-code-diff-actual').length).to.equal(0); @@ -47,8 +47,8 @@ describe('', () => { lang: 'diff' }; const wrapper = getInstance(props); - expect(wrapper.hasClass('diff')).to.equal(true); - expect(wrapper.hasClass('inline-diff')).to.equal(false); + expect(wrapper.find('pre').hasClass('diff')).to.equal(true); + expect(wrapper.find('pre').hasClass('inline-diff')).to.equal(false); expect(document.querySelectorAll('.test-code-diff-expected').length).to.equal(1); expect(document.querySelectorAll('.test-code-diff-actual').length).to.equal(1); expect(document.querySelectorAll('.hljs-addition').length).to.equal(3); @@ -66,8 +66,8 @@ describe('', () => { lang: 'diff' }; const wrapper = getInstance(props, { useInlineDiffs: true }); - expect(wrapper.hasClass('diff')).to.equal(false); - expect(wrapper.hasClass('inline-diff')).to.equal(true); + expect(wrapper.find('pre').hasClass('diff')).to.equal(false); + expect(wrapper.find('pre').hasClass('inline-diff')).to.equal(true); expect(document.querySelectorAll('.test-code-diff-expected').length).to.equal(2); expect(document.querySelectorAll('.test-code-diff-actual').length).to.equal(2); }); diff --git a/test/spec/components/test/test.test.jsx b/test/spec/components/test/test.test.jsx index 235ee1e3..59a0e534 100644 --- a/test/spec/components/test/test.test.jsx +++ b/test/spec/components/test/test.test.jsx @@ -182,6 +182,7 @@ describe('', () => { const getInstance = instanceProps => { const wrapper = shallow(); return { + wrapper, header: wrapper.find('.test-header'), snippets: wrapper.find(CodeSnippet), errorMsg: wrapper.find('.test-error-message'), @@ -201,7 +202,7 @@ describe('', () => { it('renders passing test', () => { const { header, snippets, errorMsg, ctx } = getInstance({ test: passingTest }); - expect(snippets).to.have.lengthOf(3); + expect(snippets).to.have.lengthOf(0); expect(errorMsg).to.have.lengthOf(0); expect(ctx).to.have.lengthOf(0); header.simulate('click'); @@ -209,19 +210,29 @@ describe('', () => { expect(setStateSpy.calledOnce).to.equal(true); }); + it('renders passing test, expanded', () => { + const { wrapper, snippets, errorMsg, ctx } = getInstance({ test: passingTest }); + expect(snippets).to.have.lengthOf(0); + expect(errorMsg).to.have.lengthOf(0); + expect(ctx).to.have.lengthOf(0); + wrapper.setState({ expanded: true }); + expect(setStateSpy.calledOnce).to.equal(true); + expect(wrapper.find(CodeSnippet)).to.have.lengthOf(3); + }); + it('renders passing test with context', () => { - const { header, snippets, errorMsg, ctx } = getInstance({ test: passingTestWithContext }); - expect(snippets).to.have.lengthOf(3); + const { wrapper, snippets, errorMsg, ctx } = getInstance({ test: passingTestWithContext }); + expect(snippets).to.have.lengthOf(0); expect(errorMsg).to.have.lengthOf(0); - expect(ctx).to.have.lengthOf(1); - header.simulate('click'); - expect(toggleSpy.calledOnce).to.equal(true); + expect(ctx).to.have.lengthOf(0); + wrapper.setState({ expanded: true }); expect(setStateSpy.calledOnce).to.equal(true); + expect(wrapper.find(TestContext)).to.have.lengthOf(1); }); it('renders passing test, enableCode: false', () => { const { header, snippets, errorMsg } = getInstance({ test: passingTest, enableCode: false }); - expect(snippets).to.have.lengthOf(2); + expect(snippets).to.have.lengthOf(0); expect(errorMsg).to.have.lengthOf(0); header.simulate('click'); expect(toggleSpy.calledOnce).to.equal(true); @@ -230,7 +241,7 @@ describe('', () => { it('renders passing test with context, enableCode: false', () => { const { header, snippets, errorMsg } = getInstance({ test: passingTestWithContext, enableCode: false }); - expect(snippets).to.have.lengthOf(2); + expect(snippets).to.have.lengthOf(0); expect(errorMsg).to.have.lengthOf(0); header.simulate('click'); expect(toggleSpy.calledOnce).to.equal(true); @@ -239,7 +250,7 @@ describe('', () => { it('renders failing test', () => { const { header, snippets, errorMsg } = getInstance({ test: failingTest }); - expect(snippets).to.have.lengthOf(3); + expect(snippets).to.have.lengthOf(0); expect(errorMsg).to.have.lengthOf(1); header.simulate('click'); expect(toggleSpy.calledOnce).to.equal(true); @@ -248,7 +259,7 @@ describe('', () => { it('renders pending test', () => { const { header, snippets, errorMsg } = getInstance({ test: pendingTest }); - expect(snippets).to.have.lengthOf(3); + expect(snippets).to.have.lengthOf(0); expect(errorMsg).to.have.lengthOf(0); header.simulate('click'); expect(toggleSpy.calledOnce).to.equal(true); @@ -257,7 +268,7 @@ describe('', () => { it('renders skipped test', () => { const { header, snippets, errorMsg } = getInstance({ test: skippedTest }); - expect(snippets).to.have.lengthOf(3); + expect(snippets).to.have.lengthOf(0); expect(errorMsg).to.have.lengthOf(0); header.simulate('click'); expect(toggleSpy.calledOnce).to.equal(true); @@ -266,7 +277,7 @@ describe('', () => { it('renders a before hook', () => { const { header, snippets, errorMsg } = getInstance({ test: beforeHook }); - expect(snippets).to.have.lengthOf(3); + expect(snippets).to.have.lengthOf(0); expect(errorMsg).to.have.lengthOf(0); header.simulate('click'); expect(toggleSpy.calledOnce).to.equal(true); @@ -275,7 +286,7 @@ describe('', () => { it('renders a failed before hook', () => { const { header, snippets, errorMsg } = getInstance({ test: beforeHookFailed }); - expect(snippets).to.have.lengthOf(3); + expect(snippets).to.have.lengthOf(0); expect(errorMsg).to.have.lengthOf(1); header.simulate('click'); expect(toggleSpy.calledOnce).to.equal(true); @@ -284,7 +295,7 @@ describe('', () => { it('renders an after hook', () => { const { header, snippets, errorMsg } = getInstance({ test: afterHook }); - expect(snippets).to.have.lengthOf(3); + expect(snippets).to.have.lengthOf(0); expect(errorMsg).to.have.lengthOf(0); header.simulate('click'); expect(toggleSpy.calledOnce).to.equal(true); @@ -293,7 +304,7 @@ describe('', () => { it('renders a failed after hook', () => { const { header, snippets, errorMsg } = getInstance({ test: afterHookFailed }); - expect(snippets).to.have.lengthOf(3); + expect(snippets).to.have.lengthOf(0); expect(errorMsg).to.have.lengthOf(1); header.simulate('click'); expect(toggleSpy.calledOnce).to.equal(true); diff --git a/test/spec/lib/main.test.js b/test/spec/lib/main.test.js index a71b0569..fb6793f1 100644 --- a/test/spec/lib/main.test.js +++ b/test/spec/lib/main.test.js @@ -34,24 +34,13 @@ const mareport = proxyquire('../../../lib/src/main', { '../package.json': pkg }); -const baseOpts = { - reportDir: 'test', - reportFilename: 'test', - reportTitle: 'mochawesome', - reportPageTitle: 'mochawesome-report', - inlineAssets: false, - enableCharts: true, - enableCode: true, - overwrite: true, - timestamp: false, - dev: false, - showHooks: 'failed' -}; - let opts; beforeEach(() => { - opts = Object.assign({}, baseOpts); + opts = { + reportDir: 'test', + reportFilename: 'test' + }; outputFileStub.reset(); outputFileSyncStub.reset(); copySyncStub.reset(); @@ -63,14 +52,25 @@ beforeEach(() => { describe('lib/main', () => { describe('create', () => { - it('saves report', () => { + it('saves only html', () => { const expectedHtmlFile = path.resolve(process.cwd(), 'test', 'test.html'); outputFileStub.resolves(expectedHtmlFile); const promise = mareport.create(testData, opts); - return expect(promise).to.become([ expectedHtmlFile ]); + return expect(promise).to.become([ expectedHtmlFile, null ]); + }); + + it('saves only json', () => { + opts.saveHtml = false; + opts.saveJson = true; + const expectedJsonFile = path.resolve(process.cwd(), 'test', 'test.json'); + outputFileStub + .onCall(0).resolves('') + .onCall(1).resolves(expectedJsonFile); + const promise = mareport.create(testData, opts); + return expect(promise).to.become([ null, expectedJsonFile ]); }); - it('saves report and json', () => { + it('saves html and json', () => { opts.saveJson = true; const expectedHtmlFile = path.resolve(process.cwd(), 'test', 'test.html'); const expectedJsonFile = path.resolve(process.cwd(), 'test', 'test.json'); @@ -81,6 +81,13 @@ describe('lib/main', () => { return expect(promise).to.become([ expectedHtmlFile, expectedJsonFile ]); }); + it('does NOT save report', () => { + opts.saveHtml = false; + outputFileStub.resolves(''); + const promise = mareport.create(testData, opts); + return expect(promise).to.become([ null, null ]); + }); + it('with autoOpen', () => { opts.autoOpen = true; openerStub.yields(null); @@ -183,7 +190,11 @@ describe('lib/main', () => { }); it('with overwrite:false', () => { - opts.overwrite = false; + opts = { + overwrite: false, + reportDir: 'test', + reportFilename: 'test' + }; writeFileUniqueStub.yields(null); const expectedFilename = path.resolve(process.cwd(), 'test', 'test{_###}.html'); return mareport.create(testData, opts).then(() => { @@ -299,27 +310,4 @@ describe('lib/main', () => { )); }); }); - - describe('defaults', () => { - it('should get base options', () => { - expect(mareport.getBaseConfig()) - .to.eql({ - reportDir: 'mochawesome-report', - reportTitle: process.cwd().split(path.sep).pop(), - reportPageTitle: 'Mochawesome Report', - inline: false, - inlineAssets: false, - charts: true, - enableCharts: true, - code: true, - enableCode: true, - autoOpen: false, - overwrite: true, - timestamp: false, - ts: false, - dev: false, - showHooks: 'failed' - }); - }); - }); }); diff --git a/test/spec/lib/options.test.js b/test/spec/lib/options.test.js index 8ff7a56c..8ffc9daf 100644 --- a/test/spec/lib/options.test.js +++ b/test/spec/lib/options.test.js @@ -1,7 +1,7 @@ /* eslint-disable import/no-extraneous-dependencies */ import path from 'path'; import { expect } from 'chai'; -import options from '../../../lib/src/options'; +import { getMergedOptions } from '../../../lib/src/options'; const expectedOptions = { reportDir: 'mochawesome-report', @@ -16,19 +16,46 @@ const expectedOptions = { autoOpen: false, overwrite: true, timestamp: false, + saveJson: false, + saveHtml: true, ts: false, dev: false, showHooks: 'failed' }; describe('options', () => { - it('should get yargs options', () => { - expect(options.getYargsOptions()) - .to.have.deep.property('o.default', expectedOptions.reportDir); + it('should get base options when no user options exist', () => { + expect(getMergedOptions()) + .to.eql(expectedOptions); }); - it('should get base options', () => { - expect(options.getBaseConfig()) - .to.eql(expectedOptions); + describe('with user-supplied options', () => { + let userOptions; + + beforeEach(() => { + userOptions = { + reportDir: 'userDir', + inline: true, + enableCode: false, + dev: 'true' + }; + process.env.MOCHAWESOME_REPORTTITLE = 'userTitle'; + process.env.MOCHAWESOME_AUTOOPEN = 'false'; + }); + + afterEach(() => { + delete process.env.MOCHAWESOME_REPORTTITLE; + delete process.env.MOCHAWESOME_AUTOOPEN; + }); + + it('should get merged options', () => { + expect(getMergedOptions(userOptions)) + .to.eql(Object.assign({}, expectedOptions, userOptions, { + inlineAssets: true, + code: false, + dev: true, + reportTitle: 'userTitle' + })); + }); }); }); diff --git a/test/spec/reportStore.test.js b/test/spec/reportStore.test.js index bb2a5c94..650a63fb 100644 --- a/test/spec/reportStore.test.js +++ b/test/spec/reportStore.test.js @@ -1,24 +1,31 @@ import { expect } from 'chai'; +import sinon from 'sinon'; import { ReportStore } from 'js/reportStore'; import testData from 'sample-data/nested.json'; describe('ReportStore', () => { let store; + let clock; beforeEach(() => { store = new ReportStore(); + clock = sinon.useFakeTimers(); + }); + + afterEach(() => { + clock.restore(); }); it('has the correct default state', () => { expect(store).to.include({ + isLoading: true, sideNavOpen: false, showPassed: true, showFailed: true, showPending: true, showSkipped: false, - quickSummaryWidth: null, - windowWidth: null, - showHooks: 'failed' + showHooks: 'failed', + VERSION: '__VERSION__' }); }); @@ -31,11 +38,12 @@ describe('ReportStore', () => { expect(store).to.have.property('data', testData); expect(store).to.have.property('reportTitle', undefined); expect(store).to.have.property('stats', testData.stats); - expect(store).to.have.deep.property('allSuites[0]', testData.suites); + expect(store).to.have.nested.deep.property('allSuites[0]', testData.suites); expect(store).to.have.deep.property('stats', testData.stats); expect(store).to.have.property('enableChart', false); expect(store).to.have.property('devMode', false); expect(store).to.have.property('showHooks', 'failed'); + expect(store).to.have.property('initialLoadTimeout', 300); }); it('with config options', () => { @@ -50,7 +58,7 @@ describe('ReportStore', () => { expect(store).to.have.property('data', testData); expect(store).to.have.property('reportTitle', undefined); expect(store).to.have.property('stats', testData.stats); - expect(store).to.have.deep.property('allSuites[0]', testData.suites); + expect(store).to.have.nested.deep.property('allSuites[0]', testData.suites); expect(store).to.have.deep.property('stats', testData.stats); expect(store).to.have.property('enableChart', true); expect(store).to.have.property('devMode', true); @@ -69,40 +77,6 @@ describe('ReportStore', () => { }); }); - describe('Computed Properties', () => { - beforeEach(() => { - store.setInitialData({ data: testData, config: {} }); - }); - - describe('mobileBreakpoint', () => { - it('returns false when windowWidth >= 768', () => { - store.setWindowWidth(1024); - expect(store.mobileBreakpoint).to.equal(false); - }); - - it('returns true when windowWidth < 768', () => { - store.setWindowWidth(600); - expect(store.mobileBreakpoint).to.equal(true); - }); - }); - - describe('suites', () => { - it('is not empty when filters are on', () => { - store.toggleFilter('showSkipped'); - expect(store.suites).to.have.lengthOf(1); - store.toggleFilter('showSkipped'); - }); - - it('is empty when fiilters are off', () => { - store.toggleFilter('showPassed'); - store.toggleFilter('showFailed'); - store.toggleFilter('showPending'); - store.setShowHooks('never'); - expect(store.suites).to.have.lengthOf(0); - }); - }); - }); - describe('Actions', () => { beforeEach(() => { store.setInitialData({ data: testData, config: {} }); @@ -157,16 +131,48 @@ describe('ReportStore', () => { expect(store).to.have.property('showHooks', 'failed'); }); - it('setQuickSummaryWidth', () => { - expect(store).to.have.property('quickSummaryWidth', null); - store.setQuickSummaryWidth(400); - expect(store).to.have.property('quickSummaryWidth', 400); + it('toggleIsLoading', () => { + expect(store).to.have.property('isLoading', true); + store.toggleIsLoading(); + expect(store).to.have.property('isLoading', false); + }); + + it('toggleIsLoading with value', () => { + expect(store).to.have.property('isLoading', true); + store.toggleIsLoading(true); + expect(store).to.have.property('isLoading', true); + }); + }); + + describe('updateFilteredSuites', () => { + beforeEach(() => { + store.setInitialData({ data: testData, config: {} }); + store.toggleIsLoading(true); }); - it('setWindowWidth', () => { - expect(store).to.have.property('windowWidth', null); - store.setWindowWidth(400); - expect(store).to.have.property('windowWidth', 400); + describe('when filters are on', () => { + it('should set isLoading: false and filteredSuites should not be an empty array', () => { + store.updateFilteredSuites(); + clock.next(); + expect(store.isLoading).to.equal(false); + expect(store.filteredSuites).to.have.lengthOf(1); + }); + }); + + describe('when filters are off', () => { + beforeEach(() => { + store.toggleFilter('showPassed'); + store.toggleFilter('showFailed'); + store.toggleFilter('showPending'); + store.setShowHooks('never'); + }); + + it('should set isLoading: false and filteredSuites should be an empty array', () => { + store.updateFilteredSuites(); + clock.next(); + expect(store.isLoading).to.equal(false); + expect(store.filteredSuites).to.have.lengthOf(0); + }); }); }); }); diff --git a/webpack/base.config.js b/webpack/base.config.js index 9c71d022..03b8716c 100644 --- a/webpack/base.config.js +++ b/webpack/base.config.js @@ -32,6 +32,7 @@ if (env === 'production') { module.exports = { env, + version: pkg.version, baseConfig: { devtool, entry: { diff --git a/webpack/dev.config.js b/webpack/dev.config.js index dbaf44af..17ea65c3 100644 --- a/webpack/dev.config.js +++ b/webpack/dev.config.js @@ -1,5 +1,5 @@ /* eslint-disable max-len */ -const { baseConfig } = require('./base.config'); +const { baseConfig, version } = require('./base.config'); const loaders = require('./loaders'); const { eslint, babel, globalCss, localCss, font } = loaders; @@ -15,7 +15,7 @@ module.exports = Object.assign({}, baseConfig, { module: { rules: [ eslint({ enforce: 'pre' }), - babel(), + babel({}, version), globalCss({ minimize: false, importLoaders: 1, diff --git a/webpack/external.config.js b/webpack/external.config.js index c2f1e09c..ba4143a2 100644 --- a/webpack/external.config.js +++ b/webpack/external.config.js @@ -1,6 +1,6 @@ /* eslint-disable max-len */ const path = require('path'); -const { baseConfig } = require('./base.config'); +const { baseConfig, version } = require('./base.config'); const loaders = require('./loaders'); // Set output path @@ -12,7 +12,7 @@ module.exports = Object.assign({}, baseConfig, { module: { rules: [ eslint({ enforce: 'pre' }), - babel(), + babel({}, version), globalCss({ minimize: true, importLoaders: 1 diff --git a/webpack/inline.config.js b/webpack/inline.config.js index 11484e76..b254cfd4 100644 --- a/webpack/inline.config.js +++ b/webpack/inline.config.js @@ -1,6 +1,6 @@ /* eslint-disable max-len */ const path = require('path'); -const { baseConfig } = require('./base.config'); +const { baseConfig, version } = require('./base.config'); const loaders = require('./loaders'); // Set output path @@ -12,7 +12,7 @@ module.exports = Object.assign({}, baseConfig, { module: { rules: [ eslint({ enforce: 'pre' }), - babel(), + babel({}, version), globalCss({ minimize: true, importLoaders: 1 diff --git a/webpack/loaders.js b/webpack/loaders.js index fd2d949d..246ef19f 100644 --- a/webpack/loaders.js +++ b/webpack/loaders.js @@ -10,11 +10,20 @@ function eslint(attrs) { }, attrs); } -function babel(attrs) { +function babel(attrs, version) { return Object.assign({ test: JS_REGEX, exclude: /node_modules/, - use: [ 'babel-loader' ] + use: [ + { loader: 'babel-loader' }, + { + loader: 'string-replace-loader', + options: { + search: '__VERSION__', + replace: version + } + } + ] }, attrs); }