codeceptjs 4.0.0-beta.9.esm-aria → 4.0.1-beta.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/actor.js +12 -8
- package/lib/codecept.js +25 -1
- package/lib/command/init.js +2 -1
- package/lib/config.js +7 -22
- package/lib/container.js +116 -22
- package/lib/helper/GraphQL.js +6 -4
- package/lib/helper/JSONResponse.js +3 -4
- package/lib/helper/Playwright.js +19 -35
- package/lib/helper/REST.js +15 -9
- package/lib/listener/config.js +11 -3
- package/lib/mocha/gherkin.js +4 -4
- package/lib/plugin/htmlReporter.js +4 -4
- package/lib/step/meta.js +18 -1
- package/lib/utils/loaderCheck.js +124 -0
- package/lib/utils/typescript.js +237 -0
- package/lib/workers.js +36 -41
- package/package.json +10 -1
- package/typings/promiseBasedTypes.d.ts +4 -0
- package/typings/types.d.ts +4 -0
package/lib/workers.js
CHANGED
|
@@ -5,6 +5,7 @@ import { mkdirp } from 'mkdirp'
|
|
|
5
5
|
import { Worker } from 'worker_threads'
|
|
6
6
|
import { EventEmitter } from 'events'
|
|
7
7
|
import ms from 'ms'
|
|
8
|
+
import merge from 'lodash.merge'
|
|
8
9
|
|
|
9
10
|
const __filename = fileURLToPath(import.meta.url)
|
|
10
11
|
const __dirname = dirname(__filename)
|
|
@@ -66,21 +67,21 @@ const createWorker = (workerObject, isPoolMode = false) => {
|
|
|
66
67
|
stdout: true,
|
|
67
68
|
stderr: true,
|
|
68
69
|
})
|
|
69
|
-
|
|
70
|
+
|
|
70
71
|
// Pipe worker stdout/stderr to main process
|
|
71
72
|
if (worker.stdout) {
|
|
72
73
|
worker.stdout.setEncoding('utf8')
|
|
73
|
-
worker.stdout.on('data',
|
|
74
|
+
worker.stdout.on('data', data => {
|
|
74
75
|
process.stdout.write(data)
|
|
75
76
|
})
|
|
76
77
|
}
|
|
77
78
|
if (worker.stderr) {
|
|
78
79
|
worker.stderr.setEncoding('utf8')
|
|
79
|
-
worker.stderr.on('data',
|
|
80
|
+
worker.stderr.on('data', data => {
|
|
80
81
|
process.stderr.write(data)
|
|
81
82
|
})
|
|
82
83
|
}
|
|
83
|
-
|
|
84
|
+
|
|
84
85
|
worker.on('error', err => {
|
|
85
86
|
console.error(`[Main] Worker Error:`, err)
|
|
86
87
|
output.error(`Worker Error: ${err.stack}`)
|
|
@@ -221,13 +222,13 @@ class WorkerObject {
|
|
|
221
222
|
|
|
222
223
|
addConfig(config) {
|
|
223
224
|
const oldConfig = JSON.parse(this.options.override || '{}')
|
|
224
|
-
|
|
225
|
+
|
|
225
226
|
// Remove customLocatorStrategies from both old and new config before JSON serialization
|
|
226
227
|
// since functions cannot be serialized and will be lost, causing workers to have empty strategies
|
|
227
228
|
const configWithoutFunctions = { ...config }
|
|
228
|
-
|
|
229
|
+
|
|
229
230
|
// Clean both old and new config
|
|
230
|
-
const cleanConfig =
|
|
231
|
+
const cleanConfig = cfg => {
|
|
231
232
|
if (cfg.helpers) {
|
|
232
233
|
cfg.helpers = { ...cfg.helpers }
|
|
233
234
|
Object.keys(cfg.helpers).forEach(helperName => {
|
|
@@ -239,14 +240,12 @@ class WorkerObject {
|
|
|
239
240
|
}
|
|
240
241
|
return cfg
|
|
241
242
|
}
|
|
242
|
-
|
|
243
|
+
|
|
243
244
|
const cleanedOldConfig = cleanConfig(oldConfig)
|
|
244
245
|
const cleanedNewConfig = cleanConfig(configWithoutFunctions)
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
...cleanedNewConfig,
|
|
249
|
-
}
|
|
246
|
+
|
|
247
|
+
// Deep merge configurations to preserve all helpers from base config
|
|
248
|
+
const newConfig = merge({}, cleanedOldConfig, cleanedNewConfig)
|
|
250
249
|
this.options.override = JSON.stringify(newConfig)
|
|
251
250
|
}
|
|
252
251
|
|
|
@@ -280,8 +279,8 @@ class Workers extends EventEmitter {
|
|
|
280
279
|
this.setMaxListeners(50)
|
|
281
280
|
this.codeceptPromise = initializeCodecept(config.testConfig, config.options)
|
|
282
281
|
this.codecept = null
|
|
283
|
-
this.config = config
|
|
284
|
-
this.numberOfWorkersRequested = numberOfWorkers
|
|
282
|
+
this.config = config // Save config
|
|
283
|
+
this.numberOfWorkersRequested = numberOfWorkers // Save requested worker count
|
|
285
284
|
this.options = config.options || {}
|
|
286
285
|
this.errors = []
|
|
287
286
|
this.numberOfWorkers = 0
|
|
@@ -304,11 +303,8 @@ class Workers extends EventEmitter {
|
|
|
304
303
|
// Initialize workers in these cases:
|
|
305
304
|
// 1. Positive number requested AND no manual workers pre-spawned
|
|
306
305
|
// 2. Function-based grouping (indicated by negative number) AND no manual workers pre-spawned
|
|
307
|
-
const shouldAutoInit = this.workers.length === 0 && (
|
|
308
|
-
|
|
309
|
-
(this.numberOfWorkersRequested < 0 && isFunction(this.config.by))
|
|
310
|
-
)
|
|
311
|
-
|
|
306
|
+
const shouldAutoInit = this.workers.length === 0 && ((Number.isInteger(this.numberOfWorkersRequested) && this.numberOfWorkersRequested > 0) || (this.numberOfWorkersRequested < 0 && isFunction(this.config.by)))
|
|
307
|
+
|
|
312
308
|
if (shouldAutoInit) {
|
|
313
309
|
this._initWorkers(this.numberOfWorkersRequested, this.config)
|
|
314
310
|
}
|
|
@@ -319,7 +315,7 @@ class Workers extends EventEmitter {
|
|
|
319
315
|
this.splitTestsByGroups(numberOfWorkers, config)
|
|
320
316
|
// For function-based grouping, use the actual number of test groups created
|
|
321
317
|
const actualNumberOfWorkers = isFunction(config.by) ? this.testGroups.length : numberOfWorkers
|
|
322
|
-
this.workers = createWorkerObjects(this.testGroups, this.codecept.config, config.testConfig, config.options, config.selectedRuns)
|
|
318
|
+
this.workers = createWorkerObjects(this.testGroups, this.codecept.config, getTestRoot(config.testConfig), config.options, config.selectedRuns)
|
|
323
319
|
this.numberOfWorkers = this.workers.length
|
|
324
320
|
}
|
|
325
321
|
|
|
@@ -371,9 +367,9 @@ class Workers extends EventEmitter {
|
|
|
371
367
|
* @param {Number} numberOfWorkers
|
|
372
368
|
*/
|
|
373
369
|
createGroupsOfTests(numberOfWorkers) {
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
370
|
+
// If Codecept isn't initialized yet, return empty groups as a safe fallback
|
|
371
|
+
if (!this.codecept) return populateGroups(numberOfWorkers)
|
|
372
|
+
const files = this.codecept.testFiles
|
|
377
373
|
const mocha = Container.mocha()
|
|
378
374
|
mocha.files = files
|
|
379
375
|
mocha.loadFiles()
|
|
@@ -430,7 +426,7 @@ class Workers extends EventEmitter {
|
|
|
430
426
|
for (const file of files) {
|
|
431
427
|
this.testPool.push(file)
|
|
432
428
|
}
|
|
433
|
-
|
|
429
|
+
|
|
434
430
|
this.testPoolInitialized = true
|
|
435
431
|
}
|
|
436
432
|
|
|
@@ -443,7 +439,7 @@ class Workers extends EventEmitter {
|
|
|
443
439
|
if (!this.testPoolInitialized) {
|
|
444
440
|
this._initializeTestPool()
|
|
445
441
|
}
|
|
446
|
-
|
|
442
|
+
|
|
447
443
|
return this.testPool.shift()
|
|
448
444
|
}
|
|
449
445
|
|
|
@@ -451,9 +447,9 @@ class Workers extends EventEmitter {
|
|
|
451
447
|
* @param {Number} numberOfWorkers
|
|
452
448
|
*/
|
|
453
449
|
createGroupsOfSuites(numberOfWorkers) {
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
450
|
+
// If Codecept isn't initialized yet, return empty groups as a safe fallback
|
|
451
|
+
if (!this.codecept) return populateGroups(numberOfWorkers)
|
|
452
|
+
const files = this.codecept.testFiles
|
|
457
453
|
const groups = populateGroups(numberOfWorkers)
|
|
458
454
|
|
|
459
455
|
const mocha = Container.mocha()
|
|
@@ -494,7 +490,7 @@ class Workers extends EventEmitter {
|
|
|
494
490
|
recorder.startUnlessRunning()
|
|
495
491
|
event.dispatcher.emit(event.workers.before)
|
|
496
492
|
process.env.RUNS_WITH_WORKERS = 'true'
|
|
497
|
-
|
|
493
|
+
|
|
498
494
|
// Create workers and set up message handlers immediately (not in recorder queue)
|
|
499
495
|
// This prevents a race condition where workers start sending messages before handlers are attached
|
|
500
496
|
const workerThreads = []
|
|
@@ -503,11 +499,11 @@ class Workers extends EventEmitter {
|
|
|
503
499
|
this._listenWorkerEvents(workerThread)
|
|
504
500
|
workerThreads.push(workerThread)
|
|
505
501
|
}
|
|
506
|
-
|
|
502
|
+
|
|
507
503
|
recorder.add('workers started', () => {
|
|
508
504
|
// Workers are already running, this is just a placeholder step
|
|
509
505
|
})
|
|
510
|
-
|
|
506
|
+
|
|
511
507
|
return new Promise(resolve => {
|
|
512
508
|
this.on('end', resolve)
|
|
513
509
|
})
|
|
@@ -591,7 +587,7 @@ class Workers extends EventEmitter {
|
|
|
591
587
|
// Otherwise skip - we'll emit based on finished state
|
|
592
588
|
break
|
|
593
589
|
case event.test.passed:
|
|
594
|
-
// Skip individual passed events - we'll emit based on finished state
|
|
590
|
+
// Skip individual passed events - we'll emit based on finished state
|
|
595
591
|
break
|
|
596
592
|
case event.test.skipped:
|
|
597
593
|
this.emit(event.test.skipped, deserializeTest(message.data))
|
|
@@ -602,15 +598,15 @@ class Workers extends EventEmitter {
|
|
|
602
598
|
const data = message.data
|
|
603
599
|
const uid = data?.uid
|
|
604
600
|
const isFailed = !!data?.err || data?.state === 'failed'
|
|
605
|
-
|
|
601
|
+
|
|
606
602
|
if (uid) {
|
|
607
603
|
// Track states for each test UID
|
|
608
604
|
if (!this._testStates) this._testStates = new Map()
|
|
609
|
-
|
|
605
|
+
|
|
610
606
|
if (!this._testStates.has(uid)) {
|
|
611
607
|
this._testStates.set(uid, { states: [], lastData: data })
|
|
612
608
|
}
|
|
613
|
-
|
|
609
|
+
|
|
614
610
|
const testState = this._testStates.get(uid)
|
|
615
611
|
testState.states.push({ isFailed, data })
|
|
616
612
|
testState.lastData = data
|
|
@@ -622,7 +618,7 @@ class Workers extends EventEmitter {
|
|
|
622
618
|
this.emit(event.test.passed, deserializeTest(data))
|
|
623
619
|
}
|
|
624
620
|
}
|
|
625
|
-
|
|
621
|
+
|
|
626
622
|
this.emit(event.test.finished, deserializeTest(data))
|
|
627
623
|
}
|
|
628
624
|
break
|
|
@@ -682,11 +678,10 @@ class Workers extends EventEmitter {
|
|
|
682
678
|
// For tests with retries configured, emit all failures + final success
|
|
683
679
|
// For tests without retries, emit only final state
|
|
684
680
|
const lastState = states[states.length - 1]
|
|
685
|
-
|
|
681
|
+
|
|
686
682
|
// Check if this test had retries by looking for failure followed by success
|
|
687
|
-
const hasRetryPattern = states.length > 1 &&
|
|
688
|
-
|
|
689
|
-
|
|
683
|
+
const hasRetryPattern = states.length > 1 && states.some((s, i) => s.isFailed && i < states.length - 1 && !states[i + 1].isFailed)
|
|
684
|
+
|
|
690
685
|
if (hasRetryPattern) {
|
|
691
686
|
// Emit all intermediate failures and final success for retries
|
|
692
687
|
for (const state of states) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "codeceptjs",
|
|
3
|
-
"version": "4.0.
|
|
3
|
+
"version": "4.0.1-beta.10",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Supercharged End 2 End Testing Framework for NodeJS",
|
|
6
6
|
"keywords": [
|
|
@@ -183,6 +183,7 @@
|
|
|
183
183
|
"ts-node": "10.9.2",
|
|
184
184
|
"tsd": "^0.33.0",
|
|
185
185
|
"tsd-jsdoc": "2.5.0",
|
|
186
|
+
"tsx": "^4.19.2",
|
|
186
187
|
"typedoc": "0.28.13",
|
|
187
188
|
"typedoc-plugin-markdown": "4.9.0",
|
|
188
189
|
"typescript": "5.8.3",
|
|
@@ -191,6 +192,14 @@
|
|
|
191
192
|
"xml2js": "0.6.2",
|
|
192
193
|
"xpath": "0.0.34"
|
|
193
194
|
},
|
|
195
|
+
"peerDependencies": {
|
|
196
|
+
"tsx": "^4.0.0"
|
|
197
|
+
},
|
|
198
|
+
"peerDependenciesMeta": {
|
|
199
|
+
"tsx": {
|
|
200
|
+
"optional": true
|
|
201
|
+
}
|
|
202
|
+
},
|
|
194
203
|
"engines": {
|
|
195
204
|
"node": ">=16.0",
|
|
196
205
|
"npm": ">=5.6.0"
|
|
@@ -2742,6 +2742,7 @@ declare namespace CodeceptJS {
|
|
|
2742
2742
|
* `grabStorageState({ indexedDB: true })`) IndexedDB data; treat as sensitive and do not commit.
|
|
2743
2743
|
*/
|
|
2744
2744
|
// @ts-ignore
|
|
2745
|
+
// @ts-ignore
|
|
2745
2746
|
type PlaywrightConfig = {
|
|
2746
2747
|
url?: string;
|
|
2747
2748
|
browser?: 'chromium' | 'firefox' | 'webkit' | 'electron';
|
|
@@ -6142,6 +6143,7 @@ declare namespace CodeceptJS {
|
|
|
6142
6143
|
* @property [highlightElement] - highlight the interacting elements. Default: false. Note: only activate under verbose mode (--verbose).
|
|
6143
6144
|
*/
|
|
6144
6145
|
// @ts-ignore
|
|
6146
|
+
// @ts-ignore
|
|
6145
6147
|
type PuppeteerConfig = {
|
|
6146
6148
|
url: string;
|
|
6147
6149
|
basicAuth?: any;
|
|
@@ -7987,6 +7989,7 @@ declare namespace CodeceptJS {
|
|
|
7987
7989
|
* @property [maxUploadFileSize] - set the max content file size in MB when performing api calls.
|
|
7988
7990
|
*/
|
|
7989
7991
|
// @ts-ignore
|
|
7992
|
+
// @ts-ignore
|
|
7990
7993
|
type RESTConfig = {
|
|
7991
7994
|
endpoint?: string;
|
|
7992
7995
|
prettyPrintJson?: boolean;
|
|
@@ -9143,6 +9146,7 @@ declare namespace CodeceptJS {
|
|
|
9143
9146
|
* @property [logLevel = silent] - level of logging verbosity. Default: silent. Options: trace | debug | info | warn | error | silent. More info: https://webdriver.io/docs/configuration/#loglevel
|
|
9144
9147
|
*/
|
|
9145
9148
|
// @ts-ignore
|
|
9149
|
+
// @ts-ignore
|
|
9146
9150
|
type WebDriverConfig = {
|
|
9147
9151
|
url: string;
|
|
9148
9152
|
browser: string;
|
package/typings/types.d.ts
CHANGED
|
@@ -2832,6 +2832,7 @@ declare namespace CodeceptJS {
|
|
|
2832
2832
|
* `grabStorageState({ indexedDB: true })`) IndexedDB data; treat as sensitive and do not commit.
|
|
2833
2833
|
*/
|
|
2834
2834
|
// @ts-ignore
|
|
2835
|
+
// @ts-ignore
|
|
2835
2836
|
type PlaywrightConfig = {
|
|
2836
2837
|
url?: string;
|
|
2837
2838
|
browser?: 'chromium' | 'firefox' | 'webkit' | 'electron';
|
|
@@ -6383,6 +6384,7 @@ declare namespace CodeceptJS {
|
|
|
6383
6384
|
* @property [highlightElement] - highlight the interacting elements. Default: false. Note: only activate under verbose mode (--verbose).
|
|
6384
6385
|
*/
|
|
6385
6386
|
// @ts-ignore
|
|
6387
|
+
// @ts-ignore
|
|
6386
6388
|
type PuppeteerConfig = {
|
|
6387
6389
|
url: string;
|
|
6388
6390
|
basicAuth?: any;
|
|
@@ -8364,6 +8366,7 @@ declare namespace CodeceptJS {
|
|
|
8364
8366
|
* @property [maxUploadFileSize] - set the max content file size in MB when performing api calls.
|
|
8365
8367
|
*/
|
|
8366
8368
|
// @ts-ignore
|
|
8369
|
+
// @ts-ignore
|
|
8367
8370
|
type RESTConfig = {
|
|
8368
8371
|
endpoint?: string;
|
|
8369
8372
|
prettyPrintJson?: boolean;
|
|
@@ -9580,6 +9583,7 @@ declare namespace CodeceptJS {
|
|
|
9580
9583
|
* @property [logLevel = silent] - level of logging verbosity. Default: silent. Options: trace | debug | info | warn | error | silent. More info: https://webdriver.io/docs/configuration/#loglevel
|
|
9581
9584
|
*/
|
|
9582
9585
|
// @ts-ignore
|
|
9586
|
+
// @ts-ignore
|
|
9583
9587
|
type WebDriverConfig = {
|
|
9584
9588
|
url: string;
|
|
9585
9589
|
browser: string;
|