Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add parallel mode support #331

Merged
merged 1 commit into from
Nov 1, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
.DS_Store
*.log
node_modules/
mochawesome-reports/
mochawesome-report/
mochawesome-report*/
_site/
coverage/
.nyc_output/
Expand Down
13 changes: 11 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,11 @@ Mochawesome is a custom reporter for use with the Javascript testing framework,

1. Add Mochawesome to your project:

`npm install --save-dev mochawesome`
`npm install --save-dev mochawesome`

2. Tell mocha to use the Mochawesome reporter:

`mocha testfile.js --reporter mochawesome`
`mocha testfile.js --reporter mochawesome`

3. If using mocha programatically:

Expand All @@ -38,6 +38,15 @@ Mochawesome is a custom reporter for use with the Javascript testing framework,
});
```

### Parallel
if you consider to use mocha@8 with the parallel option you need to register mochawesome as a hook, e.g.

`mocha tests --reporter mochawesome --require mochawesome/register`

**_IMPORTANT:_** Report statistics may differ between sequential and parallel test execution due to the mocha limitations. Mocha does not provide information about skipped tests in parallel mode.

Please use the parallel option reasonably, as spawning worker processes is not free. You can read more about it [here](https://developer.ibm.com/articles/parallel-tests-mocha-v8/#when-to-avoid-parallel-mode).

### Output
Mochawesome generates the following inside your project directory:
```
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
"scripts": {
"lint": "eslint src test*",
"test": "npm run lint && cross-env NODE_ENV=test nyc mocha --config test/.mocharc.json",
"test:fn": "mocha test-functional/test.js --config test-functional/.mocharc.json",
"test:fn": "mocha test-functional --config test-functional/.mocharc.json",
"test:par": "mocha test-parallel --config test-parallel/.mocharc.json",
"test:prog": "node ./test-programmatic",
"test:mem": "mocha test-functional/mem-test.js --config test-functional/.mocharc.json",
"test:ctx": "mocha test-functional/test-context.js --config test-functional/.mocharc.json",
Expand Down
8 changes: 8 additions & 0 deletions register.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/**
* Re-export `register` from ./src folder in order to register nicely mochawesome as a hook in mocha
* in the way how it's done in ts-node/register or @babel/register
* @example
* $ mocha --require mochawesome/register tests
*
*/
module.exports = require('./src/register');
73 changes: 73 additions & 0 deletions src/mochawesome.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,16 @@ const margePkg = require('mochawesome-report-generator/package.json');
const conf = require('./config');
const utils = require('./utils');
const pkg = require('../package.json');
const Mocha = require('mocha');
const {
EVENT_RUN_BEGIN,
EVENT_HOOK_END,
EVENT_SUITE_BEGIN,
EVENT_TEST_PASS,
EVENT_TEST_FAIL,
EVENT_TEST_PENDING,
EVENT_SUITE_END,
} = Mocha.Runner.constants;

// Import the utility functions
const { log, mapSuites } = utils;
Expand Down Expand Up @@ -118,6 +128,69 @@ function Mochawesome(runner, options) {
});
});

// Handle events from workers in parallel mode
if (runner.constructor.name === 'ParallelBufferedRunner') {
let currentSuite;

const HookMap = {
['"before all" ']: '_beforeAll',
['"before each" ']: '_beforeEach',
['"after each" ']: '_afterEach',
['"after all" ']: '_afterAll',
};

runner.on(EVENT_RUN_BEGIN, function () {
currentSuite = undefined;
});

runner.on(EVENT_SUITE_BEGIN, function (suite) {
suite._beforeAll = suite._beforeAll || [];
suite._beforeEach = suite._beforeEach || [];
suite.suites = suite.suites || [];
suite.tests = suite.tests || [];
suite._afterEach = suite._afterEach || [];
suite._afterAll = suite._afterAll || [];
if (suite.root) {
suite = runner.suite;
} else if (currentSuite) {
currentSuite.suites.push(suite);
suite.parent = currentSuite;
}
currentSuite = suite;
});

runner.on(EVENT_SUITE_END, function () {
if (currentSuite) {
currentSuite = currentSuite.parent;
}
});

runner.on(EVENT_HOOK_END, function (hook) {
if (currentSuite) {
const hooks = currentSuite[HookMap[hook.title.split('hook')[0]]];
// add only once, since it is attached to the Suite
if (hooks && hooks.every(it => it.title !== hook.title)) {
hook.parent = currentSuite;
hooks.push(hook);
}
}
});

[EVENT_TEST_PASS, EVENT_TEST_FAIL, EVENT_TEST_PENDING].forEach(type => {
runner.on(type, function (test) {
if (currentSuite) {
test.parent = currentSuite;
if (test.type === 'hook') {
const hooks = currentSuite[HookMap[test.title.split('hook')[0]]];
hooks && hooks.push(test);
} else {
currentSuite.tests.push(test);
}
}
});
});
}

// Process the full suite
runner.on('end', () => {
try {
Expand Down
18 changes: 18 additions & 0 deletions src/register.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
const Mocha = require('mocha');

const extendSerialize = (target, fields) => {
const serialize = target.serialize;
target.serialize = function (...args) {
const result = serialize.call(this, ...args);
for (let field of fields) {
result[field] = this[field];
}
return result;
};
};

extendSerialize(Mocha.Suite.prototype, ['file']);
extendSerialize(Mocha.Hook.prototype, ['body', 'state']);
extendSerialize(Mocha.Test.prototype, ['pending', 'context']);

module.exports = {};
4 changes: 2 additions & 2 deletions test-functional/.mocharc.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"require": ["should"],
"reporter": "src/mochawesome",
"require": ["should", "./register"],
"reporter": "./src/mochawesome",
"reporterOption": []
}
5 changes: 5 additions & 0 deletions test-parallel/.mocharc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"require": ["should", "./register"],
"reporter": "./src/mochawesome",
"reporterOption": []
}
8 changes: 8 additions & 0 deletions test-parallel/test-1.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
describe('Parallel Test Suite #1', () => {
const attempts = [1, 2, 3, 4, 5];
attempts.forEach(attempt => {
it(`should take 1 second of time ${attempt}/${attempts.length}`, done => {
setTimeout(done, 1000);
});
});
});
8 changes: 8 additions & 0 deletions test-parallel/test-2.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
describe('Parallel Test Suite #2', () => {
const attempts = [1, 2, 3, 4, 5];
attempts.forEach(attempt => {
it(`should take 1 second of time ${attempt}/${attempts.length}`, done => {
setTimeout(done, 1000);
});
});
});
8 changes: 8 additions & 0 deletions test-parallel/test-3.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
describe('Parallel Test Suite #3', () => {
const attempts = [1, 2, 3, 4, 5];
attempts.forEach(attempt => {
it(`should take 1 second of time ${attempt}/${attempts.length}`, done => {
setTimeout(done, 1000);
});
});
});
Loading