Skip to content

Commit 7250399

Browse files
DavertMikDavertMik
andauthored
Feat/test meta remarks (#4732)
* 3.7.0-beta.1 * added notes for test object, use notes for heal reports * added notes & metadata for tests, improved output * fixed styles in failing tests --------- Co-authored-by: DavertMik <[email protected]>
1 parent c503ea2 commit 7250399

File tree

18 files changed

+428
-303
lines changed

18 files changed

+428
-303
lines changed

docs/ai.md

Lines changed: 88 additions & 95 deletions
Large diffs are not rendered by default.

lib/command/run-workers.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ module.exports = async function (workerCount, selectedRuns, options) {
3030
output.print(`CodeceptJS v${require('../codecept').version()} ${output.standWithUkraine()}`)
3131
output.print(`Running tests in ${output.styles.bold(numberOfWorkers)} workers...`)
3232
output.print()
33+
store.hasWorkers = true
3334

3435
const workers = new Workers(numberOfWorkers, config)
3536
workers.overrideConfig(overrideConfigs)
@@ -100,7 +101,6 @@ module.exports = async function (workerCount, selectedRuns, options) {
100101
if (options.verbose || options.debug) store.debugMode = true
101102

102103
if (options.verbose) {
103-
global.debugMode = true
104104
const { getMachineInfo } = require('./info')
105105
await getMachineInfo()
106106
}

lib/command/workers/runTests.js

Lines changed: 112 additions & 109 deletions
Original file line numberDiff line numberDiff line change
@@ -1,120 +1,120 @@
1-
const tty = require('tty');
1+
const tty = require('tty')
22

33
if (!tty.getWindowSize) {
44
// this is really old method, long removed from Node, but Mocha
55
// reporters fall back on it if they cannot use `process.stdout.getWindowSize`
66
// we need to polyfill it.
7-
tty.getWindowSize = () => [40, 80];
7+
tty.getWindowSize = () => [40, 80]
88
}
99

10-
const { parentPort, workerData } = require('worker_threads');
11-
const event = require('../../event');
12-
const container = require('../../container');
13-
const { getConfig } = require('../utils');
14-
const { tryOrDefault, deepMerge } = require('../../utils');
10+
const { parentPort, workerData } = require('worker_threads')
11+
const event = require('../../event')
12+
const container = require('../../container')
13+
const { getConfig } = require('../utils')
14+
const { tryOrDefault, deepMerge } = require('../../utils')
1515

16-
let stdout = '';
16+
let stdout = ''
1717

18-
const stderr = '';
18+
const stderr = ''
1919

2020
// Requiring of Codecept need to be after tty.getWindowSize is available.
21-
const Codecept = require(process.env.CODECEPT_CLASS_PATH || '../../codecept');
21+
const Codecept = require(process.env.CODECEPT_CLASS_PATH || '../../codecept')
2222

23-
const { options, tests, testRoot, workerIndex } = workerData;
23+
const { options, tests, testRoot, workerIndex } = workerData
2424

2525
// hide worker output
2626
if (!options.debug && !options.verbose)
2727
process.stdout.write = string => {
28-
stdout += string;
29-
return true;
30-
};
28+
stdout += string
29+
return true
30+
}
3131

32-
const overrideConfigs = tryOrDefault(() => JSON.parse(options.override), {});
32+
const overrideConfigs = tryOrDefault(() => JSON.parse(options.override), {})
3333

3434
// important deep merge so dynamic things e.g. functions on config are not overridden
35-
const config = deepMerge(getConfig(options.config || testRoot), overrideConfigs);
35+
const config = deepMerge(getConfig(options.config || testRoot), overrideConfigs)
3636

3737
// Load test and run
38-
const codecept = new Codecept(config, options);
39-
codecept.init(testRoot);
40-
codecept.loadTests();
41-
const mocha = container.mocha();
42-
filterTests();
38+
const codecept = new Codecept(config, options)
39+
codecept.init(testRoot)
40+
codecept.loadTests()
41+
const mocha = container.mocha()
42+
filterTests()
4343

4444
// run tests
45-
(async function () {
45+
;(async function () {
4646
if (mocha.suite.total()) {
47-
await runTests();
47+
await runTests()
4848
}
49-
})();
49+
})()
5050

5151
async function runTests() {
5252
try {
53-
await codecept.bootstrap();
53+
await codecept.bootstrap()
5454
} catch (err) {
55-
throw new Error(`Error while running bootstrap file :${err}`);
55+
throw new Error(`Error while running bootstrap file :${err}`)
5656
}
57-
listenToParentThread();
58-
initializeListeners();
59-
disablePause();
57+
listenToParentThread()
58+
initializeListeners()
59+
disablePause()
6060
try {
61-
await codecept.run();
61+
await codecept.run()
6262
} finally {
63-
await codecept.teardown();
63+
await codecept.teardown()
6464
}
6565
}
6666

6767
function filterTests() {
68-
const files = codecept.testFiles;
69-
mocha.files = files;
70-
mocha.loadFiles();
68+
const files = codecept.testFiles
69+
mocha.files = files
70+
mocha.loadFiles()
7171

7272
for (const suite of mocha.suite.suites) {
73-
suite.tests = suite.tests.filter(test => tests.indexOf(test.uid) >= 0);
73+
suite.tests = suite.tests.filter(test => tests.indexOf(test.uid) >= 0)
7474
}
7575
}
7676

7777
function initializeListeners() {
7878
function simplifyError(error) {
7979
if (error) {
80-
const { stack, uncaught, message, actual, expected } = error;
80+
const { stack, uncaught, message, actual, expected } = error
8181

8282
return {
8383
stack,
8484
uncaught,
8585
message,
8686
actual,
8787
expected,
88-
};
88+
}
8989
}
9090

91-
return null;
91+
return null
9292
}
9393
function simplifyTest(test, err = null) {
94-
test = { ...test };
94+
test = { ...test }
9595

9696
if (test.start && !test.duration) {
97-
const end = new Date();
98-
test.duration = end - test.start;
97+
const end = new Date()
98+
test.duration = end - test.start
9999
}
100100

101101
if (test.err) {
102-
err = simplifyError(test.err);
103-
test.status = 'failed';
102+
err = simplifyError(test.err)
103+
test.status = 'failed'
104104
} else if (err) {
105-
err = simplifyError(err);
106-
test.status = 'failed';
105+
err = simplifyError(err)
106+
test.status = 'failed'
107107
}
108-
const parent = {};
108+
const parent = {}
109109
if (test.parent) {
110-
parent.title = test.parent.title;
110+
parent.title = test.parent.title
111111
}
112112

113113
if (test.opts) {
114114
Object.keys(test.opts).forEach(k => {
115-
if (typeof test.opts[k] === 'object') delete test.opts[k];
116-
if (typeof test.opts[k] === 'function') delete test.opts[k];
117-
});
115+
if (typeof test.opts[k] === 'object') delete test.opts[k]
116+
if (typeof test.opts[k] === 'function') delete test.opts[k]
117+
})
118118
}
119119

120120
return {
@@ -125,30 +125,33 @@ function initializeListeners() {
125125
retries: test._retries,
126126
title: test.title,
127127
status: test.status,
128+
notes: test.notes || [],
129+
meta: test.meta || {},
130+
artifacts: test.artifacts || [],
128131
duration: test.duration || 0,
129132
err,
130133
parent,
131134
steps: test.steps && test.steps.length > 0 ? simplifyStepsInTestObject(test.steps, err) : [],
132-
};
135+
}
133136
}
134137

