codeceptjs 3.7.6-beta.4 → 4.0.0-beta.10.esm-aria
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/README.md +1 -3
- package/bin/codecept.js +51 -53
- package/bin/test-server.js +14 -3
- package/docs/webapi/click.mustache +5 -1
- package/lib/actor.js +15 -11
- package/lib/ai.js +72 -107
- package/lib/assert/empty.js +9 -8
- package/lib/assert/equal.js +15 -17
- package/lib/assert/error.js +2 -2
- package/lib/assert/include.js +9 -11
- package/lib/assert/throws.js +1 -1
- package/lib/assert/truth.js +8 -5
- package/lib/assert.js +18 -18
- package/lib/codecept.js +102 -75
- package/lib/colorUtils.js +48 -50
- package/lib/command/check.js +32 -27
- package/lib/command/configMigrate.js +11 -10
- package/lib/command/definitions.js +16 -10
- package/lib/command/dryRun.js +16 -16
- package/lib/command/generate.js +62 -27
- package/lib/command/gherkin/init.js +36 -38
- package/lib/command/gherkin/snippets.js +14 -14
- package/lib/command/gherkin/steps.js +21 -18
- package/lib/command/info.js +8 -8
- package/lib/command/init.js +36 -29
- package/lib/command/interactive.js +11 -10
- package/lib/command/list.js +10 -9
- package/lib/command/run-multiple/chunk.js +5 -5
- package/lib/command/run-multiple/collection.js +5 -5
- package/lib/command/run-multiple/run.js +3 -3
- package/lib/command/run-multiple.js +16 -13
- package/lib/command/run-rerun.js +6 -7
- package/lib/command/run-workers.js +24 -9
- package/lib/command/run.js +23 -8
- package/lib/command/utils.js +20 -18
- package/lib/command/workers/runTests.js +197 -114
- package/lib/config.js +124 -51
- package/lib/container.js +438 -87
- package/lib/data/context.js +6 -5
- package/lib/data/dataScenarioConfig.js +1 -1
- package/lib/data/dataTableArgument.js +1 -1
- package/lib/data/table.js +1 -1
- package/lib/effects.js +94 -10
- package/lib/element/WebElement.js +2 -2
- package/lib/els.js +11 -9
- package/lib/event.js +11 -10
- package/lib/globals.js +141 -0
- package/lib/heal.js +12 -12
- package/lib/helper/AI.js +11 -11
- package/lib/helper/ApiDataFactory.js +50 -19
- package/lib/helper/Appium.js +19 -27
- package/lib/helper/FileSystem.js +32 -12
- package/lib/helper/GraphQL.js +3 -3
- package/lib/helper/GraphQLDataFactory.js +4 -4
- package/lib/helper/JSONResponse.js +25 -29
- package/lib/helper/Mochawesome.js +7 -4
- package/lib/helper/Playwright.js +902 -164
- package/lib/helper/Puppeteer.js +383 -76
- package/lib/helper/REST.js +29 -12
- package/lib/helper/WebDriver.js +268 -61
- package/lib/helper/clientscripts/PollyWebDriverExt.js +1 -1
- package/lib/helper/errors/ConnectionRefused.js +6 -6
- package/lib/helper/errors/ElementAssertion.js +11 -16
- package/lib/helper/errors/ElementNotFound.js +5 -9
- package/lib/helper/errors/RemoteBrowserConnectionRefused.js +5 -5
- package/lib/helper/extras/Console.js +11 -11
- package/lib/helper/extras/PlaywrightLocator.js +110 -0
- package/lib/helper/extras/PlaywrightPropEngine.js +18 -18
- package/lib/helper/extras/PlaywrightReactVueLocator.js +18 -9
- package/lib/helper/extras/PlaywrightRestartOpts.js +34 -23
- package/lib/helper/extras/Popup.js +1 -1
- package/lib/helper/extras/React.js +29 -30
- package/lib/helper/network/actions.js +29 -44
- package/lib/helper/network/utils.js +76 -83
- package/lib/helper/scripts/blurElement.js +6 -6
- package/lib/helper/scripts/focusElement.js +6 -6
- package/lib/helper/scripts/highlightElement.js +9 -9
- package/lib/helper/scripts/isElementClickable.js +34 -34
- package/lib/helper.js +2 -1
- package/lib/history.js +23 -20
- package/lib/hooks.js +10 -10
- package/lib/html.js +90 -100
- package/lib/index.js +48 -21
- package/lib/listener/config.js +19 -12
- package/lib/listener/emptyRun.js +6 -7
- package/lib/listener/enhancedGlobalRetry.js +6 -6
- package/lib/listener/exit.js +4 -3
- package/lib/listener/globalRetry.js +5 -5
- package/lib/listener/globalTimeout.js +30 -14
- package/lib/listener/helpers.js +39 -14
- package/lib/listener/mocha.js +3 -4
- package/lib/listener/result.js +4 -5
- package/lib/listener/retryEnhancer.js +3 -3
- package/lib/listener/steps.js +8 -7
- package/lib/listener/store.js +3 -3
- package/lib/locator.js +213 -192
- package/lib/mocha/asyncWrapper.js +105 -62
- package/lib/mocha/bdd.js +99 -13
- package/lib/mocha/cli.js +59 -26
- package/lib/mocha/factory.js +78 -19
- package/lib/mocha/featureConfig.js +1 -1
- package/lib/mocha/gherkin.js +56 -24
- package/lib/mocha/hooks.js +12 -3
- package/lib/mocha/index.js +13 -4
- package/lib/mocha/inject.js +22 -5
- package/lib/mocha/scenarioConfig.js +2 -2
- package/lib/mocha/suite.js +9 -2
- package/lib/mocha/test.js +10 -7
- package/lib/mocha/ui.js +28 -18
- package/lib/output.js +10 -8
- package/lib/parser.js +44 -44
- package/lib/pause.js +15 -16
- package/lib/plugin/analyze.js +19 -12
- package/lib/plugin/auth.js +20 -21
- package/lib/plugin/autoDelay.js +12 -8
- package/lib/plugin/coverage.js +28 -11
- package/lib/plugin/customLocator.js +3 -3
- package/lib/plugin/customReporter.js +3 -2
- package/lib/plugin/enhancedRetryFailedStep.js +6 -6
- package/lib/plugin/heal.js +14 -9
- package/lib/plugin/htmlReporter.js +724 -99
- package/lib/plugin/pageInfo.js +10 -10
- package/lib/plugin/pauseOnFail.js +4 -3
- package/lib/plugin/retryFailedStep.js +48 -5
- package/lib/plugin/screenshotOnFail.js +75 -37
- package/lib/plugin/stepByStepReport.js +14 -14
- package/lib/plugin/stepTimeout.js +4 -3
- package/lib/plugin/subtitles.js +6 -5
- package/lib/recorder.js +33 -14
- package/lib/rerun.js +69 -26
- package/lib/result.js +4 -4
- package/lib/retryCoordinator.js +2 -2
- package/lib/secret.js +18 -17
- package/lib/session.js +95 -89
- package/lib/step/base.js +7 -7
- package/lib/step/comment.js +2 -2
- package/lib/step/config.js +1 -1
- package/lib/step/func.js +3 -3
- package/lib/step/helper.js +3 -3
- package/lib/step/meta.js +5 -5
- package/lib/step/record.js +11 -11
- package/lib/step/retry.js +3 -3
- package/lib/step/section.js +3 -3
- package/lib/step.js +7 -10
- package/lib/steps.js +9 -5
- package/lib/store.js +1 -1
- package/lib/template/heal.js +1 -1
- package/lib/template/prompts/generatePageObject.js +31 -0
- package/lib/template/prompts/healStep.js +13 -0
- package/lib/template/prompts/writeStep.js +9 -0
- package/lib/test-server.js +17 -6
- package/lib/timeout.js +1 -7
- package/lib/transform.js +8 -8
- package/lib/translation.js +32 -18
- package/lib/utils/mask_data.js +4 -10
- package/lib/utils.js +66 -64
- package/lib/workerStorage.js +17 -17
- package/lib/workers.js +214 -84
- package/package.json +41 -37
- package/translations/de-DE.js +2 -2
- package/translations/fr-FR.js +2 -2
- package/translations/index.js +23 -10
- package/translations/it-IT.js +2 -2
- package/translations/ja-JP.js +2 -2
- package/translations/nl-NL.js +2 -2
- package/translations/pl-PL.js +2 -2
- package/translations/pt-BR.js +2 -2
- package/translations/ru-RU.js +2 -2
- package/translations/utils.js +4 -3
- package/translations/zh-CN.js +2 -2
- package/translations/zh-TW.js +2 -2
- package/typings/index.d.ts +5 -3
- package/typings/promiseBasedTypes.d.ts +4 -0
- package/typings/types.d.ts +4 -0
- package/lib/helper/Nightmare.js +0 -1486
- package/lib/helper/Protractor.js +0 -1840
- package/lib/helper/TestCafe.js +0 -1391
- package/lib/helper/clientscripts/nightmare.js +0 -213
- package/lib/helper/testcafe/testControllerHolder.js +0 -42
- package/lib/helper/testcafe/testcafe-utils.js +0 -61
- package/lib/plugin/allure.js +0 -15
- package/lib/plugin/autoLogin.js +0 -5
- package/lib/plugin/commentStep.js +0 -141
- package/lib/plugin/eachElement.js +0 -127
- package/lib/plugin/fakerTransform.js +0 -49
- package/lib/plugin/retryTo.js +0 -16
- package/lib/plugin/selenoid.js +0 -364
- package/lib/plugin/standardActingHelpers.js +0 -6
- package/lib/plugin/tryTo.js +0 -16
- package/lib/plugin/wdio.js +0 -247
- package/lib/within.js +0 -90
package/lib/workers.js
CHANGED
|
@@ -1,36 +1,41 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
const
|
|
10
|
-
const
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
1
|
+
import path from 'path'
|
|
2
|
+
import { fileURLToPath } from 'url'
|
|
3
|
+
import { dirname } from 'path'
|
|
4
|
+
import { mkdirp } from 'mkdirp'
|
|
5
|
+
import { Worker } from 'worker_threads'
|
|
6
|
+
import { EventEmitter } from 'events'
|
|
7
|
+
import ms from 'ms'
|
|
8
|
+
|
|
9
|
+
const __filename = fileURLToPath(import.meta.url)
|
|
10
|
+
const __dirname = dirname(__filename)
|
|
11
|
+
import Codecept from './codecept.js'
|
|
12
|
+
import MochaFactory from './mocha/factory.js'
|
|
13
|
+
import Container from './container.js'
|
|
14
|
+
import { getTestRoot } from './command/utils.js'
|
|
15
|
+
import { isFunction, fileExists, replaceValueDeep, deepClone } from './utils.js'
|
|
16
|
+
import mainConfig from './config.js'
|
|
17
|
+
import output from './output.js'
|
|
18
|
+
import event from './event.js'
|
|
19
|
+
import { deserializeTest } from './mocha/test.js'
|
|
20
|
+
import { deserializeSuite } from './mocha/suite.js'
|
|
21
|
+
import recorder from './recorder.js'
|
|
22
|
+
import runHook from './hooks.js'
|
|
23
|
+
import WorkerStorage from './workerStorage.js'
|
|
24
|
+
import { createRuns } from './command/run-multiple/collection.js'
|
|
21
25
|
|
|
22
26
|
const pathToWorker = path.join(__dirname, 'command', 'workers', 'runTests.js')
|
|
23
27
|
|
|
24
|
-
const initializeCodecept = (configPath, options = {}) => {
|
|
25
|
-
const
|
|
26
|
-
codecept
|
|
28
|
+
const initializeCodecept = async (configPath, options = {}) => {
|
|
29
|
+
const config = await mainConfig.load(configPath || '.')
|
|
30
|
+
const codecept = new Codecept(config, options)
|
|
31
|
+
await codecept.init(getTestRoot(configPath))
|
|
27
32
|
codecept.loadTests()
|
|
28
33
|
|
|
29
34
|
return codecept
|
|
30
35
|
}
|
|
31
36
|
|
|
32
|
-
const createOutputDir = configPath => {
|
|
33
|
-
const config = mainConfig.load(configPath || '.')
|
|
37
|
+
const createOutputDir = async configPath => {
|
|
38
|
+
const config = await mainConfig.load(configPath || '.')
|
|
34
39
|
const testRoot = getTestRoot(configPath)
|
|
35
40
|
const outputDir = path.isAbsolute(config.output) ? config.output : path.join(testRoot, config.output)
|
|
36
41
|
|
|
@@ -58,8 +63,28 @@ const createWorker = (workerObject, isPoolMode = false) => {
|
|
|
58
63
|
workerIndex: workerObject.workerIndex + 1,
|
|
59
64
|
poolMode: isPoolMode,
|
|
60
65
|
},
|
|
66
|
+
stdout: true,
|
|
67
|
+
stderr: true,
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
// Pipe worker stdout/stderr to main process
|
|
71
|
+
if (worker.stdout) {
|
|
72
|
+
worker.stdout.setEncoding('utf8')
|
|
73
|
+
worker.stdout.on('data', (data) => {
|
|
74
|
+
process.stdout.write(data)
|
|
75
|
+
})
|
|
76
|
+
}
|
|
77
|
+
if (worker.stderr) {
|
|
78
|
+
worker.stderr.setEncoding('utf8')
|
|
79
|
+
worker.stderr.on('data', (data) => {
|
|
80
|
+
process.stderr.write(data)
|
|
81
|
+
})
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
worker.on('error', err => {
|
|
85
|
+
console.error(`[Main] Worker Error:`, err)
|
|
86
|
+
output.error(`Worker Error: ${err.stack}`)
|
|
61
87
|
})
|
|
62
|
-
worker.on('error', err => output.error(`Worker Error: ${err.stack}`))
|
|
63
88
|
|
|
64
89
|
WorkerStorage.addWorker(worker)
|
|
65
90
|
return worker
|
|
@@ -99,7 +124,7 @@ const createWorkerObjects = (testGroups, config, testRoot, options, selectedRuns
|
|
|
99
124
|
currentMochaJunitReporterFile = config.mocha.reporterOptions['mocha-junit-reporter'].options.mochaFile
|
|
100
125
|
}
|
|
101
126
|
|
|
102
|
-
|
|
127
|
+
createRuns(selectedRuns, config).forEach(worker => {
|
|
103
128
|
const separator = path.sep
|
|
104
129
|
const _config = { ...config }
|
|
105
130
|
let workerName = worker.name.replace(':', '_')
|
|
@@ -196,9 +221,31 @@ class WorkerObject {
|
|
|
196
221
|
|
|
197
222
|
addConfig(config) {
|
|
198
223
|
const oldConfig = JSON.parse(this.options.override || '{}')
|
|
224
|
+
|
|
225
|
+
// Remove customLocatorStrategies from both old and new config before JSON serialization
|
|
226
|
+
// since functions cannot be serialized and will be lost, causing workers to have empty strategies
|
|
227
|
+
const configWithoutFunctions = { ...config }
|
|
228
|
+
|
|
229
|
+
// Clean both old and new config
|
|
230
|
+
const cleanConfig = (cfg) => {
|
|
231
|
+
if (cfg.helpers) {
|
|
232
|
+
cfg.helpers = { ...cfg.helpers }
|
|
233
|
+
Object.keys(cfg.helpers).forEach(helperName => {
|
|
234
|
+
if (cfg.helpers[helperName] && cfg.helpers[helperName].customLocatorStrategies !== undefined) {
|
|
235
|
+
cfg.helpers[helperName] = { ...cfg.helpers[helperName] }
|
|
236
|
+
delete cfg.helpers[helperName].customLocatorStrategies
|
|
237
|
+
}
|
|
238
|
+
})
|
|
239
|
+
}
|
|
240
|
+
return cfg
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
const cleanedOldConfig = cleanConfig(oldConfig)
|
|
244
|
+
const cleanedNewConfig = cleanConfig(configWithoutFunctions)
|
|
245
|
+
|
|
199
246
|
const newConfig = {
|
|
200
|
-
...
|
|
201
|
-
...
|
|
247
|
+
...cleanedOldConfig,
|
|
248
|
+
...cleanedNewConfig,
|
|
202
249
|
}
|
|
203
250
|
this.options.override = JSON.stringify(newConfig)
|
|
204
251
|
}
|
|
@@ -231,7 +278,10 @@ class Workers extends EventEmitter {
|
|
|
231
278
|
constructor(numberOfWorkers, config = { by: 'test' }) {
|
|
232
279
|
super()
|
|
233
280
|
this.setMaxListeners(50)
|
|
234
|
-
this.
|
|
281
|
+
this.codeceptPromise = initializeCodecept(config.testConfig, config.options)
|
|
282
|
+
this.codecept = null
|
|
283
|
+
this.config = config // Save config
|
|
284
|
+
this.numberOfWorkersRequested = numberOfWorkers // Save requested worker count
|
|
235
285
|
this.options = config.options || {}
|
|
236
286
|
this.errors = []
|
|
237
287
|
this.numberOfWorkers = 0
|
|
@@ -245,11 +295,30 @@ class Workers extends EventEmitter {
|
|
|
245
295
|
this.maxWorkers = numberOfWorkers // Track original worker count for pool mode
|
|
246
296
|
|
|
247
297
|
createOutputDir(config.testConfig)
|
|
248
|
-
|
|
298
|
+
// Defer worker initialization until codecept is ready
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
async _ensureInitialized() {
|
|
302
|
+
if (!this.codecept) {
|
|
303
|
+
this.codecept = await this.codeceptPromise
|
|
304
|
+
// Initialize workers in these cases:
|
|
305
|
+
// 1. Positive number requested AND no manual workers pre-spawned
|
|
306
|
+
// 2. Function-based grouping (indicated by negative number) AND no manual workers pre-spawned
|
|
307
|
+
const shouldAutoInit = this.workers.length === 0 && (
|
|
308
|
+
(Number.isInteger(this.numberOfWorkersRequested) && this.numberOfWorkersRequested > 0) ||
|
|
309
|
+
(this.numberOfWorkersRequested < 0 && isFunction(this.config.by))
|
|
310
|
+
)
|
|
311
|
+
|
|
312
|
+
if (shouldAutoInit) {
|
|
313
|
+
this._initWorkers(this.numberOfWorkersRequested, this.config)
|
|
314
|
+
}
|
|
315
|
+
}
|
|
249
316
|
}
|
|
250
317
|
|
|
251
318
|
_initWorkers(numberOfWorkers, config) {
|
|
252
319
|
this.splitTestsByGroups(numberOfWorkers, config)
|
|
320
|
+
// For function-based grouping, use the actual number of test groups created
|
|
321
|
+
const actualNumberOfWorkers = isFunction(config.by) ? this.testGroups.length : numberOfWorkers
|
|
253
322
|
this.workers = createWorkerObjects(this.testGroups, this.codecept.config, config.testConfig, config.options, config.selectedRuns)
|
|
254
323
|
this.numberOfWorkers = this.workers.length
|
|
255
324
|
}
|
|
@@ -302,7 +371,9 @@ class Workers extends EventEmitter {
|
|
|
302
371
|
* @param {Number} numberOfWorkers
|
|
303
372
|
*/
|
|
304
373
|
createGroupsOfTests(numberOfWorkers) {
|
|
305
|
-
|
|
374
|
+
// If Codecept isn't initialized yet, return empty groups as a safe fallback
|
|
375
|
+
if (!this.codecept) return populateGroups(numberOfWorkers)
|
|
376
|
+
const files = this.codecept.testFiles
|
|
306
377
|
const mocha = Container.mocha()
|
|
307
378
|
mocha.files = files
|
|
308
379
|
mocha.loadFiles()
|
|
@@ -340,70 +411,49 @@ class Workers extends EventEmitter {
|
|
|
340
411
|
return
|
|
341
412
|
}
|
|
342
413
|
|
|
343
|
-
|
|
344
|
-
if (!
|
|
414
|
+
// Ensure codecept is initialized
|
|
415
|
+
if (!this.codecept) {
|
|
416
|
+
output.log('Warning: codecept not initialized when initializing test pool')
|
|
345
417
|
this.testPoolInitialized = true
|
|
346
418
|
return
|
|
347
419
|
}
|
|
348
420
|
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
mocha.suite.eachTest(test => {
|
|
355
|
-
if (test) {
|
|
356
|
-
this.testPool.push(test.uid)
|
|
357
|
-
}
|
|
358
|
-
})
|
|
359
|
-
} catch (e) {
|
|
360
|
-
// If mocha loading fails due to state pollution, skip
|
|
361
|
-
}
|
|
362
|
-
|
|
363
|
-
// If no tests were found, fallback to using createGroupsOfTests approach
|
|
364
|
-
// This works around state pollution issues
|
|
365
|
-
if (this.testPool.length === 0 && files.length > 0) {
|
|
366
|
-
try {
|
|
367
|
-
const testGroups = this.createGroupsOfTests(2) // Use 2 as a default for fallback
|
|
368
|
-
for (const group of testGroups) {
|
|
369
|
-
this.testPool.push(...group)
|
|
370
|
-
}
|
|
371
|
-
} catch (e) {
|
|
372
|
-
// If createGroupsOfTests fails, fallback to simple file names
|
|
373
|
-
for (const file of files) {
|
|
374
|
-
this.testPool.push(`test_${file.replace(/[^a-zA-Z0-9]/g, '_')}`)
|
|
375
|
-
}
|
|
376
|
-
}
|
|
421
|
+
const files = this.codecept.testFiles
|
|
422
|
+
if (!files || files.length === 0) {
|
|
423
|
+
this.testPoolInitialized = true
|
|
424
|
+
return
|
|
377
425
|
}
|
|
378
426
|
|
|
379
|
-
//
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
427
|
+
// In ESM, test UIDs are not stable across different mocha instances
|
|
428
|
+
// So instead of using UIDs, we distribute test FILES
|
|
429
|
+
// Each file may contain multiple tests
|
|
430
|
+
for (const file of files) {
|
|
431
|
+
this.testPool.push(file)
|
|
384
432
|
}
|
|
385
|
-
|
|
433
|
+
|
|
386
434
|
this.testPoolInitialized = true
|
|
387
435
|
}
|
|
388
436
|
|
|
389
437
|
/**
|
|
390
438
|
* Gets the next test from the pool
|
|
391
|
-
* @returns {String|null} test
|
|
439
|
+
* @returns {String|null} test file path or null if no tests available
|
|
392
440
|
*/
|
|
393
441
|
getNextTest() {
|
|
394
|
-
//
|
|
442
|
+
// Lazy initialization of test pool on first call
|
|
395
443
|
if (!this.testPoolInitialized) {
|
|
396
444
|
this._initializeTestPool()
|
|
397
445
|
}
|
|
398
|
-
|
|
399
|
-
return this.testPool.shift()
|
|
446
|
+
|
|
447
|
+
return this.testPool.shift()
|
|
400
448
|
}
|
|
401
449
|
|
|
402
450
|
/**
|
|
403
451
|
* @param {Number} numberOfWorkers
|
|
404
452
|
*/
|
|
405
453
|
createGroupsOfSuites(numberOfWorkers) {
|
|
406
|
-
|
|
454
|
+
// If Codecept isn't initialized yet, return empty groups as a safe fallback
|
|
455
|
+
if (!this.codecept) return populateGroups(numberOfWorkers)
|
|
456
|
+
const files = this.codecept.testFiles
|
|
407
457
|
const groups = populateGroups(numberOfWorkers)
|
|
408
458
|
|
|
409
459
|
const mocha = Container.mocha()
|
|
@@ -430,23 +480,34 @@ class Workers extends EventEmitter {
|
|
|
430
480
|
}
|
|
431
481
|
|
|
432
482
|
async bootstrapAll() {
|
|
483
|
+
await this._ensureInitialized()
|
|
433
484
|
return runHook(this.codecept.config.bootstrapAll, 'bootstrapAll')
|
|
434
485
|
}
|
|
435
486
|
|
|
436
487
|
async teardownAll() {
|
|
488
|
+
await this._ensureInitialized()
|
|
437
489
|
return runHook(this.codecept.config.teardownAll, 'teardownAll')
|
|
438
490
|
}
|
|
439
491
|
|
|
440
|
-
run() {
|
|
492
|
+
async run() {
|
|
493
|
+
await this._ensureInitialized()
|
|
441
494
|
recorder.startUnlessRunning()
|
|
442
495
|
event.dispatcher.emit(event.workers.before)
|
|
443
496
|
process.env.RUNS_WITH_WORKERS = 'true'
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
497
|
+
|
|
498
|
+
// Create workers and set up message handlers immediately (not in recorder queue)
|
|
499
|
+
// This prevents a race condition where workers start sending messages before handlers are attached
|
|
500
|
+
const workerThreads = []
|
|
501
|
+
for (const worker of this.workers) {
|
|
502
|
+
const workerThread = createWorker(worker, this.isPoolMode)
|
|
503
|
+
this._listenWorkerEvents(workerThread)
|
|
504
|
+
workerThreads.push(workerThread)
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
recorder.add('workers started', () => {
|
|
508
|
+
// Workers are already running, this is just a placeholder step
|
|
449
509
|
})
|
|
510
|
+
|
|
450
511
|
return new Promise(resolve => {
|
|
451
512
|
this.on('end', resolve)
|
|
452
513
|
})
|
|
@@ -522,16 +583,48 @@ class Workers extends EventEmitter {
|
|
|
522
583
|
this.emit(event.test.started, deserializeTest(message.data))
|
|
523
584
|
break
|
|
524
585
|
case event.test.failed:
|
|
525
|
-
|
|
586
|
+
// For hook failures, emit immediately as there won't be a test.finished event
|
|
587
|
+
// Regular test failures are handled via test.finished to support retries
|
|
588
|
+
if (message.data?.hookName) {
|
|
589
|
+
this.emit(event.test.failed, deserializeTest(message.data))
|
|
590
|
+
}
|
|
591
|
+
// Otherwise skip - we'll emit based on finished state
|
|
526
592
|
break
|
|
527
593
|
case event.test.passed:
|
|
528
|
-
|
|
594
|
+
// Skip individual passed events - we'll emit based on finished state
|
|
529
595
|
break
|
|
530
596
|
case event.test.skipped:
|
|
531
597
|
this.emit(event.test.skipped, deserializeTest(message.data))
|
|
532
598
|
break
|
|
533
599
|
case event.test.finished:
|
|
534
|
-
|
|
600
|
+
// Handle different types of test completion properly
|
|
601
|
+
{
|
|
602
|
+
const data = message.data
|
|
603
|
+
const uid = data?.uid
|
|
604
|
+
const isFailed = !!data?.err || data?.state === 'failed'
|
|
605
|
+
|
|
606
|
+
if (uid) {
|
|
607
|
+
// Track states for each test UID
|
|
608
|
+
if (!this._testStates) this._testStates = new Map()
|
|
609
|
+
|
|
610
|
+
if (!this._testStates.has(uid)) {
|
|
611
|
+
this._testStates.set(uid, { states: [], lastData: data })
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
const testState = this._testStates.get(uid)
|
|
615
|
+
testState.states.push({ isFailed, data })
|
|
616
|
+
testState.lastData = data
|
|
617
|
+
} else {
|
|
618
|
+
// For tests without UID, emit immediately
|
|
619
|
+
if (isFailed) {
|
|
620
|
+
this.emit(event.test.failed, deserializeTest(data))
|
|
621
|
+
} else {
|
|
622
|
+
this.emit(event.test.passed, deserializeTest(data))
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
this.emit(event.test.finished, deserializeTest(data))
|
|
627
|
+
}
|
|
535
628
|
break
|
|
536
629
|
case event.test.after:
|
|
537
630
|
this.emit(event.test.after, deserializeTest(message.data))
|
|
@@ -548,6 +641,11 @@ class Workers extends EventEmitter {
|
|
|
548
641
|
case event.step.failed:
|
|
549
642
|
this.emit(event.step.failed, message.data, message.data.error)
|
|
550
643
|
break
|
|
644
|
+
case event.hook.failed:
|
|
645
|
+
// Hook failures are already reported as test failures by the worker
|
|
646
|
+
// Just emit the hook.failed event for listeners
|
|
647
|
+
this.emit(event.hook.failed, message.data)
|
|
648
|
+
break
|
|
551
649
|
}
|
|
552
650
|
})
|
|
553
651
|
|
|
@@ -578,6 +676,38 @@ class Workers extends EventEmitter {
|
|
|
578
676
|
process.exitCode = 0
|
|
579
677
|
}
|
|
580
678
|
|
|
679
|
+
// Emit states for all tracked tests before emitting results
|
|
680
|
+
if (this._testStates) {
|
|
681
|
+
for (const [uid, { states, lastData }] of this._testStates) {
|
|
682
|
+
// For tests with retries configured, emit all failures + final success
|
|
683
|
+
// For tests without retries, emit only final state
|
|
684
|
+
const lastState = states[states.length - 1]
|
|
685
|
+
|
|
686
|
+
// Check if this test had retries by looking for failure followed by success
|
|
687
|
+
const hasRetryPattern = states.length > 1 &&
|
|
688
|
+
states.some((s, i) => s.isFailed && i < states.length - 1 && !states[i + 1].isFailed)
|
|
689
|
+
|
|
690
|
+
if (hasRetryPattern) {
|
|
691
|
+
// Emit all intermediate failures and final success for retries
|
|
692
|
+
for (const state of states) {
|
|
693
|
+
if (state.isFailed) {
|
|
694
|
+
this.emit(event.test.failed, deserializeTest(state.data))
|
|
695
|
+
} else {
|
|
696
|
+
this.emit(event.test.passed, deserializeTest(state.data))
|
|
697
|
+
}
|
|
698
|
+
}
|
|
699
|
+
} else {
|
|
700
|
+
// For non-retries (like step failures), emit only the final state
|
|
701
|
+
if (lastState.isFailed) {
|
|
702
|
+
this.emit(event.test.failed, deserializeTest(lastState.data))
|
|
703
|
+
} else {
|
|
704
|
+
this.emit(event.test.passed, deserializeTest(lastState.data))
|
|
705
|
+
}
|
|
706
|
+
}
|
|
707
|
+
}
|
|
708
|
+
this._testStates.clear()
|
|
709
|
+
}
|
|
710
|
+
|
|
581
711
|
this.emit(event.all.result, Container.result())
|
|
582
712
|
event.dispatcher.emit(event.workers.result, Container.result())
|
|
583
713
|
this.emit('end') // internal event
|
|
@@ -602,10 +732,10 @@ class Workers extends EventEmitter {
|
|
|
602
732
|
this.failuresLog.forEach(log => output.print(...log))
|
|
603
733
|
}
|
|
604
734
|
|
|
605
|
-
output.result(result.stats
|
|
735
|
+
output.result(result.stats?.passes || 0, result.stats?.failures || 0, result.stats?.pending || 0, ms(result.duration), result.stats?.failedHooks || 0)
|
|
606
736
|
|
|
607
737
|
process.env.RUNS_WITH_WORKERS = 'false'
|
|
608
738
|
}
|
|
609
739
|
}
|
|
610
740
|
|
|
611
|
-
|
|
741
|
+
export default Workers
|
package/package.json
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "codeceptjs",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "4.0.0-beta.10.esm-aria",
|
|
4
|
+
"type": "module",
|
|
4
5
|
"description": "Supercharged End 2 End Testing Framework for NodeJS",
|
|
5
6
|
"keywords": [
|
|
6
7
|
"acceptance",
|
|
@@ -8,7 +9,6 @@
|
|
|
8
9
|
"end 2 end",
|
|
9
10
|
"puppeteer",
|
|
10
11
|
"webdriver",
|
|
11
|
-
"testcafe",
|
|
12
12
|
"playwright",
|
|
13
13
|
"bdd",
|
|
14
14
|
"tdd",
|
|
@@ -40,47 +40,49 @@
|
|
|
40
40
|
"./lib/*": "./lib/*.js",
|
|
41
41
|
"./els": "./lib/els.js",
|
|
42
42
|
"./effects": "./lib/effects.js",
|
|
43
|
-
"./steps": "./lib/steps.js"
|
|
43
|
+
"./steps": "./lib/steps.js",
|
|
44
|
+
"./store": "./lib/store.js"
|
|
44
45
|
},
|
|
45
46
|
"bin": {
|
|
46
47
|
"codeceptjs": "./bin/codecept.js"
|
|
47
48
|
},
|
|
48
49
|
"repository": "Codeception/codeceptjs",
|
|
49
50
|
"scripts": {
|
|
50
|
-
"test-server": "node bin/test-server.js test/data/rest/db.json --host 0.0.0.0 -p 8010",
|
|
51
|
+
"test-server": "node bin/test-server.js test/data/rest/db.json --host 0.0.0.0 -p 8010 --read-only",
|
|
52
|
+
"test-server:writable": "node bin/test-server.js test/data/rest/db.json --host 0.0.0.0 -p 8010",
|
|
51
53
|
"mock-server:start": "node test/mock-server/start-mock-server.js",
|
|
52
54
|
"mock-server:stop": "kill -9 $(lsof -t -i:3001)",
|
|
53
55
|
"test:with-mock-server": "npm run mock-server:start && npm test",
|
|
54
56
|
"json-server:graphql": "node test/data/graphql/index.js",
|
|
55
|
-
"lint": "eslint bin/ examples/ lib/ test/ translations/ runok.
|
|
56
|
-
"lint-fix": "eslint bin/ examples/ lib/ test/ translations/ runok.
|
|
57
|
-
"prettier": "prettier --config prettier.config.js --write bin/**/*.js lib/**/*.js test/**/*.js translations/**/*.js runok.
|
|
58
|
-
"docs": "./runok.
|
|
59
|
-
"test:unit": "mocha test/unit --recursive --timeout 10000",
|
|
60
|
-
"test:
|
|
61
|
-
"test": "
|
|
62
|
-
"test
|
|
63
|
-
"test:appium-
|
|
64
|
-
"test:
|
|
65
|
-
"test:ios:appium-
|
|
57
|
+
"lint": "eslint bin/ examples/ lib/ test/ translations/ runok.cjs",
|
|
58
|
+
"lint-fix": "eslint bin/ examples/ lib/ test/ translations/ runok.cjs --fix",
|
|
59
|
+
"prettier": "prettier --config prettier.config.js --write bin/**/*.js lib/**/*.js test/**/*.js translations/**/*.js runok.cjs",
|
|
60
|
+
"docs": "./runok.cjs docs",
|
|
61
|
+
"test:unit": "mocha test/unit --recursive --timeout 10000 --reporter @testomatio/reporter/mocha",
|
|
62
|
+
"test:rest": "mocha test/rest --recursive --timeout 20000 --reporter @testomatio/reporter/mocha",
|
|
63
|
+
"test:runner": "mocha test/runner --recursive --timeout 10000 --reporter @testomatio/reporter/mocha",
|
|
64
|
+
"test": "npm run test:unit && npm run test:rest && npm run test:runner",
|
|
65
|
+
"test:appium-quick": "mocha test/helper/Appium_test.js --grep 'quick' --reporter @testomatio/reporter/mocha",
|
|
66
|
+
"test:appium-other": "mocha test/helper/Appium_test.js --grep 'second' --reporter @testomatio/reporter/mocha",
|
|
67
|
+
"test:ios:appium-quick": "mocha test/helper/Appium_ios_test.js --grep 'quick' --reporter @testomatio/reporter/mocha",
|
|
68
|
+
"test:ios:appium-other": "mocha test/helper/Appium_ios_test.js --grep 'second' --reporter @testomatio/reporter/mocha",
|
|
66
69
|
"test-app:start": "php -S 127.0.0.1:8000 -t test/data/app",
|
|
67
70
|
"test-app:stop": "kill -9 $(lsof -t -i:8000)",
|
|
68
|
-
"test:unit:webbapi:playwright": "mocha test/helper/Playwright_test.js",
|
|
69
|
-
"test:unit:webbapi:puppeteer": "mocha test/helper/Puppeteer_test.js",
|
|
70
|
-
"test:unit:webbapi:webDriver": "mocha test/helper/WebDriver_test.js --timeout 10000",
|
|
71
|
-
"test:unit:webbapi:webDriver:noSeleniumServer": "mocha test/helper/WebDriver.noSeleniumServer_test.js --timeout 10000",
|
|
72
|
-
"test:unit:
|
|
73
|
-
"test:
|
|
74
|
-
"
|
|
75
|
-
"def": "./runok.js def",
|
|
71
|
+
"test:unit:webbapi:playwright": "mocha test/helper/Playwright_test.js --reporter @testomatio/reporter/mocha",
|
|
72
|
+
"test:unit:webbapi:puppeteer": "mocha test/helper/Puppeteer_test.js --reporter @testomatio/reporter/mocha",
|
|
73
|
+
"test:unit:webbapi:webDriver": "mocha test/helper/WebDriver_test.js --timeout 10000 --reporter @testomatio/reporter/mocha",
|
|
74
|
+
"test:unit:webbapi:webDriver:noSeleniumServer": "mocha test/helper/WebDriver.noSeleniumServer_test.js --timeout 10000 --reporter @testomatio/reporter/mocha",
|
|
75
|
+
"test:unit:expect": "mocha test/helper/Expect_test.js --reporter @testomatio/reporter/mocha",
|
|
76
|
+
"test:plugin": "mocha test/plugin/plugin_test.js --reporter @testomatio/reporter/mocha",
|
|
77
|
+
"def": "./runok.cjs def",
|
|
76
78
|
"dev:graphql": "node test/data/graphql/index.js",
|
|
77
|
-
"publish:site": "./runok.
|
|
78
|
-
"update-contributor-faces": "./runok.
|
|
79
|
+
"publish:site": "./runok.cjs publish:site",
|
|
80
|
+
"update-contributor-faces": "./runok.cjs contributor:faces",
|
|
79
81
|
"types-fix": "node typings/fixDefFiles.js",
|
|
80
82
|
"dtslint": "npm run types-fix && tsd",
|
|
81
83
|
"prepare": "husky install",
|
|
82
|
-
"prepare-release": "./runok.
|
|
83
|
-
"publish-beta": "./runok.
|
|
84
|
+
"prepare-release": "./runok.cjs versioning && ./runok.cjs get:commit-log",
|
|
85
|
+
"publish-beta": "./runok.cjs publish:next-beta-version"
|
|
84
86
|
},
|
|
85
87
|
"dependencies": {
|
|
86
88
|
"@codeceptjs/configure": "1.0.6",
|
|
@@ -90,6 +92,7 @@
|
|
|
90
92
|
"@cucumber/messages": "29.0.1",
|
|
91
93
|
"@xmldom/xmldom": "0.9.8",
|
|
92
94
|
"acorn": "8.15.0",
|
|
95
|
+
"ai": "^5.0.60",
|
|
93
96
|
"arrify": "3.0.0",
|
|
94
97
|
"axios": "1.12.2",
|
|
95
98
|
"chalk": "4.1.2",
|
|
@@ -99,7 +102,7 @@
|
|
|
99
102
|
"cross-spawn": "7.0.6",
|
|
100
103
|
"css-to-xpath": "0.1.0",
|
|
101
104
|
"csstoxpath": "1.6.0",
|
|
102
|
-
"envinfo": "7.
|
|
105
|
+
"envinfo": "7.20.0",
|
|
103
106
|
"escape-string-regexp": "4.0.0",
|
|
104
107
|
"figures": "3.2.0",
|
|
105
108
|
"fn-args": "4.0.0",
|
|
@@ -115,7 +118,7 @@
|
|
|
115
118
|
"lodash.merge": "4.6.2",
|
|
116
119
|
"lodash.shuffle": "4.2.0",
|
|
117
120
|
"mkdirp": "3.0.1",
|
|
118
|
-
"mocha": "11.7.
|
|
121
|
+
"mocha": "11.7.5",
|
|
119
122
|
"monocart-coverage-reports": "2.12.9",
|
|
120
123
|
"ms": "2.1.3",
|
|
121
124
|
"multer": "^2.0.2",
|
|
@@ -137,16 +140,19 @@
|
|
|
137
140
|
"@eslint/eslintrc": "3.3.1",
|
|
138
141
|
"@eslint/js": "9.36.0",
|
|
139
142
|
"@faker-js/faker": "9.8.0",
|
|
143
|
+
"@inquirer/testing": "^2.1.49",
|
|
140
144
|
"@pollyjs/adapter-puppeteer": "6.0.6",
|
|
141
145
|
"@pollyjs/core": "6.0.6",
|
|
146
|
+
"@testomatio/reporter": "^2.3.1",
|
|
142
147
|
"@types/chai": "5.2.2",
|
|
143
148
|
"@types/inquirer": "9.0.9",
|
|
144
|
-
"@types/node": "^24.
|
|
149
|
+
"@types/node": "^24.9.2",
|
|
145
150
|
"@wdio/sauce-service": "9.12.5",
|
|
146
151
|
"@wdio/selenium-standalone-service": "8.15.0",
|
|
147
|
-
"@wdio/utils": "9.
|
|
152
|
+
"@wdio/utils": "9.20.0",
|
|
148
153
|
"@xmldom/xmldom": "0.9.8",
|
|
149
|
-
"
|
|
154
|
+
"bunosh": "latest",
|
|
155
|
+
"chai": "^4.5.0",
|
|
150
156
|
"chai-as-promised": "7.1.2",
|
|
151
157
|
"chai-subset": "1.6.0",
|
|
152
158
|
"documentation": "14.0.3",
|
|
@@ -160,7 +166,6 @@
|
|
|
160
166
|
"graphql": "16.11.0",
|
|
161
167
|
"graphql-tag": "^2.12.6",
|
|
162
168
|
"husky": "9.1.7",
|
|
163
|
-
"inquirer-test": "2.0.1",
|
|
164
169
|
"jsdoc": "^3.6.11",
|
|
165
170
|
"jsdoc-typeof-plugin": "1.0.0",
|
|
166
171
|
"json-server": "0.17.4",
|
|
@@ -170,12 +175,11 @@
|
|
|
170
175
|
"puppeteer": "24.15.0",
|
|
171
176
|
"qrcode-terminal": "0.12.0",
|
|
172
177
|
"rosie": "2.1.1",
|
|
173
|
-
"runok": "0.9.3",
|
|
174
|
-
"semver": "7.7.
|
|
178
|
+
"runok": "^0.9.3",
|
|
179
|
+
"semver": "7.7.3",
|
|
175
180
|
"sinon": "21.0.0",
|
|
176
181
|
"sinon-chai": "3.7.0",
|
|
177
|
-
"
|
|
178
|
-
"ts-morph": "26.0.0",
|
|
182
|
+
"ts-morph": "27.0.2",
|
|
179
183
|
"ts-node": "10.9.2",
|
|
180
184
|
"tsd": "^0.33.0",
|
|
181
185
|
"tsd-jsdoc": "2.5.0",
|
package/translations/de-DE.js
CHANGED
package/translations/fr-FR.js
CHANGED
package/translations/index.js
CHANGED
|
@@ -1,10 +1,23 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
1
|
+
import deDE from './de-DE.js'
|
|
2
|
+
import itIT from './it-IT.js'
|
|
3
|
+
import frFR from './fr-FR.js'
|
|
4
|
+
import jaJP from './ja-JP.js'
|
|
5
|
+
import plPL from './pl-PL.js'
|
|
6
|
+
import ptBR from './pt-BR.js'
|
|
7
|
+
import ruRU from './ru-RU.js'
|
|
8
|
+
import zhCN from './zh-CN.js'
|
|
9
|
+
import zhTW from './zh-TW.js'
|
|
10
|
+
import nlNL from './nl-NL.js'
|
|
11
|
+
|
|
12
|
+
export default {
|
|
13
|
+
'de-DE': deDE,
|
|
14
|
+
'it-IT': itIT,
|
|
15
|
+
'fr-FR': frFR,
|
|
16
|
+
'ja-JP': jaJP,
|
|
17
|
+
'pl-PL': plPL,
|
|
18
|
+
'pt-BR': ptBR,
|
|
19
|
+
'ru-RU': ruRU,
|
|
20
|
+
'zh-CN': zhCN,
|
|
21
|
+
'zh-TW': zhTW,
|
|
22
|
+
'nl-NL': nlNL
|
|
23
|
+
}
|