codeceptjs 4.0.0-beta.4 → 4.0.0-beta.5

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.
Files changed (150) hide show
  1. package/README.md +134 -119
  2. package/bin/codecept.js +12 -2
  3. package/bin/test-server.js +53 -0
  4. package/docs/webapi/clearCookie.mustache +1 -1
  5. package/lib/actor.js +66 -102
  6. package/lib/ai.js +130 -121
  7. package/lib/assert/empty.js +3 -5
  8. package/lib/assert/equal.js +4 -7
  9. package/lib/assert/include.js +4 -6
  10. package/lib/assert/throws.js +2 -4
  11. package/lib/assert/truth.js +2 -2
  12. package/lib/codecept.js +139 -87
  13. package/lib/command/check.js +201 -0
  14. package/lib/command/configMigrate.js +2 -4
  15. package/lib/command/definitions.js +8 -26
  16. package/lib/command/generate.js +10 -14
  17. package/lib/command/gherkin/snippets.js +75 -73
  18. package/lib/command/gherkin/steps.js +1 -1
  19. package/lib/command/info.js +42 -8
  20. package/lib/command/init.js +13 -12
  21. package/lib/command/interactive.js +10 -2
  22. package/lib/command/list.js +1 -1
  23. package/lib/command/run-multiple/chunk.js +48 -45
  24. package/lib/command/run-multiple.js +12 -35
  25. package/lib/command/run-workers.js +21 -58
  26. package/lib/command/utils.js +5 -6
  27. package/lib/command/workers/runTests.js +262 -220
  28. package/lib/container.js +386 -238
  29. package/lib/data/context.js +10 -13
  30. package/lib/data/dataScenarioConfig.js +8 -8
  31. package/lib/data/dataTableArgument.js +6 -6
  32. package/lib/data/table.js +5 -11
  33. package/lib/effects.js +223 -0
  34. package/lib/element/WebElement.js +327 -0
  35. package/lib/els.js +158 -0
  36. package/lib/event.js +21 -17
  37. package/lib/heal.js +88 -80
  38. package/lib/helper/AI.js +2 -1
  39. package/lib/helper/ApiDataFactory.js +3 -6
  40. package/lib/helper/Appium.js +47 -51
  41. package/lib/helper/FileSystem.js +3 -3
  42. package/lib/helper/GraphQLDataFactory.js +3 -3
  43. package/lib/helper/JSONResponse.js +75 -37
  44. package/lib/helper/Mochawesome.js +31 -9
  45. package/lib/helper/Nightmare.js +35 -53
  46. package/lib/helper/Playwright.js +262 -267
  47. package/lib/helper/Protractor.js +54 -77
  48. package/lib/helper/Puppeteer.js +246 -260
  49. package/lib/helper/REST.js +5 -17
  50. package/lib/helper/TestCafe.js +21 -44
  51. package/lib/helper/WebDriver.js +151 -170
  52. package/lib/helper/extras/Popup.js +22 -22
  53. package/lib/helper/testcafe/testcafe-utils.js +26 -27
  54. package/lib/listener/emptyRun.js +55 -0
  55. package/lib/listener/exit.js +7 -10
  56. package/lib/listener/{retry.js → globalRetry.js} +5 -5
  57. package/lib/listener/globalTimeout.js +165 -0
  58. package/lib/listener/helpers.js +15 -15
  59. package/lib/listener/mocha.js +1 -1
  60. package/lib/listener/result.js +12 -0
  61. package/lib/listener/retryEnhancer.js +85 -0
  62. package/lib/listener/steps.js +32 -18
  63. package/lib/listener/store.js +20 -0
  64. package/lib/mocha/asyncWrapper.js +231 -0
  65. package/lib/{interfaces → mocha}/bdd.js +3 -3
  66. package/lib/mocha/cli.js +308 -0
  67. package/lib/mocha/factory.js +104 -0
  68. package/lib/{interfaces → mocha}/featureConfig.js +32 -12
  69. package/lib/{interfaces → mocha}/gherkin.js +26 -28
  70. package/lib/mocha/hooks.js +112 -0
  71. package/lib/mocha/index.js +12 -0
  72. package/lib/mocha/inject.js +29 -0
  73. package/lib/{interfaces → mocha}/scenarioConfig.js +31 -7
  74. package/lib/mocha/suite.js +82 -0
  75. package/lib/mocha/test.js +181 -0
  76. package/lib/mocha/types.d.ts +42 -0
  77. package/lib/mocha/ui.js +232 -0
  78. package/lib/output.js +82 -62
  79. package/lib/pause.js +160 -138
  80. package/lib/plugin/analyze.js +396 -0
  81. package/lib/plugin/auth.js +435 -0
  82. package/lib/plugin/autoDelay.js +8 -8
  83. package/lib/plugin/autoLogin.js +3 -338
  84. package/lib/plugin/commentStep.js +6 -1
  85. package/lib/plugin/coverage.js +10 -19
  86. package/lib/plugin/customLocator.js +3 -3
  87. package/lib/plugin/customReporter.js +52 -0
  88. package/lib/plugin/eachElement.js +1 -1
  89. package/lib/plugin/fakerTransform.js +1 -1
  90. package/lib/plugin/heal.js +36 -9
  91. package/lib/plugin/htmlReporter.js +1947 -0
  92. package/lib/plugin/pageInfo.js +140 -0
  93. package/lib/plugin/retryFailedStep.js +17 -18
  94. package/lib/plugin/retryTo.js +2 -113
  95. package/lib/plugin/screenshotOnFail.js +17 -58
  96. package/lib/plugin/selenoid.js +15 -35
  97. package/lib/plugin/standardActingHelpers.js +4 -1
  98. package/lib/plugin/stepByStepReport.js +56 -17
  99. package/lib/plugin/stepTimeout.js +5 -12
  100. package/lib/plugin/subtitles.js +4 -4
  101. package/lib/plugin/tryTo.js +3 -102
  102. package/lib/plugin/wdio.js +8 -10
  103. package/lib/recorder.js +155 -124
  104. package/lib/rerun.js +43 -42
  105. package/lib/result.js +161 -0
  106. package/lib/secret.js +1 -1
  107. package/lib/step/base.js +239 -0
  108. package/lib/step/comment.js +10 -0
  109. package/lib/step/config.js +50 -0
  110. package/lib/step/func.js +46 -0
  111. package/lib/step/helper.js +50 -0
  112. package/lib/step/meta.js +99 -0
  113. package/lib/step/record.js +74 -0
  114. package/lib/step/retry.js +11 -0
  115. package/lib/step/section.js +55 -0
  116. package/lib/step.js +21 -332
  117. package/lib/steps.js +50 -0
  118. package/lib/store.js +37 -5
  119. package/lib/template/heal.js +2 -11
  120. package/lib/test-server.js +323 -0
  121. package/lib/timeout.js +66 -0
  122. package/lib/utils.js +351 -218
  123. package/lib/within.js +75 -55
  124. package/lib/workerStorage.js +2 -1
  125. package/lib/workers.js +386 -276
  126. package/package.json +76 -70
  127. package/translations/de-DE.js +4 -3
  128. package/translations/fr-FR.js +4 -3
  129. package/translations/index.js +1 -0
  130. package/translations/it-IT.js +4 -3
  131. package/translations/ja-JP.js +4 -3
  132. package/translations/nl-NL.js +76 -0
  133. package/translations/pl-PL.js +4 -3
  134. package/translations/pt-BR.js +4 -3
  135. package/translations/ru-RU.js +4 -3
  136. package/translations/utils.js +9 -0
  137. package/translations/zh-CN.js +4 -3
  138. package/translations/zh-TW.js +4 -3
  139. package/typings/index.d.ts +188 -186
  140. package/typings/promiseBasedTypes.d.ts +18 -705
  141. package/typings/types.d.ts +301 -804
  142. package/lib/cli.js +0 -256
  143. package/lib/helper/ExpectHelper.js +0 -391
  144. package/lib/helper/SoftExpectHelper.js +0 -381
  145. package/lib/listener/artifacts.js +0 -19
  146. package/lib/listener/timeout.js +0 -109
  147. package/lib/mochaFactory.js +0 -113
  148. package/lib/plugin/debugErrors.js +0 -67
  149. package/lib/scenario.js +0 -224
  150. package/lib/ui.js +0 -236