135138
function simplifyStepsInTestObject(steps, err) {
136-
steps = [...steps];
137-
const _steps = [];
139+
steps = [...steps]
140+
const _steps = []
138141

139142
for (step of steps) {
140-
const _args = [];
143+
const _args = []
141144

142145
if (step.args) {
143146
for (const arg of step.args) {
144147
// check if arg is a JOI object
145148
if (arg && arg.$_root) {
146-
_args.push(JSON.stringify(arg).slice(0, 300));
149+
_args.push(JSON.stringify(arg).slice(0, 300))
147150
// check if arg is a function
148151
} else if (arg && typeof arg === 'function') {
149-
_args.push(arg.name);
152+
_args.push(arg.name)
150153
} else {
151-
_args.push(arg);
154+
_args.push(arg)
152155
}
153156
}
154157
}
@@ -164,38 +167,38 @@ function initializeListeners() {
164167
finishedAt: step.finishedAt,
165168
duration: step.duration,
166169
err,
167-
});
170+
})
168171
}
169172

170-
return _steps;
173+
return _steps
171174
}
172175

173176
function simplifyStep(step, err = null) {
174-
step = { ...step };
177+
step = { ...step }
175178

176179
if (step.startTime && !step.duration) {
177-
const end = new Date();
178-
step.duration = end - step.startTime;
180+
const end = new Date()
181+
step.duration = end - step.startTime
179182
}
180183

181184
if (step.err) {
182-
err = simplifyError(step.err);
183-
step.status = 'failed';
185+
err = simplifyError(step.err)
186+
step.status = 'failed'
184187
} else if (err) {
185-
err = simplifyError(err);
186-
step.status = 'failed';
188+
err = simplifyError(err)
189+
step.status = 'failed'
187190
}
188191

189-
const parent = {};
192+
const parent = {}
190193
if (step.metaStep) {
191-
parent.title = step.metaStep.actor;
194+
parent.title = step.metaStep.actor
192195
}
193196

194197
if (step.opts) {
195198
Object.keys(step.opts).forEach(k => {
196-
if (typeof step.opts[k] === 'object') delete step.opts[k];
197-
if (typeof step.opts[k] === 'function') delete step.opts[k];
198-
});
199+
if (typeof step.opts[k] === 'object') delete step.opts[k]
200+
if (typeof step.opts[k] === 'function') delete step.opts[k]
201+
})
199202
}
200203

