Skip to content

Commit

Permalink
fix(workers): event improvements (#3953)
Browse files Browse the repository at this point in the history
  • Loading branch information
kobenguyent authored Dec 5, 2023
1 parent 7317709 commit 8926f9c
Show file tree
Hide file tree
Showing 7 changed files with 172 additions and 10 deletions.
1 change: 1 addition & 0 deletions docs/internal-api.md
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ Available events:
* `event.all.result` - when results are printed
* `event.workers.before` - before spawning workers in parallel run
* `event.workers.after` - after workers finished in parallel run
* `event.workers.result` - test results after workers finished in parallel run


> *sync* - means that event is fired in the moment of the action happening.
Expand Down
116 changes: 114 additions & 2 deletions docs/parallel.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,124 @@ This command is similar to `run`, however, steps output can't be shown in worker

Each worker spins an instance of CodeceptJS, executes a group of tests, and sends back report to the main process.

By default the tests are assigned one by one to the available workers this may lead to multiple execution of `BeforeSuite()`. Use the option `--suites` to assigne the suites one by one to the workers.
By default, the tests are assigned one by one to the available workers this may lead to multiple execution of `BeforeSuite()`. Use the option `--suites` to assign the suites one by one to the workers.

```sh
npx codeceptjs run-workers --suites 2
```

## Test stats with Parallel Execution by Workers

```js
const { event } = require('codeceptjs');

module.exports = function() {

event.dispatcher.on(event.workers.result, function (result) {

console.log(result);

});
}

// in console log
FAIL | 7 passed, 1 failed, 1 skipped // 2s
{
"tests": {
"passed": [
{
"type": "test",
"title": "Assert @C3",
"body": "() => { }",
"async": 0,
"sync": true,
"_timeout": 2000,
"_slow": 75,
"_retries": -1,
"timedOut": false,
"_currentRetry": 0,
"pending": false,
"opts": {},
"tags": [
"@C3"
],
"uid": "xe4q1HdqpRrZG5dPe0JG+A",
"workerIndex": 3,
"retries": -1,
"duration": 493,
"err": null,
"parent": {
"title": "My",
"ctx": {},
"suites": [],
"tests": [],
"root": false,
"pending": false,
"_retries": -1,
"_beforeEach": [],
"_beforeAll": [],
"_afterEach": [],
"_afterAll": [],
"_timeout": 2000,
"_slow": 75,
"_bail": false,
"_onlyTests": [],
"_onlySuites": [],
"delayed": false
},
"steps": [
{
"actor": "I",
"name": "amOnPage",
"status": "success",
"agrs": [
"https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/POST"
],
"startedAt": 1698760652610,
"startTime": 1698760652611,
"endTime": 1698760653098,
"finishedAt": 1698760653098,
"duration": 488
},
{
"actor": "I",
"name": "grabCurrentUrl",
"status": "success",
"agrs": [],
"startedAt": 1698760653098,
"startTime": 1698760653098,
"endTime": 1698760653099,
"finishedAt": 1698760653099,
"duration": 1
}
]
}
],
"failed": [],
"skipped": []
}
}
```

CodeceptJS also exposes the env var `process.env.RUNS_WITH_WORKERS` when running tests with `run-workers` command so that you could handle the events better in your plugins/helpers

```js
const { event } = require('codeceptjs');

module.exports = function() {
// this event would trigger the `_publishResultsToTestrail` when running `run-workers` command
event.dispatcher.on(event.workers.result, async () => {
await _publishResultsToTestrail();
});

// this event would not trigger the `_publishResultsToTestrail` multiple times when running `run-workers` command
event.dispatcher.on(event.all.result, async () => {
// when running `run` command, this env var is undefined
if (!process.env.RUNS_WITH_WORKERS) await _publishResultsToTestrail();
});
}
```

## Parallel Execution by Workers on Multiple Browsers

To run tests in parallel across multiple browsers, modify your `codecept.conf.js` file to configure multiple browsers on which you want to run your tests and your tests will run across multiple browsers.
Expand Down Expand Up @@ -236,7 +348,7 @@ customWorkers.on(event.all.result, () => {
### Emitting messages to the parent worker
Child workers can send non test events to the main process. This is useful if you want to pass along information not related to the tests event cycles itself such as `event.test.success`.
Child workers can send non-test events to the main process. This is useful if you want to pass along information not related to the tests event cycles itself such as `event.test.success`.
```js
// inside main process
Expand Down
34 changes: 30 additions & 4 deletions lib/command/run-workers.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@ const Workers = require('../workers');
module.exports = async function (workerCount, selectedRuns, options) {
process.env.profile = options.profile;

const suiteArr = [];
const passedTestArr = [];
const failedTestArr = [];
const skippedTestArr = [];

const { config: testConfig, override = '' } = options;
const overrideConfigs = tryOrDefault(() => JSON.parse(override), {});
const by = options.suites ? 'suite' : 'test';
Expand All @@ -26,15 +31,36 @@ module.exports = async function (workerCount, selectedRuns, options) {

const workers = new Workers(numberOfWorkers, config);
workers.overrideConfig(overrideConfigs);
workers.on(event.test.failed, (failedTest) => {
output.test.failed(failedTest);

workers.on(event.suite.before, (suite) => {
suiteArr.push(suite);
});

workers.on(event.test.failed, (test) => {
failedTestArr.push(test);
output.test.failed(test);
});

workers.on(event.test.passed, (test) => {
passedTestArr.push(test);
output.test.passed(test);
});

workers.on(event.test.passed, (successTest) => {
output.test.passed(successTest);
workers.on(event.test.skipped, (test) => {
skippedTestArr.push(test);
output.test.passed(test);
});

workers.on(event.all.result, () => {
// expose test stats after all workers finished their execution
event.dispatcher.emit(event.workers.result, {
suites: suiteArr,
tests: {
passed: passedTestArr,
failed: failedTestArr,
skipped: skippedTestArr,
},
});
workers.printResults();
});

Expand Down
23 changes: 23 additions & 0 deletions lib/command/workers/runTests.js
Original file line number Diff line number Diff line change
Expand Up @@ -132,9 +132,32 @@ function initializeListeners() {
duration: test.duration || 0,
err,
parent,
steps: test.steps ? simplifyStepsInTestObject(test.steps, err) : [],
};
}

function simplifyStepsInTestObject(steps, err) {
steps = [...steps];
const _steps = [];

for (step of steps) {
_steps.push({
actor: step.actor,
name: step.name,
status: step.status,
agrs: step.args,
startedAt: step.startedAt,
startTime: step.startTime,
endTime: step.endTime,
finishedAt: step.finishedAt,
duration: step.duration,
err,
});
}

return _steps;
}

function simplifyStep(step, err = null) {
step = { ...step };

Expand Down
2 changes: 2 additions & 0 deletions lib/event.js
Original file line number Diff line number Diff line change
Expand Up @@ -127,10 +127,12 @@ module.exports = {
* @inner
* @property {'workers.before'} before
* @property {'workers.after'} after
* @property {'workers.result'} result
*/
workers: {
before: 'workers.before',
after: 'workers.after',
result: 'workers.result',
},

/**
Expand Down
2 changes: 2 additions & 0 deletions lib/workers.js
Original file line number Diff line number Diff line change
Expand Up @@ -359,6 +359,7 @@ class Workers extends EventEmitter {
this.stats.start = new Date();
recorder.startUnlessRunning();
event.dispatcher.emit(event.workers.before);
process.env.RUNS_WITH_WORKERS = 'true';
recorder.add('starting workers', () => {
for (const worker of this.workers) {
const workerThread = createWorker(worker);
Expand Down Expand Up @@ -492,6 +493,7 @@ class Workers extends EventEmitter {
}

output.result(this.stats.passes, this.stats.failures, this.stats.pending, ms(this.stats.duration));
process.env.RUNS_WITH_WORKERS = 'false';
}
}

Expand Down
4 changes: 0 additions & 4 deletions test/unit/worker_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,6 @@ describe('Workers', function () {
global.codecept_dir = path.join(__dirname, '/../data/sandbox');
});

beforeEach(function () {
this.timeout(40000);
});

it('should run simple worker', (done) => {
const workerConfig = {
by: 'test',
Expand Down

0 comments on commit 8926f9c

Please sign in to comment.