@@ -1,291 +1,333 @@
1
- const tty = require('tty');
1
+ const tty = require('tty')
2
2
 
3
3
  if (!tty.getWindowSize) {
4
4
  // this is really old method, long removed from Node, but Mocha
5
5
  // reporters fall back on it if they cannot use `process.stdout.getWindowSize`
6
6
  // we need to polyfill it.
7
- tty.getWindowSize = () => [40, 80];
7
+ tty.getWindowSize = () => [40, 80]
8
8
  }
9
9
 
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')
15
15
 
16
- let stdout = '';
16
+ let stdout = ''
17
17
 
18
- const stderr = '';
18
+ const stderr = ''
19
19
 
20
20
  // 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')
22
22
 
23
- const {
24
- options, tests, testRoot, workerIndex,
25
- } = workerData;
23
+ const { options, tests, testRoot, workerIndex, poolMode } = workerData
26
24
 
27
25
  // hide worker output
28
- if (!options.debug && !options.verbose) process.stdout.write = (string) => { stdout += string; return true; };
26
+ if (!options.debug && !options.verbose)
27
+ process.stdout.write = string => {
28
+ stdout += string
29
+ return true
30
+ }
29
31
 
30
- const overrideConfigs = tryOrDefault(() => JSON.parse(options.override), {});
32
+ const overrideConfigs = tryOrDefault(() => JSON.parse(options.override), {})
31
33
 