201204
return {
@@ -207,43 +210,43 @@ function initializeListeners() {
207210
err,
208211
parent,
209212
test: simplifyTest(step.test),
210-
};
213+
}
211214
}
212215

213-
collectStats();
216+
collectStats()
214217
// suite
215-
event.dispatcher.on(event.suite.before, suite => sendToParentThread({ event: event.suite.before, workerIndex, data: simplifyTest(suite) }));
216-
event.dispatcher.on(event.suite.after, suite => sendToParentThread({ event: event.suite.after, workerIndex, data: simplifyTest(suite) }));
218+
event.dispatcher.on(event.suite.before, suite => sendToParentThread({ event: event.suite.before, workerIndex, data: simplifyTest(suite) }))
219+
event.dispatcher.on(event.suite.after, suite => sendToParentThread({ event: event.suite.after, workerIndex, data: simplifyTest(suite) }))
217220

218221
// calculate duration
219-
event.dispatcher.on(event.test.started, test => (test.start = new Date()));
222+
event.dispatcher.on(event.test.started, test => (test.start = new Date()))
220223

221224
// tests
222-
event.dispatcher.on(event.test.before, test => sendToParentThread({ event: event.test.before, workerIndex, data: simplifyTest(test) }));
223-
event.dispatcher.on(event.test.after, test => sendToParentThread({ event: event.test.after, workerIndex, data: simplifyTest(test) }));
225+
event.dispatcher.on(event.test.before, test => sendToParentThread({ event: event.test.before, workerIndex, data: simplifyTest(test) }))
226+
event.dispatcher.on(event.test.after, test => sendToParentThread({ event: event.test.after, workerIndex, data: simplifyTest(test) }))
224227
// we should force-send correct errors to prevent race condition
225-
event.dispatcher.on(event.test.finished, (test, err) => sendToParentThread({ event: event.test.finished, workerIndex, data: simplifyTest(test, err) }));
226-
event.dispatcher.on(event.test.failed, (test, err) => sendToParentThread({ event: event.test.failed, workerIndex, data: simplifyTest(test, err) }));
227-
event.dispatcher.on(event.test.passed, (test, err) => sendToParentThread({ event: event.test.passed, workerIndex, data: simplifyTest(test, err) }));
228-
event.dispatcher.on(event.test.started, test => sendToParentThread({ event: event.test.started, workerIndex, data: simplifyTest(test) }));
229-
event.dispatcher.on(event.test.skipped, test => sendToParentThread({ event: event.test.skipped, workerIndex, data: simplifyTest(test) }));
228+
event.dispatcher.on(event.test.finished, (test, err) => sendToParentThread({ event: event.test.finished, workerIndex, data: simplifyTest(test, err) }))
229+
event.dispatcher.on(event.test.failed, (test, err) => sendToParentThread({ event: event.test.failed, workerIndex, data: simplifyTest(test, err) }))
230+
event.dispatcher.on(event.test.passed, (test, err) => sendToParentThread({ event: event.test.passed, workerIndex, data: simplifyTest(test, err) }))
231+
event.dispatcher.on(event.test.started, test => sendToParentThread({ event: event.test.started, workerIndex, data: simplifyTest(test) }))
232+
event.dispatcher.on(event.test.skipped, test => sendToParentThread({ event: event.test.skipped, workerIndex, data: simplifyTest(test) }))
230233

