Skip to content

Commit 8926f9c

Browse files
authored
fix(workers): event improvements (#3953)
1 parent 7317709 commit 8926f9c

File tree

7 files changed

+172
-10
lines changed

7 files changed

+172
-10
lines changed

docs/internal-api.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,7 @@ Available events:
119119
* `event.all.result` - when results are printed
120120
* `event.workers.before` - before spawning workers in parallel run
121121
* `event.workers.after` - after workers finished in parallel run
122+
* `event.workers.result` - test results after workers finished in parallel run
122123

123124

124125
> *sync* - means that event is fired in the moment of the action happening.

docs/parallel.md

Lines changed: 114 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,124 @@ This command is similar to `run`, however, steps output can't be shown in worker
2626

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

29-
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.
29+
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.
3030

3131
```sh
3232
npx codeceptjs run-workers --suites 2
3333
```
3434

35+
## Test stats with Parallel Execution by Workers
36+
37+
```js
38+
const { event } = require('codeceptjs');
39+
40+
module.exports = function() {
41+
42+
event.dispatcher.on(event.workers.result, function (result) {
43+
44+
console.log(result);
45+
46+
});
47+
}
48+
49+
// in console log
50+
FAIL | 7 passed, 1 failed, 1 skipped // 2s
51+
{
52+
"tests": {
53+
"passed": [
54+
{
55+
"type": "test",
56+
"title": "Assert @C3",
57+
"body": "() => { }",
58+
"async": 0,
59+
"sync": true,
60+
"_timeout": 2000,
61+
"_slow": 75,
62+
"_retries": -1,
63+
"timedOut": false,
64+
"_currentRetry": 0,
65+
"pending": false,
66+
"opts": {},
67+
"tags": [
68+
"@C3"
69+
],
70+
"uid": "xe4q1HdqpRrZG5dPe0JG+A",
71+
"workerIndex": 3,
72+
"retries": -1,
73+
"duration": 493,
74+
"err": null,
75+
"parent": {
76+
"title": "My",
77+
"ctx": {},
78+
"suites": [],
79+
"tests": [],
80+
"root": false,
81+
"pending": false,
82+
"_retries": -1,
83+
"_beforeEach": [],
84+
"_beforeAll": [],
85+
"_afterEach": [],
86+
"_afterAll": [],
87+
"_timeout": 2000,
88+
"_slow": 75,
89+
"_bail": false,
90+
"_onlyTests": [],
91+
"_onlySuites": [],
92+
"delayed": false
93+
},
94+
"steps": [
95+
{
96+
"actor": "I",
97+
"name": "amOnPage",
98+
"status": "success",
99+
"agrs": [
100+
"https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/POST"
101+
],
102+
"startedAt": 1698760652610,
103+
"startTime": 1698760652611,
104+
"endTime": 1698760653098,
105+
"finishedAt": 1698760653098,
106+
"duration": 488
107+
},
108+
{
109+
"actor": "I",
110+
"name": "grabCurrentUrl",
111+
"status": "success",
112+
"agrs": [],
113+
"startedAt": 1698760653098,
114+
"startTime": 1698760653098,
115+
"endTime": 1698760653099,
116+
"finishedAt": 1698760653099,
117+
"duration": 1
118+
}
119+
]
120+
}
121+
],
122+
"failed": [],
123+
"skipped": []
124+
}
125+
}
126+
```
127+
128+
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
129+
130+
```js
131+
const { event } = require('codeceptjs');
132+
133+
module.exports = function() {
134+
// this event would trigger the `_publishResultsToTestrail` when running `run-workers` command
135+
event.dispatcher.on(event.workers.result, async () => {
136+
await _publishResultsToTestrail();
137+
});
138+
139+
// this event would not trigger the `_publishResultsToTestrail` multiple times when running `run-workers` command
140+
event.dispatcher.on(event.all.result, async () => {
141+
// when running `run` command, this env var is undefined
142+
if (!process.env.RUNS_WITH_WORKERS) await _publishResultsToTestrail();
143+
});
144+
}
145+
```
146+
35147
## Parallel Execution by Workers on Multiple Browsers
36148

37149
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.
@@ -236,7 +348,7 @@ customWorkers.on(event.all.result, () => {
236348
237349
### Emitting messages to the parent worker
238350
239-
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`.
351+
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`.
240352
241353
```js
242354
// inside main process

lib/command/run-workers.js

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,11 @@ const Workers = require('../workers');
77
module.exports = async function (workerCount, selectedRuns, options) {
88
process.env.profile = options.profile;
99

10+
const suiteArr = [];
11+
const passedTestArr = [];
12+
const failedTestArr = [];
13+
const skippedTestArr = [];
14+
1015
const { config: testConfig, override = '' } = options;
1116
const overrideConfigs = tryOrDefault(() => JSON.parse(override), {});
1217
const by = options.suites ? 'suite' : 'test';
@@ -26,15 +31,36 @@ module.exports = async function (workerCount, selectedRuns, options) {
2631

2732
const workers = new Workers(numberOfWorkers, config);
2833
workers.overrideConfig(overrideConfigs);
29-
workers.on(event.test.failed, (failedTest) => {
30-
output.test.failed(failedTest);
34+
35+
workers.on(event.suite.before, (suite) => {
36+
suiteArr.push(suite);
37+
});
38+
39+
workers.on(event.test.failed, (test) => {
40+
failedTestArr.push(test);
41+
output.test.failed(test);
42+
});
43+
44+
workers.on(event.test.passed, (test) => {
45+
passedTestArr.push(test);
46+
output.test.passed(test);
3147
});
3248

33-
workers.on(event.test.passed, (successTest) => {
34-
output.test.passed(successTest);
49+
workers.on(event.test.skipped, (test) => {
50+
skippedTestArr.push(test);
51+
output.test.passed(test);
3552
});
3653

3754
workers.on(event.all.result, () => {
55+
// expose test stats after all workers finished their execution
56+
event.dispatcher.emit(event.workers.result, {
57+
suites: suiteArr,
58+
tests: {
59+
passed: passedTestArr,
60+
failed: failedTestArr,
61+
skipped: skippedTestArr,
62+
},
63+
});
3864
workers.printResults();
3965
});
4066

lib/command/workers/runTests.js

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,9 +132,32 @@ function initializeListeners() {
132132
duration: test.duration || 0,
133133
err,
134134
parent,
135+
steps: test.steps ? simplifyStepsInTestObject(test.steps, err) : [],
135136
};
136137
}
137138

139+
function simplifyStepsInTestObject(steps, err) {
140+
steps = [...steps];
141+
const _steps = [];
142+
143+
for (step of steps) {
144+
_steps.push({
145+
actor: step.actor,
146+
name: step.name,
147+
status: step.status,
148+
agrs: step.args,
149+
startedAt: step.startedAt,
150+
startTime: step.startTime,
151+
endTime: step.endTime,
152+
finishedAt: step.finishedAt,
153+
duration: step.duration,
154+
err,
155+
});
156+
}
157+
158+
return _steps;
159+
}
160+
138161
function simplifyStep(step, err = null) {
139162
step = { ...step };
140163

lib/event.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,10 +127,12 @@ module.exports = {
127127
* @inner
128128
* @property {'workers.before'} before
129129
* @property {'workers.after'} after
130+
* @property {'workers.result'} result
130131
*/
131132
workers: {
132133
before: 'workers.before',
133134
after: 'workers.after',
135+
result: 'workers.result',
134136
},
135137

136138
/**

lib/workers.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -359,6 +359,7 @@ class Workers extends EventEmitter {
359359
this.stats.start = new Date();
360360
recorder.startUnlessRunning();
361361
event.dispatcher.emit(event.workers.before);
362+
process.env.RUNS_WITH_WORKERS = 'true';
362363
recorder.add('starting workers', () => {
363364
for (const worker of this.workers) {
364365
const workerThread = createWorker(worker);
@@ -492,6 +493,7 @@ class Workers extends EventEmitter {
492493
}
493494

494495
output.result(this.stats.passes, this.stats.failures, this.stats.pending, ms(this.stats.duration));
496+
process.env.RUNS_WITH_WORKERS = 'false';
495497
}
496498
}
497499

test/unit/worker_test.js

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,6 @@ describe('Workers', function () {
99
global.codecept_dir = path.join(__dirname, '/../data/sandbox');
1010
});
1111

12-
beforeEach(function () {
13-
this.timeout(40000);
14-
});
15-
1612
it('should run simple worker', (done) => {
1713
const workerConfig = {
1814
by: 'test',

0 commit comments

Comments
 (0)