32
34
  // important deep merge so dynamic things e.g. functions on config are not overridden
33
- const config = deepMerge(getConfig(options.config || testRoot), overrideConfigs);
35
+ const config = deepMerge(getConfig(options.config || testRoot), overrideConfigs)
34
36
 
35
37
  // Load test and run
36
- const codecept = new Codecept(config, options);
37
- codecept.init(testRoot);
38
- codecept.loadTests();
39
- const mocha = container.mocha();
40
- filterTests();
41
-
42
- (async function () {
43
- if (mocha.suite.total()) {
44
- await runTests();
38
+ const codecept = new Codecept(config, options)
39
+ codecept.init(testRoot)
40
+ codecept.loadTests()
41
+ const mocha = container.mocha()
42
+
43
+ if (poolMode) {
44
+ // In pool mode, don't filter tests upfront - wait for assignments
45
+ // We'll reload test files fresh for each test request
46
+ } else {
47
+ // Legacy mode - filter tests upfront
48
+ filterTests()
49
+ }
50
+
51
+ // run tests
52
+ ;(async function () {
53
+ if (poolMode) {
54
+ await runPoolTests()
55
+ } else if (mocha.suite.total()) {
56
+ await runTests()
45
57
  }
46
- }());
58
+ })()
59
+
60
+ let globalStats = { passes: 0, failures: 0, tests: 0, pending: 0, failedHooks: 0 }
47
61
 
48
62
  async function runTests() {
49
63
  try {
50
- await codecept.bootstrap();
64
+ await codecept.bootstrap()
51
65
  } catch (err) {
52
- throw new Error(`Error while running bootstrap file :${err}`);
66
+ throw new Error(`Error while running bootstrap file :${err}`)
53
67
  }
54
- listenToParentThread();
55
- initializeListeners();
56
- disablePause();
68
+ listenToParentThread()
69
+ initializeListeners()
70
+ disablePause()
57
71
  try {
58
- await codecept.run();
72
+ await codecept.run()
59
73
  } finally {
60
- await codecept.teardown();
74
+ await codecept.teardown()
61
75
  }
62
76
  }
63
77
 
64
- function filterTests() {
65
- const files = codecept.testFiles;
66
- mocha.files = files;
67
- mocha.loadFiles();
68
-
69
- for (const suite of mocha.suite.suites) {
70
- suite.tests = suite.tests.filter(test => tests.indexOf(test.uid) >= 0);
78
+ async function runPoolTests() {
79
+ try {
80
+ await codecept.bootstrap()
81
+ } catch (err) {
82
+ throw new Error(`Error while running bootstrap file :${err}`)
71
83
  }
72
- }
73
84
 
74
- function initializeListeners() {
75
- function simplifyError(error) {
76
- if (error) {
77
- const {
78
- stack,
79
- uncaught,
80
- message,
81
- actual,
82
- expected,
83
- } = error;
84
-
85
- return {
86
- stack,
87
- uncaught,
88
- message,
89
- actual,
90
- expected,
91
- };
85
+ initializeListeners()
86
+ disablePause()
87
+
88
+ // Accumulate results across all tests in pool mode
89
+ let consolidatedStats = { passes: 0, failures: 0, tests: 0, pending: 0, failedHooks: 0 }
90
+ let allTests = []
91
+ let allFailures = []
92
+ let previousStats = { passes: 0, failures: 0, tests: 0, pending: 0, failedHooks: 0 }
93
+
94
+ // Keep requesting tests until no more available
95
+ while (true) {
96
+ // Request a test assignment
97
+ sendToParentThread({ type: 'REQUEST_TEST', workerIndex })
98
+
99
+ const testResult = await new Promise((resolve, reject) => {
100
+ // Set up pool mode message handler
101
+ const messageHandler = async eventData => {
102
+ if (eventData.type === 'TEST_ASSIGNED') {
103
+ const testUid = eventData.test
104
+
105
+ try {
106
+ // In pool mode, we need to create a fresh Mocha instance for each test
107
+ // because Mocha instances become disposed after running tests
108
+ container.createMocha() // Create fresh Mocha instance
109
+ filterTestById(testUid)
110
+ const mocha = container.mocha()
111
+
112
+ if (mocha.suite.total() > 0) {
113
+ // Run the test and complete
114
+ await codecept.run()
115
+
116
+ // Get the results from this specific test run
117
+ const result = container.result()
118
+ const currentStats = result.stats || {}
119
+
120
+ // Calculate the difference from previous accumulated stats
121
+ const newPasses = Math.max(0, (currentStats.passes || 0) - previousStats.passes)
122
+ const newFailures = Math.max(0, (currentStats.failures || 0) - previousStats.failures)
123
+ const newTests = Math.max(0, (currentStats.tests || 0) - previousStats.tests)
124
+ const newPending = Math.max(0, (currentStats.pending || 0) - previousStats.pending)
125
+ const newFailedHooks = Math.max(0, (currentStats.failedHooks || 0) - previousStats.failedHooks)
126
+
127
+ // Add only the new results
128
+ consolidatedStats.passes += newPasses
129
+ consolidatedStats.failures += newFailures
130
+ consolidatedStats.tests += newTests
131
+ consolidatedStats.pending += newPending
132
+ consolidatedStats.failedHooks += newFailedHooks
133
+
134
+ // Update previous stats for next comparison
135
+ previousStats = { ...currentStats }
136
+
137
+ // Add new failures to consolidated collections
138
+ if (result.failures && result.failures.length > allFailures.length) {
139
+ const newFailures = result.failures.slice(allFailures.length)
140
+ allFailures.push(...newFailures)
141
+ }
142
+ }
143
+
144
+ // Signal test completed and request next
145
+ parentPort?.off('message', messageHandler)
146
+ resolve('TEST_COMPLETED')
147
+ } catch (err) {
148
+ parentPort?.off('message', messageHandler)
149
+ reject(err)
150
+ }
151
+ } else if (eventData.type === 'NO_MORE_TESTS') {
152
+ // No tests available, exit worker
153
+ parentPort?.off('message', messageHandler)
154
+ resolve('NO_MORE_TESTS')
155
+ } else {
156
+ // Handle other message types (support messages, etc.)
157
+ container.append({ support: eventData.data })
158
+ }
159
+ }
160
+
161
+ parentPort?.on('message', messageHandler)
162
+ })
163
+
164
+ // Exit if no more tests
165
+ if (testResult === 'NO_MORE_TESTS') {
166
+ break
92
167
  }
168
+ }
93
169
 
94
- return null;
170
+ try {
171
+ await codecept.teardown()
172
+ } catch (err) {
173
+ // Log teardown errors but don't fail
174
+ console.error('Teardown error:', err)
95
175
  }
96
- function simplifyTest(test, err = null) {
97
- test = { ...test };
98
176
 
99
- if (test.start && !test.duration) {
100
- const end = new Date();
101
- test.duration = end - test.start;
102
- }
177
+ // Send final consolidated results for the entire worker
178
+ const finalResult = {
179
+ hasFailed: consolidatedStats.failures > 0,
180
+ stats: consolidatedStats,
181
+ duration: 0, // Pool mode doesn't track duration per worker
182
+ tests: [], // Keep tests empty to avoid serialization issues - stats are sufficient
183
+ failures: allFailures, // Include all failures for error reporting
184
+ }
103
185
 
104
- if (test.err) {
105
- err = simplifyError(test.err);
106
- test.status = 'failed';
107
- } else if (err) {
108
- err = simplifyError(err);
109
- test.status = 'failed';
110
- }
111
- const parent = {};
112
- if (test.parent) {
113
- parent.title = test.parent.title;
114
- }
186
+ sendToParentThread({ event: event.all.after, workerIndex, data: finalResult })
187
+ sendToParentThread({ event: event.all.result, workerIndex, data: finalResult })
115
188
 
116
- if (test.opts) {
117
- Object.keys(test.opts).forEach(k => {
118
- if (typeof test.opts[k] === 'object') delete test.opts[k];
119
- if (typeof test.opts[k] === 'function') delete test.opts[k];
120
- });
121
- }
189
+ // Add longer delay to ensure messages are delivered before closing
190
+ await new Promise(resolve => setTimeout(resolve, 100))
122
191
 
123
- return {
124
- opts: test.opts || {},
125
- tags: test.tags || [],
126
- uid: test.uid,
127
- workerIndex,
128
- retries: test._retries,
129
- title: test.title,
130
- status: test.status,
131
- duration: test.duration || 0,
132
- err,
133
- parent,
134
- steps: test.steps && test.steps.length > 0 ? simplifyStepsInTestObject(test.steps, err) : [],
135
- };
136
- }
192
+ // Close worker thread when pool mode is complete
193
+ parentPort?.close()
194
+ }
137
195
 
138
- function simplifyStepsInTestObject(steps, err) {
139
- steps = [...steps];
140
- const _steps = [];
141
-
142
- for (step of steps) {
143
- const _args = [];
144
-
145
- if (step.args) {
146
- for (const arg of step.args) {
147
- // check if arg is a JOI object
148
- if (arg && arg.$_root) {
149
- _args.push(JSON.stringify(arg).slice(0, 300));
150
- // check if arg is a function
151
- } else if (arg && typeof arg === 'function') {
152
- _args.push(arg.name);
153
- } else {
154
- _args.push(arg);
155
- }
156
- }
157
- }
196
+ function filterTestById(testUid) {
197
+ // Reload test files fresh for each test in pool mode
198
+ const files = codecept.testFiles
158
199
 
159
- _steps.push({
160
- actor: step.actor,
161
- name: step.name,
162
- status: step.status,
163
- args: JSON.stringify(_args),
164
- startedAt: step.startedAt,
165
- startTime: step.startTime,
166
- endTime: step.endTime,
167
- finishedAt: step.finishedAt,
168
- duration: step.duration,
169
- err,
170
- });
171
- }
200
+ // Get the existing mocha instance
201
+ const mocha = container.mocha()
172
202
 
173
- return _steps;
174
- }
203
+ // Clear suites and tests but preserve other mocha settings
204
+ mocha.suite.suites = []
205
+ mocha.suite.tests = []
175
206
 
176
- function simplifyStep(step, err = null) {
177
- step = { ...step };
207
+ // Clear require cache for test files to ensure fresh loading
208
+ files.forEach(file => {
209
+ delete require.cache[require.resolve(file)]
210
+ })
178
211
 
179
- if (step.startTime && !step.duration) {
180
- const end = new Date();
181
- step.duration = end - step.startTime;
182
- }
212
+ // Set files and load them
213
+ mocha.files = files
214
+ mocha.loadFiles()
183
215
 
184
- if (step.err) {
185
- err = simplifyError(step.err);
186
- step.status = 'failed';
187
- } else if (err) {
188
- err = simplifyError(err);
189
- step.status = 'failed';
216
+ // Now filter to only the target test - use a more robust approach
217
+ let foundTest = false
218
+ for (const suite of mocha.suite.suites) {
219
+ const originalTests = [...suite.tests]
220
+ suite.tests = []
221
+
222
+ for (const test of originalTests) {
223
+ if (test.uid === testUid) {
224
+ suite.tests.push(test)
225
+ foundTest = true
226
+ break // Only add one matching test
227
+ }
190
228
  }
191
229
 
192
- const parent = {};
193
- if (step.metaStep) {
194
- parent.title = step.metaStep.actor;
230
+ // If no tests found in this suite, remove it
231
+ if (suite.tests.length === 0) {
232
+ suite.parent.suites = suite.parent.suites.filter(s => s !== suite)
195
233
  }
234
+ }
196
235
 
197
- if (step.opts) {
198
- Object.keys(step.opts).forEach(k => {
199
- if (typeof step.opts[k] === 'object') delete step.opts[k];
200
- if (typeof step.opts[k] === 'function') delete step.opts[k];
201
- });
236
+ // Filter out empty suites from the root
237
+ mocha.suite.suites = mocha.suite.suites.filter(suite => suite.tests.length > 0)
238
+
239
+ if (!foundTest) {
240
+ // If testUid doesn't match, maybe it's a simple test name - try fallback
241
+ mocha.suite.suites = []
242
+ mocha.suite.tests = []
243
+ mocha.loadFiles()
244
+
245
+ // Try matching by title
246
+ for (const suite of mocha.suite.suites) {
247
+ const originalTests = [...suite.tests]
248
+ suite.tests = []
249
+
250
+ for (const test of originalTests) {
251
+ if (test.title === testUid || test.fullTitle() === testUid || test.uid === testUid) {
252
+ suite.tests.push(test)
253
+ foundTest = true
254
+ break
255
+ }
256
+ }
202
257
  }
203
258
 
204
- return {
205
- opts: step.opts || {},
206
- workerIndex,
207
- title: step.name,
208
- status: step.status,
209
- duration: step.duration || 0,
210
- err,
211
- parent,
212
- test: simplifyTest(step.test),
213
- };
259
+ // Clean up empty suites again
260
+ mocha.suite.suites = mocha.suite.suites.filter(suite => suite.tests.length > 0)
261
+ }
262
+ }
263
+
264
+ function filterTests() {
265
+ const files = codecept.testFiles
266
+ mocha.files = files
267
+ mocha.loadFiles()
268
+
269
+ for (const suite of mocha.suite.suites) {
270
+ suite.tests = suite.tests.filter(test => tests.indexOf(test.uid) >= 0)
214
271
  }
272
+ }
215
273
 
216
- collectStats();
274
+ function initializeListeners() {
217
275
  // 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) }));
276
+ event.dispatcher.on(event.suite.before, suite => sendToParentThread({ event: event.suite.before, workerIndex, data: suite.simplify() }))
277
+ event.dispatcher.on(event.suite.after, suite => sendToParentThread({ event: event.suite.after, workerIndex, data: suite.simplify() }))
220
278
 
221
279
  // calculate duration
222
- event.dispatcher.on(event.test.started, test => test.start = new Date());
280
+ event.dispatcher.on(event.test.started, test => (test.start = new Date()))
223
281
 
224
282
  // tests
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) }));
283
+ event.dispatcher.on(event.test.before, test => sendToParentThread({ event: event.test.before, workerIndex, data: test.simplify() }))
284
+ event.dispatcher.on(event.test.after, test => sendToParentThread({ event: event.test.after, workerIndex, data: test.simplify() }))
227
285
  // we should force-send correct errors to prevent race condition
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) }));
286
+ event.dispatcher.on(event.test.finished, (test, err) => sendToParentThread({ event: event.test.finished, workerIndex, data: { ...test.simplify(), err } }))
287
+ event.dispatcher.on(event.test.failed, (test, err) => sendToParentThread({ event: event.test.failed, workerIndex, data: { ...test.simplify(), err } }))
288
+ event.dispatcher.on(event.test.passed, (test, err) => sendToParentThread({ event: event.test.passed, workerIndex, data: { ...test.simplify(), err } }))
289
+ event.dispatcher.on(event.test.started, test => sendToParentThread({ event: event.test.started, workerIndex, data: test.simplify() }))
290
+ event.dispatcher.on(event.test.skipped, test => sendToParentThread({ event: event.test.skipped, workerIndex, data: test.simplify() }))
233
291
 
234
292
  // steps
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) }));
239
-
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 }));
243
-
244
- // all
245
- event.dispatcher.once(event.all.result, () => parentPort.close());
293
+ event.dispatcher.on(event.step.finished, step => sendToParentThread({ event: event.step.finished, workerIndex, data: step.simplify() }))
294
+ event.dispatcher.on(event.step.started, step => sendToParentThread({ event: event.step.started, workerIndex, data: step.simplify() }))
295
+ event.dispatcher.on(event.step.passed, step => sendToParentThread({ event: event.step.passed, workerIndex, data: step.simplify() }))
296
+ event.dispatcher.on(event.step.failed, step => sendToParentThread({ event: event.step.failed, workerIndex, data: step.simplify() }))
297
+
298
+ event.dispatcher.on(event.hook.failed, (hook, err) => sendToParentThread({ event: event.hook.failed, workerIndex, data: { ...hook.simplify(), err } }))
299
+ event.dispatcher.on(event.hook.passed, hook => sendToParentThread({ event: event.hook.passed, workerIndex, data: hook.simplify() }))
300
+ event.dispatcher.on(event.hook.finished, hook => sendToParentThread({ event: event.hook.finished, workerIndex, data: hook.simplify() }))
301
+
302
+ if (!poolMode) {
303
+ // In regular mode, close worker after all tests are complete
304
+ event.dispatcher.once(event.all.after, () => {
305
+ sendToParentThread({ event: event.all.after, workerIndex, data: container.result().simplify() })
306
+ })
307
+ // all
308
+ event.dispatcher.once(event.all.result, () => {
309
+ sendToParentThread({ event: event.all.result, workerIndex, data: container.result().simplify() })
310
+ parentPort?.close()
311
+ })
312
+ } else {
313
+ // In pool mode, don't send result events for individual tests
314
+ // Results will be sent once when the worker completes all tests
315
+ }
246
316
  }