231234
// steps
232-
event.dispatcher.on(event.step.finished, step => sendToParentThread({ event: event.step.finished, workerIndex, data: simplifyStep(step) }));
233-
event.dispatcher.on(event.step.started, step => sendToParentThread({ event: event.step.started, workerIndex, data: simplifyStep(step) }));
234-
event.dispatcher.on(event.step.passed, step => sendToParentThread({ event: event.step.passed, workerIndex, data: simplifyStep(step) }));
235-
event.dispatcher.on(event.step.failed, step => sendToParentThread({ event: event.step.failed, workerIndex, data: simplifyStep(step) }));
235+
event.dispatcher.on(event.step.finished, step => sendToParentThread({ event: event.step.finished, workerIndex, data: simplifyStep(step) }))
236+
event.dispatcher.on(event.step.started, step => sendToParentThread({ event: event.step.started, workerIndex, data: simplifyStep(step) }))
237+
event.dispatcher.on(event.step.passed, step => sendToParentThread({ event: event.step.passed, workerIndex, data: simplifyStep(step) }))
238+
event.dispatcher.on(event.step.failed, step => sendToParentThread({ event: event.step.failed, workerIndex, data: simplifyStep(step) }))
236239

237-
event.dispatcher.on(event.hook.failed, (test, err) => sendToParentThread({ event: event.hook.failed, workerIndex, data: simplifyTest(test, err) }));
238-
event.dispatcher.on(event.hook.passed, (test, err) => sendToParentThread({ event: event.hook.passed, workerIndex, data: simplifyTest(test, err) }));
239-
event.dispatcher.on(event.all.failures, data => sendToParentThread({ event: event.all.failures, workerIndex, data }));
240+
event.dispatcher.on(event.hook.failed, (test, err) => sendToParentThread({ event: event.hook.failed, workerIndex, data: simplifyTest(test, err) }))
241+
event.dispatcher.on(event.hook.passed, (test, err) => sendToParentThread({ event: event.hook.passed, workerIndex, data: simplifyTest(test, err) }))
242+
event.dispatcher.on(event.all.failures, data => sendToParentThread({ event: event.all.failures, workerIndex, data }))
240243

241244
// all
242-
event.dispatcher.once(event.all.result, () => parentPort.close());
245+
event.dispatcher.once(event.all.result, () => parentPort.close())
243246
}
244247

245248
function disablePause() {
246-
global.pause = () => {};
249+
global.pause = () => {}
247250
}
248251

249252
function collectStats() {
@@ -253,36 +256,36 @@ function collectStats() {
253256
skipped: 0,
254257
tests: 0,
255258
pending: 0,
256-
};
259+
}
257260
event.dispatcher.on(event.test.skipped, () => {
258-
stats.skipped++;
259-
});
261+
stats.skipped++
262+
})
260263
event.dispatcher.on(event.test.passed, () => {
261-
stats.passes++;
262-
});
264+
stats.passes++
265+
})
263266
event.dispatcher.on(event.test.failed, test => {
264267
if (test.ctx._runnable.title.includes('hook: AfterSuite')) {
265-
stats.failedHooks += 1;
268+
stats.failedHooks += 1
266269
}
267-
stats.failures++;
268-
});
270+
stats.failures++
271+
})
269272
event.dispatcher.on(event.test.skipped, () => {
270-
stats.pending++;
271-
});
273+
stats.pending++
274+
})
272275
event.dispatcher.on(event.test.finished, () => {
273-
stats.tests++;
274-
});
276+
stats.tests++
277+
})
275278
event.dispatcher.once(event.all.after, () => {
276-
sendToParentThread({ event: event.all.after, data: stats });
277-
});
279+
sendToParentThread({ event: event.all.after, data: stats })
280+
})
278281
}
279282

280283
function sendToParentThread(data) {
281-
parentPort.postMessage(data);
284+
parentPort.postMessage(data)
282285
}
283286

284287
function listenToParentThread() {
285288
parentPort.on('message', eventData => {
286-
container.append({ support: eventData.data });
287-
});
289+
container.append({ support: eventData.data })
290+
})
288291
}

0 commit comments

Comments
 (0)