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
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
import tty from 'tty'
|
|
2
2
|
|
|
3
3
|
if (!tty.getWindowSize) {
|
|
4
4
|
// this is really old method, long removed from Node, but Mocha
|
|
@@ -7,33 +7,39 @@ if (!tty.getWindowSize) {
|
|
|
7
7
|
tty.getWindowSize = () => [40, 80]
|
|
8
8
|
}
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
10
|
+
import { parentPort, workerData } from 'worker_threads'
|
|
11
|
+
|
|
12
|
+
// Delay imports to avoid ES Module loader race conditions in Node 22.x worker threads
|
|
13
|
+
// These will be imported dynamically when needed
|
|
14
|
+
let event, container, Codecept, getConfig, tryOrDefault, deepMerge
|
|
15
15
|
|
|
16
16
|
let stdout = ''
|
|
17
17
|
|
|
18
18
|
const stderr = ''
|
|
19
19
|
|
|
20
|
-
// Requiring of Codecept need to be after tty.getWindowSize is available.
|
|
21
|
-
const Codecept = require(process.env.CODECEPT_CLASS_PATH || '../../codecept')
|
|
22
|
-
|
|
23
20
|
const { options, tests, testRoot, workerIndex, poolMode } = workerData
|
|
24
21
|
|
|
25
22
|
// hide worker output
|
|
26
23
|
// In pool mode, only suppress output if debug is NOT enabled
|
|
27
24
|
// In regular mode, hide result output but allow step output in verbose/debug
|
|
28
25
|
if (poolMode && !options.debug) {
|
|
29
|
-
// In pool mode without debug,
|
|
26
|
+
// In pool mode without debug, allow test names and important output but suppress verbose details
|
|
30
27
|
const originalWrite = process.stdout.write
|
|
31
28
|
process.stdout.write = string => {
|
|
32
|
-
//
|
|
33
|
-
if (
|
|
29
|
+
// Allow test names (✔ or ✖), Scenario Steps, failures, and important markers
|
|
30
|
+
if (
|
|
31
|
+
string.includes('✔') ||
|
|
32
|
+
string.includes('✖') ||
|
|
33
|
+
string.includes('Scenario Steps:') ||
|
|
34
|
+
string.includes('◯ Scenario Steps:') ||
|
|
35
|
+
string.includes('-- FAILURES:') ||
|
|
36
|
+
string.includes('AssertionError:') ||
|
|
37
|
+
string.includes('Feature(')
|
|
38
|
+
) {
|
|
34
39
|
return originalWrite.call(process.stdout, string)
|
|
35
40
|
}
|
|
36
|
-
|
|
41
|
+
// Suppress result summaries to avoid duplicates
|
|
42
|
+
if (string.includes(' FAIL |') || string.includes(' OK |') || string.includes('◯ File:')) {
|
|
37
43
|
return true
|
|
38
44
|
}
|
|
39
45
|
return originalWrite.call(process.stdout, string)
|
|
@@ -67,31 +73,62 @@ if (poolMode && !options.debug) {
|
|
|
67
73
|
}
|
|
68
74
|
}
|
|
69
75
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
76
|
+
// Declare codecept and mocha at module level so they can be accessed by functions
|
|
77
|
+
let codecept
|
|
78
|
+
let mocha
|
|
79
|
+
let initPromise
|
|
80
|
+
let config
|
|
74
81
|
|
|
75
82
|
// Load test and run
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
const
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
83
|
+
initPromise = (async function () {
|
|
84
|
+
try {
|
|
85
|
+
// Import modules dynamically to avoid ES Module loader race conditions in Node 22.x
|
|
86
|
+
const eventModule = await import('../../event.js')
|
|
87
|
+
const containerModule = await import('../../container.js')
|
|
88
|
+
const utilsModule = await import('../utils.js')
|
|
89
|
+
const coreUtilsModule = await import('../../utils.js')
|
|
90
|
+
const CodeceptModule = await import('../../codecept.js')
|
|
91
|
+
|
|
92
|
+
event = eventModule.default
|
|
93
|
+
container = containerModule.default
|
|
94
|
+
getConfig = utilsModule.getConfig
|
|
95
|
+
tryOrDefault = coreUtilsModule.tryOrDefault
|
|
96
|
+
deepMerge = coreUtilsModule.deepMerge
|
|
97
|
+
Codecept = CodeceptModule.default
|
|
98
|
+
|
|
99
|
+
const overrideConfigs = tryOrDefault(() => JSON.parse(options.override), {})
|
|
100
|
+
|
|
101
|
+
// IMPORTANT: await is required here since getConfig is async
|
|
102
|
+
const baseConfig = await getConfig(options.config || testRoot)
|
|
103
|
+
|
|
104
|
+
// important deep merge so dynamic things e.g. functions on config are not overridden
|
|
105
|
+
config = deepMerge(baseConfig, overrideConfigs)
|
|
106
|
+
|
|
107
|
+
codecept = new Codecept(config, options)
|
|
108
|
+
await codecept.init(testRoot)
|
|
109
|
+
codecept.loadTests()
|
|
110
|
+
mocha = container.mocha()
|
|
111
|
+
|
|
112
|
+
if (poolMode) {
|
|
113
|
+
// In pool mode, don't filter tests upfront - wait for assignments
|
|
114
|
+
// We'll reload test files fresh for each test request
|
|
115
|
+
} else {
|
|
116
|
+
// Legacy mode - filter tests upfront
|
|
117
|
+
filterTests()
|
|
118
|
+
}
|
|
88
119
|
|
|
89
|
-
// run tests
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
120
|
+
// run tests
|
|
121
|
+
if (poolMode) {
|
|
122
|
+
await runPoolTests()
|
|
123
|
+
} else if (mocha.suite.total()) {
|
|
124
|
+
await runTests()
|
|
125
|
+
} else {
|
|
126
|
+
// No tests to run, close the worker
|
|
127
|
+
parentPort?.close()
|
|
128
|
+
}
|
|
129
|
+
} catch (err) {
|
|
130
|
+
console.error('Error in worker initialization:', err)
|
|
131
|
+
process.exit(1)
|
|
95
132
|
}
|
|
96
133
|
})()
|
|
97
134
|
|
|
@@ -123,6 +160,9 @@ async function runPoolTests() {
|
|
|
123
160
|
initializeListeners()
|
|
124
161
|
disablePause()
|
|
125
162
|
|
|
163
|
+
// Emit event.all.before once at the start of pool mode
|
|
164
|
+
event.dispatcher.emit(event.all.before, codecept)
|
|
165
|
+
|
|
126
166
|
// Accumulate results across all tests in pool mode
|
|
127
167
|
let consolidatedStats = { passes: 0, failures: 0, tests: 0, pending: 0, failedHooks: 0 }
|
|
128
168
|
let allTests = []
|
|
@@ -131,25 +171,42 @@ async function runPoolTests() {
|
|
|
131
171
|
|
|
132
172
|
// Keep requesting tests until no more available
|
|
133
173
|
while (true) {
|
|
134
|
-
// Request a test assignment
|
|
135
|
-
sendToParentThread({ type: 'REQUEST_TEST', workerIndex })
|
|
136
|
-
|
|
174
|
+
// Request a test assignment and wait for response
|
|
137
175
|
const testResult = await new Promise((resolve, reject) => {
|
|
138
|
-
// Set up pool mode message handler
|
|
176
|
+
// Set up pool mode message handler FIRST before sending request
|
|
139
177
|
const messageHandler = async eventData => {
|
|
178
|
+
// Remove handler immediately to prevent duplicate processing
|
|
179
|
+
parentPort?.off('message', messageHandler)
|
|
180
|
+
|
|
140
181
|
if (eventData.type === 'TEST_ASSIGNED') {
|
|
141
|
-
|
|
182
|
+
// In pool mode with ESM, we receive test FILE paths instead of UIDs
|
|
183
|
+
// because UIDs are not stable across different mocha instances
|
|
184
|
+
const testIdentifier = eventData.test
|
|
142
185
|
|
|
143
186
|
try {
|
|
144
|
-
//
|
|
145
|
-
|
|
146
|
-
container.createMocha() // Create fresh Mocha instance
|
|
147
|
-
filterTestById(testUid)
|
|
187
|
+
// Create a fresh Mocha instance for each test file
|
|
188
|
+
container.createMocha()
|
|
148
189
|
const mocha = container.mocha()
|
|
190
|
+
|
|
191
|
+
// Load only the assigned test file
|
|
192
|
+
mocha.files = [testIdentifier]
|
|
193
|
+
mocha.loadFiles()
|
|
194
|
+
|
|
195
|
+
try {
|
|
196
|
+
require('fs').appendFileSync('/tmp/config_listener_debug.log', `${new Date().toISOString()} [POOL] Loaded ${testIdentifier}, tests: ${mocha.suite.total()}\n`)
|
|
197
|
+
} catch (e) { /* ignore */ }
|
|
149
198
|
|
|
150
199
|
if (mocha.suite.total() > 0) {
|
|
151
|
-
// Run the
|
|
152
|
-
|
|
200
|
+
// Run only the tests in the current mocha suite
|
|
201
|
+
// Don't use codecept.run() as it overwrites mocha.files with ALL test files
|
|
202
|
+
await new Promise((resolve, reject) => {
|
|
203
|
+
mocha.run(() => {
|
|
204
|
+
try {
|
|
205
|
+
require('fs').appendFileSync('/tmp/config_listener_debug.log', `${new Date().toISOString()} [POOL] Finished ${testIdentifier}\n`)
|
|
206
|
+
} catch (e) { /* ignore */ }
|
|
207
|
+
resolve()
|
|
208
|
+
})
|
|
209
|
+
})
|
|
153
210
|
|
|
154
211
|
// Get the results from this specific test run
|
|
155
212
|
const result = container.result()
|
|
@@ -179,24 +236,26 @@ async function runPoolTests() {
|
|
|
179
236
|
}
|
|
180
237
|
}
|
|
181
238
|
|
|
182
|
-
// Signal test completed
|
|
183
|
-
parentPort?.off('message', messageHandler)
|
|
239
|
+
// Signal test completed
|
|
184
240
|
resolve('TEST_COMPLETED')
|
|
185
241
|
} catch (err) {
|
|
186
|
-
parentPort?.off('message', messageHandler)
|
|
187
242
|
reject(err)
|
|
188
243
|
}
|
|
189
244
|
} else if (eventData.type === 'NO_MORE_TESTS') {
|
|
190
245
|
// No tests available, exit worker
|
|
191
|
-
parentPort?.off('message', messageHandler)
|
|
192
246
|
resolve('NO_MORE_TESTS')
|
|
193
247
|
} else {
|
|
194
248
|
// Handle other message types (support messages, etc.)
|
|
195
249
|
container.append({ support: eventData.data })
|
|
250
|
+
// Don't re-add handler - each test request creates its own one-time handler
|
|
196
251
|
}
|
|
197
252
|
}
|
|
198
253
|
|
|
254
|
+
// Set up handler BEFORE sending request to avoid race condition
|
|
199
255
|
parentPort?.on('message', messageHandler)
|
|
256
|
+
|
|
257
|
+
// Now send the request
|
|
258
|
+
sendToParentThread({ type: 'REQUEST_TEST', workerIndex })
|
|
200
259
|
})
|
|
201
260
|
|
|
202
261
|
// Exit if no more tests
|
|
@@ -205,6 +264,9 @@ async function runPoolTests() {
|
|
|
205
264
|
}
|
|
206
265
|
}
|
|
207
266
|
|
|
267
|
+
// Emit event.all.after once at the end of pool mode
|
|
268
|
+
event.dispatcher.emit(event.all.after, codecept)
|
|
269
|
+
|
|
208
270
|
try {
|
|
209
271
|
await codecept.teardown()
|
|
210
272
|
} catch (err) {
|
|
@@ -232,70 +294,38 @@ async function runPoolTests() {
|
|
|
232
294
|
}
|
|
233
295
|
|
|
234
296
|
function filterTestById(testUid) {
|
|
235
|
-
//
|
|
236
|
-
|
|
237
|
-
|
|
297
|
+
// In pool mode with ESM, test files are already loaded once at initialization
|
|
298
|
+
// We just need to filter the existing mocha suite to only include the target test
|
|
299
|
+
|
|
238
300
|
// Get the existing mocha instance
|
|
239
301
|
const mocha = container.mocha()
|
|
240
302
|
|
|
303
|
+
// Save reference to all suites before clearing
|
|
304
|
+
const allSuites = [...mocha.suite.suites]
|
|
305
|
+
|
|
241
306
|
// Clear suites and tests but preserve other mocha settings
|
|
242
307
|
mocha.suite.suites = []
|
|
243
308
|
mocha.suite.tests = []
|
|
244
309
|
|
|
245
|
-
//
|
|
246
|
-
files.forEach(file => {
|
|
247
|
-
delete require.cache[require.resolve(file)]
|
|
248
|
-
})
|
|
249
|
-
|
|
250
|
-
// Set files and load them
|
|
251
|
-
mocha.files = files
|
|
252
|
-
mocha.loadFiles()
|
|
253
|
-
|
|
254
|
-
// Now filter to only the target test - use a more robust approach
|
|
310
|
+
// Find and add only the suite containing our target test
|
|
255
311
|
let foundTest = false
|
|
256
|
-
for (const suite of
|
|
312
|
+
for (const suite of allSuites) {
|
|
257
313
|
const originalTests = [...suite.tests]
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
// If no tests found in this suite, remove it
|
|
269
|
-
if (suite.tests.length === 0) {
|
|
270
|
-
suite.parent.suites = suite.parent.suites.filter(s => s !== suite)
|
|
314
|
+
|
|
315
|
+
// Check if this suite has our target test
|
|
316
|
+
const targetTest = originalTests.find(test => test.uid === testUid)
|
|
317
|
+
|
|
318
|
+
if (targetTest) {
|
|
319
|
+
// Create a filtered suite with only the target test
|
|
320
|
+
suite.tests = [targetTest]
|
|
321
|
+
mocha.suite.suites.push(suite)
|
|
322
|
+
foundTest = true
|
|
323
|
+
break // Only include one test
|
|
271
324
|
}
|
|
272
325
|
}
|
|
273
326
|
|
|
274
|
-
// Filter out empty suites from the root
|
|
275
|
-
mocha.suite.suites = mocha.suite.suites.filter(suite => suite.tests.length > 0)
|
|
276
|
-
|
|
277
327
|
if (!foundTest) {
|
|
278
|
-
|
|
279
|
-
mocha.suite.suites = []
|
|
280
|
-
mocha.suite.tests = []
|
|
281
|
-
mocha.loadFiles()
|
|
282
|
-
|
|
283
|
-
// Try matching by title
|
|
284
|
-
for (const suite of mocha.suite.suites) {
|
|
285
|
-
const originalTests = [...suite.tests]
|
|
286
|
-
suite.tests = []
|
|
287
|
-
|
|
288
|
-
for (const test of originalTests) {
|
|
289
|
-
if (test.title === testUid || test.fullTitle() === testUid || test.uid === testUid) {
|
|
290
|
-
suite.tests.push(test)
|
|
291
|
-
foundTest = true
|
|
292
|
-
break
|
|
293
|
-
}
|
|
294
|
-
}
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
// Clean up empty suites again
|
|
298
|
-
mocha.suite.suites = mocha.suite.suites.filter(suite => suite.tests.length > 0)
|
|
328
|
+
console.error(`WARNING: Test with UID ${testUid} not found in mocha suites`)
|
|
299
329
|
}
|
|
300
330
|
}
|
|
301
331
|
|
|
@@ -311,27 +341,36 @@ function filterTests() {
|
|
|
311
341
|
|
|
312
342
|
function initializeListeners() {
|
|
313
343
|
// suite
|
|
314
|
-
event.dispatcher.on(event.suite.before, suite =>
|
|
315
|
-
event.dispatcher.on(event.suite.after, suite =>
|
|
344
|
+
event.dispatcher.on(event.suite.before, suite => safelySendToParent({ event: event.suite.before, workerIndex, data: suite.simplify() }))
|
|
345
|
+
event.dispatcher.on(event.suite.after, suite => safelySendToParent({ event: event.suite.after, workerIndex, data: suite.simplify() }))
|
|
316
346
|
|
|
317
347
|
// calculate duration
|
|
318
348
|
event.dispatcher.on(event.test.started, test => (test.start = new Date()))
|
|
319
349
|
|
|
320
350
|
// tests
|
|
321
|
-
event.dispatcher.on(event.test.before, test =>
|
|
322
|
-
event.dispatcher.on(event.test.after, test =>
|
|
351
|
+
event.dispatcher.on(event.test.before, test => safelySendToParent({ event: event.test.before, workerIndex, data: test.simplify() }))
|
|
352
|
+
event.dispatcher.on(event.test.after, test => safelySendToParent({ event: event.test.after, workerIndex, data: test.simplify() }))
|
|
323
353
|
// we should force-send correct errors to prevent race condition
|
|
324
|
-
event.dispatcher.on(event.test.finished, (test, err) =>
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
354
|
+
event.dispatcher.on(event.test.finished, (test, err) => {
|
|
355
|
+
const simplifiedData = test.simplify()
|
|
356
|
+
const serializableErr = serializeError(err)
|
|
357
|
+
safelySendToParent({ event: event.test.finished, workerIndex, data: { ...simplifiedData, err: serializableErr } })
|
|
358
|
+
})
|
|
359
|
+
event.dispatcher.on(event.test.failed, (test, err, hookName) => {
|
|
360
|
+
const simplifiedData = test.simplify()
|
|
361
|
+
const serializableErr = serializeError(err)
|
|
362
|
+
// Include hookName to identify hook failures
|
|
363
|
+
safelySendToParent({ event: event.test.failed, workerIndex, data: { ...simplifiedData, err: serializableErr, hookName } })
|
|
364
|
+
})
|
|
365
|
+
event.dispatcher.on(event.test.passed, (test, err) => safelySendToParent({ event: event.test.passed, workerIndex, data: { ...test.simplify(), err } }))
|
|
366
|
+
event.dispatcher.on(event.test.started, test => safelySendToParent({ event: event.test.started, workerIndex, data: test.simplify() }))
|
|
367
|
+
event.dispatcher.on(event.test.skipped, test => safelySendToParent({ event: event.test.skipped, workerIndex, data: test.simplify() }))
|
|
329
368
|
|
|
330
369
|
// steps
|
|
331
|
-
event.dispatcher.on(event.step.finished, step =>
|
|
332
|
-
event.dispatcher.on(event.step.started, step =>
|
|
333
|
-
event.dispatcher.on(event.step.passed, step =>
|
|
334
|
-
event.dispatcher.on(event.step.failed, step =>
|
|
370
|
+
event.dispatcher.on(event.step.finished, step => safelySendToParent({ event: event.step.finished, workerIndex, data: step.simplify() }))
|
|
371
|
+
event.dispatcher.on(event.step.started, step => safelySendToParent({ event: event.step.started, workerIndex, data: step.simplify() }))
|
|
372
|
+
event.dispatcher.on(event.step.passed, step => safelySendToParent({ event: event.step.passed, workerIndex, data: step.simplify() }))
|
|
373
|
+
event.dispatcher.on(event.step.failed, step => safelySendToParent({ event: event.step.failed, workerIndex, data: step.simplify() }))
|
|
335
374
|
|
|
336
375
|
event.dispatcher.on(event.hook.failed, (hook, err) => sendToParentThread({ event: event.hook.failed, workerIndex, data: { ...hook.simplify(), err } }))
|
|
337
376
|
event.dispatcher.on(event.hook.passed, hook => sendToParentThread({ event: event.hook.passed, workerIndex, data: hook.simplify() }))
|
|
@@ -357,6 +396,50 @@ function disablePause() {
|
|
|
357
396
|
global.pause = () => {}
|
|
358
397
|
}
|
|
359
398
|
|
|
399
|
+
function serializeError(err) {
|
|
400
|
+
if (!err) return null
|
|
401
|
+
try {
|
|
402
|
+
return {
|
|
403
|
+
message: err.message,
|
|
404
|
+
stack: err.stack,
|
|
405
|
+
name: err.name,
|
|
406
|
+
actual: err.actual,
|
|
407
|
+
expected: err.expected,
|
|
408
|
+
}
|
|
409
|
+
} catch {
|
|
410
|
+
return { message: 'Error could not be serialized', name: 'Error' }
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
function safelySendToParent(data) {
|
|
415
|
+
try {
|
|
416
|
+
parentPort?.postMessage(data)
|
|
417
|
+
} catch (cloneError) {
|
|
418
|
+
// Fallback for non-serializable data
|
|
419
|
+
const fallbackData = { ...data }
|
|
420
|
+
|
|
421
|
+
// Try to serialize error objects if present
|
|
422
|
+
if (fallbackData.data && fallbackData.data.err) {
|
|
423
|
+
fallbackData.data.err = serializeError(fallbackData.data.err)
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
// If still fails, send minimal data
|
|
427
|
+
try {
|
|
428
|
+
parentPort?.postMessage(fallbackData)
|
|
429
|
+
} catch (finalError) {
|
|
430
|
+
parentPort?.postMessage({
|
|
431
|
+
event: data.event,
|
|
432
|
+
workerIndex,
|
|
433
|
+
data: {
|
|
434
|
+
title: fallbackData.data?.title || 'Unknown',
|
|
435
|
+
state: fallbackData.data?.state || 'error',
|
|
436
|
+
err: { message: 'Data could not be serialized' },
|
|
437
|
+
},
|
|
438
|
+
})
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
|
|
360
443
|
function sendToParentThread(data) {
|
|
361
444
|
parentPort?.postMessage(data)
|
|
362
445
|
}
|