247
317
 
248
318
  function disablePause() {
249
- global.pause = () => {};
250
- }
251
-
252
- function collectStats() {
253
- const stats = {
254
- passes: 0,
255
- failures: 0,
256
- skipped: 0,
257
- tests: 0,
258
- pending: 0,
259
- };
260
- event.dispatcher.on(event.test.skipped, () => {
261
- stats.skipped++;
262
- });
263
- event.dispatcher.on(event.test.passed, () => {
264
- stats.passes++;
265
- });
266
- event.dispatcher.on(event.test.failed, (test) => {
267
- if (test.ctx._runnable.title.includes('hook: AfterSuite')) {
268
- stats.failedHooks += 1;
269
- }
270
- stats.failures++;
271
- });
272
- event.dispatcher.on(event.test.skipped, () => {
273
- stats.pending++;
274
- });
275
- event.dispatcher.on(event.test.finished, () => {
276
- stats.tests++;
277
- });
278
- event.dispatcher.once(event.all.after, () => {
279
- sendToParentThread({ event: event.all.after, data: stats });
280
- });
319
+ global.pause = () => {}
281
320
  }
282
321
 
283
322
  function sendToParentThread(data) {
284
- parentPort.postMessage(data);
323
+ parentPort?.postMessage(data)
285
324
  }
286
325
 
287
326
  function listenToParentThread() {
288
- parentPort.on('message', (eventData) => {
289
- container.append({ support: eventData.data });
290
- });
327
+ if (!poolMode) {
328
+ parentPort?.on('message', eventData => {
329
+ container.append({ support: eventData.data })
330
+ })
331
+ }
332
+ // In pool mode, message handling is done in runPoolTests()
291
333
  }