codeceptjs 4.0.1-beta.9 → 4.0.1

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 (328) hide show
  1. package/README.md +39 -28
  2. package/bin/codecept.js +17 -4
  3. package/bin/codeceptq.js +49 -0
  4. package/bin/mcp-server.js +1189 -0
  5. package/docs/advanced.md +201 -0
  6. package/docs/agents.md +181 -0
  7. package/docs/ai.md +489 -0
  8. package/docs/aitrace.md +266 -0
  9. package/docs/api.md +332 -0
  10. package/docs/architecture.md +235 -0
  11. package/docs/assertions.md +415 -0
  12. package/docs/auth.md +318 -0
  13. package/docs/basics.md +424 -0
  14. package/docs/bdd.md +539 -0
  15. package/docs/best.md +240 -0
  16. package/docs/bootstrap.md +132 -0
  17. package/docs/commands.md +352 -0
  18. package/docs/community-helpers.md +63 -0
  19. package/docs/configuration.md +185 -0
  20. package/docs/continuous-integration.md +431 -0
  21. package/docs/custom-helpers.md +297 -0
  22. package/docs/data.md +448 -0
  23. package/docs/debugging.md +332 -0
  24. package/docs/detox.md +235 -0
  25. package/docs/docker.md +107 -0
  26. package/docs/effects.md +179 -0
  27. package/docs/element-based-testing.md +295 -0
  28. package/docs/element-selection.md +125 -0
  29. package/docs/els.md +328 -0
  30. package/docs/environment-variables.md +131 -0
  31. package/docs/examples.md +160 -0
  32. package/docs/heal.md +213 -0
  33. package/docs/helpers/ApiDataFactory.md +267 -0
  34. package/docs/helpers/Appium.md +1419 -0
  35. package/docs/helpers/Detox.md +665 -0
  36. package/docs/helpers/ExpectHelper.md +275 -0
  37. package/docs/helpers/FileSystem.md +152 -0
  38. package/docs/helpers/GraphQL.md +152 -0
  39. package/docs/helpers/GraphQLDataFactory.md +226 -0
  40. package/docs/helpers/JSONResponse.md +255 -0
  41. package/docs/helpers/MockRequest.md +377 -0
  42. package/docs/helpers/Playwright.md +2970 -0
  43. package/docs/helpers/Puppeteer-firefox.md +86 -0
  44. package/docs/helpers/Puppeteer.md +2583 -0
  45. package/docs/helpers/REST.md +289 -0
  46. package/docs/helpers/WebDriver.md +2639 -0
  47. package/docs/hooks.md +148 -0
  48. package/docs/index.md +111 -0
  49. package/docs/installation.md +121 -0
  50. package/docs/internal-test-server.md +89 -0
  51. package/docs/locators.md +355 -0
  52. package/docs/mcp.md +485 -0
  53. package/docs/migrate-from-cypress.md +98 -0
  54. package/docs/migrate-from-java.md +108 -0
  55. package/docs/migrate-from-protractor.md +101 -0
  56. package/docs/migrate-from-testcafe.md +99 -0
  57. package/docs/migration-4.md +745 -0
  58. package/docs/mobile.md +338 -0
  59. package/docs/pageobjects.md +399 -0
  60. package/docs/parallel.md +187 -0
  61. package/docs/playwright.md +714 -0
  62. package/docs/plugins/aiTrace.md +49 -0
  63. package/docs/plugins/analyze.md +66 -0
  64. package/docs/plugins/auth.md +241 -0
  65. package/docs/plugins/autoDelay.md +48 -0
  66. package/docs/plugins/browser.md +41 -0
  67. package/docs/plugins/coverage.md +39 -0
  68. package/docs/plugins/customLocator.md +119 -0
  69. package/docs/plugins/customReporter.md +16 -0
  70. package/docs/plugins/expose.md +75 -0
  71. package/docs/plugins/heal.md +44 -0
  72. package/docs/plugins/junitReporter.md +51 -0
  73. package/docs/plugins/pageInfo.md +34 -0
  74. package/docs/plugins/pause.md +43 -0
  75. package/docs/plugins/pauseOnFail.md +18 -0
  76. package/docs/plugins/retryFailedStep.md +75 -0
  77. package/docs/plugins/screencast.md +55 -0
  78. package/docs/plugins/screenshot.md +58 -0
  79. package/docs/plugins/screenshotOnFail.md +18 -0
  80. package/docs/plugins/stepTimeout.md +65 -0
  81. package/docs/plugins.md +87 -0
  82. package/docs/puppeteer.md +314 -0
  83. package/docs/quickstart.md +120 -0
  84. package/docs/reports.md +195 -0
  85. package/docs/retry.md +311 -0
  86. package/docs/secrets.md +150 -0
  87. package/docs/sessions.md +80 -0
  88. package/docs/shadow.md +68 -0
  89. package/docs/store.md +94 -0
  90. package/docs/test-structure.md +275 -0
  91. package/docs/timeouts.md +183 -0
  92. package/docs/translation.md +247 -0
  93. package/docs/tutorial.md +323 -0
  94. package/docs/typescript.md +159 -0
  95. package/docs/web-element.md +251 -0
  96. package/docs/webdriver.md +641 -0
  97. package/docs/within.md +55 -0
  98. package/lib/actor.js +1 -36
  99. package/lib/ai.js +3 -2
  100. package/lib/aria.js +260 -0
  101. package/lib/assertions.js +18 -0
  102. package/lib/codecept.js +34 -25
  103. package/lib/command/check.js +2 -1
  104. package/lib/command/definitions.js +14 -10
  105. package/lib/command/dryRun.js +24 -5
  106. package/lib/command/generate.js +3 -1
  107. package/lib/command/gherkin/snippets.js +5 -4
  108. package/lib/command/init.js +249 -270
  109. package/lib/command/list.js +150 -10
  110. package/lib/command/query.js +218 -0
  111. package/lib/command/run-multiple.js +3 -2
  112. package/lib/command/run-workers.js +14 -16
  113. package/lib/command/run.js +3 -17
  114. package/lib/command/utils.js +14 -0
  115. package/lib/command/workers/runTests.js +117 -9
  116. package/lib/config.js +98 -19
  117. package/lib/container.js +188 -19
  118. package/lib/effects.js +17 -0
  119. package/lib/element/WebElement.js +246 -2
  120. package/lib/els.js +12 -6
  121. package/lib/globals.js +32 -19
  122. package/lib/heal.js +7 -4
  123. package/lib/helper/ApiDataFactory.js +2 -1
  124. package/lib/helper/Appium.js +8 -8
  125. package/lib/helper/FileSystem.js +3 -2
  126. package/lib/helper/GraphQLDataFactory.js +2 -1
  127. package/lib/helper/Playwright.js +367 -516
  128. package/lib/helper/Puppeteer.js +343 -197
  129. package/lib/helper/WebDriver.js +324 -111
  130. package/lib/helper/errors/ElementNotFound.js +5 -2
  131. package/lib/helper/errors/MultipleElementsFound.js +52 -0
  132. package/lib/helper/errors/NonFocusedType.js +8 -0
  133. package/lib/helper/extras/Download.js +45 -0
  134. package/lib/helper/extras/PlaywrightLocator.js +7 -107
  135. package/lib/helper/extras/elementSelection.js +58 -0
  136. package/lib/helper/extras/focusCheck.js +43 -0
  137. package/lib/helper/extras/richTextEditor.js +178 -0
  138. package/lib/helper/scripts/dropFile.js +11 -0
  139. package/lib/history.js +3 -2
  140. package/lib/html.js +103 -16
  141. package/lib/index.js +9 -1
  142. package/lib/listener/config.js +6 -4
  143. package/lib/listener/emptyRun.js +2 -1
  144. package/lib/listener/globalRetry.js +32 -6
  145. package/lib/listener/helpers.js +6 -15
  146. package/lib/listener/mocha.js +2 -1
  147. package/lib/listener/pageobjects.js +43 -0
  148. package/lib/listener/result.js +3 -2
  149. package/lib/locator.js +158 -16
  150. package/lib/mocha/cli.js +19 -1
  151. package/lib/mocha/factory.js +13 -28
  152. package/lib/mocha/inject.js +1 -1
  153. package/lib/mocha/scenarioConfig.js +2 -1
  154. package/lib/mocha/test.js +4 -2
  155. package/lib/mocha/ui.js +5 -6
  156. package/lib/output.js +2 -2
  157. package/lib/parser.js +2 -2
  158. package/lib/pause.js +38 -4
  159. package/lib/plugin/aiTrace.js +457 -0
  160. package/lib/plugin/analyze.js +9 -9
  161. package/lib/plugin/auth.js +5 -4
  162. package/lib/plugin/browser.js +77 -0
  163. package/lib/plugin/expose.js +159 -0
  164. package/lib/plugin/heal.js +47 -3
  165. package/lib/plugin/junitReporter.js +303 -0
  166. package/lib/plugin/pageInfo.js +54 -52
  167. package/lib/plugin/pause.js +131 -0
  168. package/lib/plugin/pauseOnFail.js +11 -33
  169. package/lib/plugin/retryFailedStep.js +43 -32
  170. package/lib/plugin/screencast.js +289 -0
  171. package/lib/plugin/screenshot.js +558 -0
  172. package/lib/plugin/screenshotOnFail.js +9 -170
  173. package/lib/plugin/stepTimeout.js +3 -2
  174. package/lib/recorder.js +1 -1
  175. package/lib/rerun.js +2 -1
  176. package/lib/result.js +2 -1
  177. package/lib/step/base.js +23 -9
  178. package/lib/step/comment.js +2 -2
  179. package/lib/step/config.js +15 -2
  180. package/lib/step/helper.js +4 -4
  181. package/lib/step/meta.js +3 -3
  182. package/lib/step/record.js +12 -4
  183. package/lib/store.js +72 -3
  184. package/lib/translation.js +2 -1
  185. package/lib/utils/loaderCheck.js +41 -3
  186. package/lib/utils/mask_data.js +2 -1
  187. package/lib/utils/pluginParser.js +151 -0
  188. package/lib/utils/trace.js +297 -0
  189. package/lib/utils/typescript.js +261 -49
  190. package/lib/utils.js +77 -3
  191. package/lib/workers.js +123 -17
  192. package/package.json +48 -43
  193. package/typings/index.d.ts +120 -9
  194. package/typings/promiseBasedTypes.d.ts +3243 -6057
  195. package/typings/types.d.ts +3541 -6506
  196. package/docs/webapi/amOnPage.mustache +0 -11
  197. package/docs/webapi/appendField.mustache +0 -11
  198. package/docs/webapi/attachFile.mustache +0 -12
  199. package/docs/webapi/blur.mustache +0 -18
  200. package/docs/webapi/checkOption.mustache +0 -13
  201. package/docs/webapi/clearCookie.mustache +0 -9
  202. package/docs/webapi/clearField.mustache +0 -9
  203. package/docs/webapi/click.mustache +0 -29
  204. package/docs/webapi/clickLink.mustache +0 -8
  205. package/docs/webapi/closeCurrentTab.mustache +0 -7
  206. package/docs/webapi/closeOtherTabs.mustache +0 -8
  207. package/docs/webapi/dontSee.mustache +0 -11
  208. package/docs/webapi/dontSeeCheckboxIsChecked.mustache +0 -10
  209. package/docs/webapi/dontSeeCookie.mustache +0 -8
  210. package/docs/webapi/dontSeeCurrentUrlEquals.mustache +0 -10
  211. package/docs/webapi/dontSeeElement.mustache +0 -8
  212. package/docs/webapi/dontSeeElementInDOM.mustache +0 -8
  213. package/docs/webapi/dontSeeInCurrentUrl.mustache +0 -4
  214. package/docs/webapi/dontSeeInField.mustache +0 -11
  215. package/docs/webapi/dontSeeInSource.mustache +0 -8
  216. package/docs/webapi/dontSeeInTitle.mustache +0 -8
  217. package/docs/webapi/dontSeeTraffic.mustache +0 -13
  218. package/docs/webapi/doubleClick.mustache +0 -13
  219. package/docs/webapi/downloadFile.mustache +0 -12
  220. package/docs/webapi/dragAndDrop.mustache +0 -9
  221. package/docs/webapi/dragSlider.mustache +0 -11
  222. package/docs/webapi/executeAsyncScript.mustache +0 -24
  223. package/docs/webapi/executeScript.mustache +0 -26
  224. package/docs/webapi/fillField.mustache +0 -16
  225. package/docs/webapi/flushNetworkTraffics.mustache +0 -5
  226. package/docs/webapi/focus.mustache +0 -13
  227. package/docs/webapi/forceClick.mustache +0 -28
  228. package/docs/webapi/forceRightClick.mustache +0 -18
  229. package/docs/webapi/grabAllWindowHandles.mustache +0 -7
  230. package/docs/webapi/grabAttributeFrom.mustache +0 -10
  231. package/docs/webapi/grabAttributeFromAll.mustache +0 -9
  232. package/docs/webapi/grabBrowserLogs.mustache +0 -9
  233. package/docs/webapi/grabCookie.mustache +0 -11
  234. package/docs/webapi/grabCssPropertyFrom.mustache +0 -11
  235. package/docs/webapi/grabCssPropertyFromAll.mustache +0 -10
  236. package/docs/webapi/grabCurrentUrl.mustache +0 -9
  237. package/docs/webapi/grabCurrentWindowHandle.mustache +0 -6
  238. package/docs/webapi/grabDataFromPerformanceTiming.mustache +0 -20
  239. package/docs/webapi/grabElementBoundingRect.mustache +0 -20
  240. package/docs/webapi/grabGeoLocation.mustache +0 -8
  241. package/docs/webapi/grabHTMLFrom.mustache +0 -10
  242. package/docs/webapi/grabHTMLFromAll.mustache +0 -9
  243. package/docs/webapi/grabNumberOfOpenTabs.mustache +0 -8
  244. package/docs/webapi/grabNumberOfVisibleElements.mustache +0 -9
  245. package/docs/webapi/grabPageScrollPosition.mustache +0 -8
  246. package/docs/webapi/grabPopupText.mustache +0 -5
  247. package/docs/webapi/grabRecordedNetworkTraffics.mustache +0 -10
  248. package/docs/webapi/grabSource.mustache +0 -8
  249. package/docs/webapi/grabTextFrom.mustache +0 -10
  250. package/docs/webapi/grabTextFromAll.mustache +0 -9
  251. package/docs/webapi/grabTitle.mustache +0 -8
  252. package/docs/webapi/grabValueFrom.mustache +0 -9
  253. package/docs/webapi/grabValueFromAll.mustache +0 -8
  254. package/docs/webapi/grabWebElement.mustache +0 -9
  255. package/docs/webapi/grabWebElements.mustache +0 -9
  256. package/docs/webapi/moveCursorTo.mustache +0 -12
  257. package/docs/webapi/openNewTab.mustache +0 -7
  258. package/docs/webapi/pressKey.mustache +0 -12
  259. package/docs/webapi/pressKeyDown.mustache +0 -12
  260. package/docs/webapi/pressKeyUp.mustache +0 -12
  261. package/docs/webapi/pressKeyWithKeyNormalization.mustache +0 -60
  262. package/docs/webapi/refreshPage.mustache +0 -6
  263. package/docs/webapi/resizeWindow.mustache +0 -6
  264. package/docs/webapi/rightClick.mustache +0 -14
  265. package/docs/webapi/saveElementScreenshot.mustache +0 -10
  266. package/docs/webapi/saveScreenshot.mustache +0 -12
  267. package/docs/webapi/say.mustache +0 -10
  268. package/docs/webapi/scrollIntoView.mustache +0 -11
  269. package/docs/webapi/scrollPageToBottom.mustache +0 -6
  270. package/docs/webapi/scrollPageToTop.mustache +0 -6
  271. package/docs/webapi/scrollTo.mustache +0 -12
  272. package/docs/webapi/see.mustache +0 -11
  273. package/docs/webapi/seeAttributesOnElements.mustache +0 -9
  274. package/docs/webapi/seeCheckboxIsChecked.mustache +0 -10
  275. package/docs/webapi/seeCookie.mustache +0 -8
  276. package/docs/webapi/seeCssPropertiesOnElements.mustache +0 -9
  277. package/docs/webapi/seeCurrentUrlEquals.mustache +0 -11
  278. package/docs/webapi/seeElement.mustache +0 -8
  279. package/docs/webapi/seeElementInDOM.mustache +0 -8
  280. package/docs/webapi/seeInCurrentUrl.mustache +0 -8
  281. package/docs/webapi/seeInField.mustache +0 -12
  282. package/docs/webapi/seeInPopup.mustache +0 -8
  283. package/docs/webapi/seeInSource.mustache +0 -7
  284. package/docs/webapi/seeInTitle.mustache +0 -8
  285. package/docs/webapi/seeNumberOfElements.mustache +0 -11
  286. package/docs/webapi/seeNumberOfVisibleElements.mustache +0 -10
  287. package/docs/webapi/seeTextEquals.mustache +0 -9
  288. package/docs/webapi/seeTitleEquals.mustache +0 -8
  289. package/docs/webapi/seeTraffic.mustache +0 -36
  290. package/docs/webapi/selectOption.mustache +0 -21
  291. package/docs/webapi/setCookie.mustache +0 -16
  292. package/docs/webapi/setGeoLocation.mustache +0 -12
  293. package/docs/webapi/startRecordingTraffic.mustache +0 -8
  294. package/docs/webapi/startRecordingWebSocketMessages.mustache +0 -8
  295. package/docs/webapi/stopRecordingTraffic.mustache +0 -5
  296. package/docs/webapi/stopRecordingWebSocketMessages.mustache +0 -7
  297. package/docs/webapi/switchTo.mustache +0 -9
  298. package/docs/webapi/switchToNextTab.mustache +0 -10
  299. package/docs/webapi/switchToPreviousTab.mustache +0 -10
  300. package/docs/webapi/type.mustache +0 -21
  301. package/docs/webapi/uncheckOption.mustache +0 -13
  302. package/docs/webapi/wait.mustache +0 -8
  303. package/docs/webapi/waitForClickable.mustache +0 -11
  304. package/docs/webapi/waitForCookie.mustache +0 -9
  305. package/docs/webapi/waitForDetached.mustache +0 -10
  306. package/docs/webapi/waitForDisabled.mustache +0 -6
  307. package/docs/webapi/waitForElement.mustache +0 -11
  308. package/docs/webapi/waitForEnabled.mustache +0 -6
  309. package/docs/webapi/waitForFunction.mustache +0 -17
  310. package/docs/webapi/waitForInvisible.mustache +0 -10
  311. package/docs/webapi/waitForNumberOfTabs.mustache +0 -9
  312. package/docs/webapi/waitForText.mustache +0 -13
  313. package/docs/webapi/waitForValue.mustache +0 -10
  314. package/docs/webapi/waitForVisible.mustache +0 -10
  315. package/docs/webapi/waitInUrl.mustache +0 -9
  316. package/docs/webapi/waitNumberOfVisibleElements.mustache +0 -10
  317. package/docs/webapi/waitToHide.mustache +0 -10
  318. package/docs/webapi/waitUrlEquals.mustache +0 -10
  319. package/lib/helper/AI.js +0 -214
  320. package/lib/helper/Mochawesome.js +0 -96
  321. package/lib/helper/extras/PlaywrightReactVueLocator.js +0 -52
  322. package/lib/helper/extras/React.js +0 -65
  323. package/lib/listener/enhancedGlobalRetry.js +0 -110
  324. package/lib/plugin/enhancedRetryFailedStep.js +0 -99
  325. package/lib/plugin/htmlReporter.js +0 -3648
  326. package/lib/plugin/stepByStepReport.js +0 -427
  327. package/lib/plugin/subtitles.js +0 -89
  328. package/lib/retryCoordinator.js +0 -207
package/lib/workers.js CHANGED
@@ -20,6 +20,7 @@ import event from './event.js'
20
20
  import { deserializeTest } from './mocha/test.js'
21
21
  import { deserializeSuite } from './mocha/suite.js'
22
22
  import recorder from './recorder.js'
23
+ import store from './store.js'
23
24
  import runHook from './hooks.js'
24
25
  import WorkerStorage from './workerStorage.js'
25
26
  import { createRuns } from './command/run-multiple/collection.js'
@@ -28,7 +29,7 @@ const pathToWorker = path.join(__dirname, 'command', 'workers', 'runTests.js')
28
29
 
29
30
  const initializeCodecept = async (configPath, options = {}) => {
30
31
  const config = await mainConfig.load(configPath || '.')
31
- const codecept = new Codecept(config, options)
32
+ const codecept = new Codecept(config, { ...options, skipDefaultListeners: true })
32
33
  await codecept.init(getTestRoot(configPath))
33
34
  codecept.loadTests()
34
35
 
@@ -117,11 +118,9 @@ const createWorkerObjects = (testGroups, config, testRoot, options, selectedRuns
117
118
  const workersToExecute = []
118
119
 
119
120
  const currentOutputFolder = config.output
120
- let currentMochawesomeReportDir
121
121
  let currentMochaJunitReporterFile
122
122
 
123
123
  if (config.mocha && config.mocha.reporterOptions) {
124
- currentMochawesomeReportDir = config.mocha.reporterOptions?.mochawesome.options.reportDir
125
124
  currentMochaJunitReporterFile = config.mocha.reporterOptions['mocha-junit-reporter'].options.mochaFile
126
125
  }
127
126
 
@@ -131,8 +130,6 @@ const createWorkerObjects = (testGroups, config, testRoot, options, selectedRuns
131
130
  let workerName = worker.name.replace(':', '_')
132
131
  _config.output = `${currentOutputFolder}${separator}${workerName}`
133
132
  if (config.mocha && config.mocha.reporterOptions) {
134
- _config.mocha.reporterOptions.mochawesome.options.reportDir = `${currentMochawesomeReportDir}${separator}${workerName}`
135
-
136
133
  const _tempArray = currentMochaJunitReporterFile.split(separator)
137
134
  _tempArray.splice(
138
135
  _tempArray.findIndex(item => item.includes('.xml')),
@@ -224,7 +221,8 @@ class WorkerObject {
224
221
  const oldConfig = JSON.parse(this.options.override || '{}')
225
222
 
226
223
  // Remove customLocatorStrategies from both old and new config before JSON serialization
227
- // since functions cannot be serialized and will be lost, causing workers to have empty strategies
224
+ // since functions cannot be serialized and will be lost, causing workers to have empty strategies.
225
+ // Note: Only WebDriver helper supports customLocatorStrategies
228
226
  const configWithoutFunctions = { ...config }
229
227
 
230
228
  // Clean both old and new config
@@ -370,6 +368,9 @@ class Workers extends EventEmitter {
370
368
  // If Codecept isn't initialized yet, return empty groups as a safe fallback
371
369
  if (!this.codecept) return populateGroups(numberOfWorkers)
372
370
  const files = this.codecept.testFiles
371
+
372
+ // Create a fresh mocha instance to avoid state pollution
373
+ Container.createMocha(this.codecept.config.mocha || {}, this.options)
373
374
  const mocha = Container.mocha()
374
375
  mocha.files = files
375
376
  mocha.loadFiles()
@@ -384,6 +385,10 @@ class Workers extends EventEmitter {
384
385
  groupCounter++
385
386
  }
386
387
  })
388
+
389
+ // Clean up after collecting test UIDs
390
+ mocha.unloadFiles()
391
+
387
392
  return groups
388
393
  }
389
394
 
@@ -452,9 +457,12 @@ class Workers extends EventEmitter {
452
457
  const files = this.codecept.testFiles
453
458
  const groups = populateGroups(numberOfWorkers)
454
459
 
460
+ // Create a fresh mocha instance to avoid state pollution
461
+ Container.createMocha(this.codecept.config.mocha || {}, this.options)
455
462
  const mocha = Container.mocha()
456
463
  mocha.files = files
457
464
  mocha.loadFiles()
465
+
458
466
  mocha.suite.suites.forEach(suite => {
459
467
  const i = indexOfSmallestElement(groups)
460
468
  suite.tests.forEach(test => {
@@ -463,6 +471,10 @@ class Workers extends EventEmitter {
463
471
  }
464
472
  })
465
473
  })
474
+
475
+ // Clean up after collecting test UIDs
476
+ mocha.unloadFiles()
477
+
466
478
  return groups
467
479
  }
468
480
 
@@ -489,6 +501,7 @@ class Workers extends EventEmitter {
489
501
  await this._ensureInitialized()
490
502
  recorder.startUnlessRunning()
491
503
  event.dispatcher.emit(event.workers.before)
504
+ store.workerMode = true
492
505
  process.env.RUNS_WITH_WORKERS = 'true'
493
506
 
494
507
  // Create workers and set up message handlers immediately (not in recorder queue)
@@ -505,7 +518,9 @@ class Workers extends EventEmitter {
505
518
  })
506
519
 
507
520
  return new Promise(resolve => {
508
- this.on('end', resolve)
521
+ this.on('end', () => {
522
+ resolve()
523
+ })
509
524
  })
510
525
  }
511
526
 
@@ -529,7 +544,32 @@ class Workers extends EventEmitter {
529
544
  this.activeWorkers.set(worker, { available: true, workerIndex: null })
530
545
  }
531
546
 
547
+ // Track last activity time to detect hanging workers
548
+ let lastActivity = Date.now()
549
+ let currentTest = null
550
+ let autoTerminated = false
551
+ const workerTimeout = process.env.CODECEPT_WORKER_TIMEOUT ? ms(process.env.CODECEPT_WORKER_TIMEOUT) : ms('5m')
552
+
553
+ const timeoutChecker = setInterval(() => {
554
+ const elapsed = Date.now() - lastActivity
555
+ if (elapsed > workerTimeout) {
556
+ console.error(`[Main] Worker appears to be hanging (no activity for ${Math.floor(elapsed/1000)}s). Terminating...`)
557
+ if (currentTest) {
558
+ console.error(`[Main] Last test: ${currentTest}`)
559
+ }
560
+ clearInterval(timeoutChecker)
561
+ worker.terminate()
562
+ }
563
+ }, 30000) // Check every 30 seconds
564
+
532
565
  worker.on('message', message => {
566
+ lastActivity = Date.now() // Update activity timestamp
567
+
568
+ // Track current test
569
+ if (message.event === event.test.started && message.data) {
570
+ currentTest = message.data.title || message.data.fullTitle
571
+ }
572
+
533
573
  output.process(message.workerIndex)
534
574
 
535
575
  // Handle test requests for pool mode
@@ -568,15 +608,41 @@ class Workers extends EventEmitter {
568
608
  })
569
609
  }
570
610
 
611
+ const exitTimeout = parseInt(process.env.CODECEPT_AUTO_EXIT_TIMEOUT, 10)
612
+ if (exitTimeout === 0) break
613
+ setTimeout(() => {
614
+ autoTerminated = true
615
+ worker.terminate()
616
+ }, exitTimeout || 2000)
617
+
571
618
  break
572
619
  case event.suite.before:
573
- this.emit(event.suite.before, deserializeSuite(message.data))
620
+ {
621
+ const suite = deserializeSuite(message.data)
622
+ this.emit(event.suite.before, suite)
623
+ event.dispatcher.emit(event.suite.before, suite)
624
+ }
625
+ break
626
+ case event.suite.after:
627
+ {
628
+ const suite = deserializeSuite(message.data)
629
+ this.emit(event.suite.after, suite)
630
+ event.dispatcher.emit(event.suite.after, suite)
631
+ }
574
632
  break
575
633
  case event.test.before:
576
- this.emit(event.test.before, deserializeTest(message.data))
634
+ {
635
+ const test = deserializeTest(message.data)
636
+ this.emit(event.test.before, test)
637
+ event.dispatcher.emit(event.test.before, test)
638
+ }
577
639
  break
578
640
  case event.test.started:
579
- this.emit(event.test.started, deserializeTest(message.data))
641
+ {
642
+ const test = deserializeTest(message.data)
643
+ this.emit(event.test.started, test)
644
+ event.dispatcher.emit(event.test.started, test)
645
+ }
580
646
  break
581
647
  case event.test.failed:
582
648
  // For hook failures, emit immediately as there won't be a test.finished event
@@ -590,7 +656,11 @@ class Workers extends EventEmitter {
590
656
  // Skip individual passed events - we'll emit based on finished state
591
657
  break
592
658
  case event.test.skipped:
593
- this.emit(event.test.skipped, deserializeTest(message.data))
659
+ {
660
+ const test = deserializeTest(message.data)
661
+ this.emit(event.test.skipped, test)
662
+ event.dispatcher.emit(event.test.skipped, test)
663
+ }
594
664
  break
595
665
  case event.test.finished:
596
666
  // Handle different types of test completion properly
@@ -604,7 +674,7 @@ class Workers extends EventEmitter {
604
674
  if (!this._testStates) this._testStates = new Map()
605
675
 
606
676
  if (!this._testStates.has(uid)) {
607
- this._testStates.set(uid, { states: [], lastData: data })
677
+ this._testStates.set(uid, { states: [], lastData: data, workerIndex: message.workerIndex })
608
678
  }
609
679
 
610
680
  const testState = this._testStates.get(uid)
@@ -619,39 +689,72 @@ class Workers extends EventEmitter {
619
689
  }
620
690
  }
621
691
 
622
- this.emit(event.test.finished, deserializeTest(data))
692
+ const test = deserializeTest(data)
693
+ this.emit(event.test.finished, test)
694
+ event.dispatcher.emit(event.test.finished, test)
623
695
  }
624
696
  break
625
697
  case event.test.after:
626
- this.emit(event.test.after, deserializeTest(message.data))
698
+ {
699
+ const test = deserializeTest(message.data)
700
+ this.emit(event.test.after, test)
701
+ event.dispatcher.emit(event.test.after, test)
702
+ }
627
703
  break
628
704
  case event.step.finished:
629
705
  this.emit(event.step.finished, message.data)
706
+ event.dispatcher.emit(event.step.finished, message.data)
630
707
  break
631
708
  case event.step.started:
632
709
  this.emit(event.step.started, message.data)
710
+ event.dispatcher.emit(event.step.started, message.data)
633
711
  break
634
712
  case event.step.passed:
635
713
  this.emit(event.step.passed, message.data)
714
+ event.dispatcher.emit(event.step.passed, message.data)
636
715
  break
637
716
  case event.step.failed:
638
717
  this.emit(event.step.failed, message.data, message.data.error)
718
+ event.dispatcher.emit(event.step.failed, message.data, message.data.error)
639
719
  break
640
720
  case event.hook.failed:
641
721
  // Hook failures are already reported as test failures by the worker
642
722
  // Just emit the hook.failed event for listeners
643
723
  this.emit(event.hook.failed, message.data)
724
+ event.dispatcher.emit(event.hook.failed, message.data)
725
+ break
726
+ case event.hook.passed:
727
+ this.emit(event.hook.passed, message.data)
728
+ event.dispatcher.emit(event.hook.passed, message.data)
729
+ break
730
+ case event.hook.finished:
731
+ this.emit(event.hook.finished, message.data)
732
+ event.dispatcher.emit(event.hook.finished, message.data)
644
733
  break
645
734
  }
646
735
  })
647
736
 
648
737
  worker.on('error', err => {
738
+ console.error(`[Main] Worker error:`, err.message || err)
739
+ if (currentTest) {
740
+ console.error(`[Main] Failed during test: ${currentTest}`)
741
+ }
649
742
  this.errors.push(err)
650
743
  })
651
744
 
652
- worker.on('exit', () => {
745
+ worker.on('exit', (code) => {
746
+ clearInterval(timeoutChecker)
653
747
  this.closedWorkers += 1
654
748
 
749
+ if (code !== 0 && !autoTerminated) {
750
+ console.error(`[Main] Worker exited with code ${code}`)
751
+ if (currentTest) {
752
+ console.error(`[Main] Last test running: ${currentTest}`)
753
+ }
754
+ // Mark as failed
755
+ process.exitCode = 1
756
+ }
757
+
655
758
  if (this.isPoolMode) {
656
759
  // Pool mode: finish when all workers have exited and no more tests
657
760
  if (this.closedWorkers === this.numberOfWorkers) {
@@ -666,7 +769,7 @@ class Workers extends EventEmitter {
666
769
 
667
770
  _finishRun() {
668
771
  event.dispatcher.emit(event.workers.after, { tests: this.workers.map(worker => worker.tests) })
669
- if (Container.result().hasFailed) {
772
+ if (Container.result().hasFailed || this.errors.length > 0) {
670
773
  process.exitCode = 1
671
774
  } else {
672
775
  process.exitCode = 0
@@ -674,7 +777,10 @@ class Workers extends EventEmitter {
674
777
 
675
778
  // Emit states for all tracked tests before emitting results
676
779
  if (this._testStates) {
677
- for (const [uid, { states, lastData }] of this._testStates) {
780
+ for (const [uid, { states, lastData, workerIndex }] of this._testStates) {
781
+ // Set correct worker index for output
782
+ output.process(workerIndex)
783
+
678
784
  // For tests with retries configured, emit all failures + final success
679
785
  // For tests without retries, emit only final state
680
786
  const lastState = states[states.length - 1]
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codeceptjs",
3
- "version": "4.0.1-beta.9",
3
+ "version": "4.0.1",
4
4
  "type": "module",
5
5
  "description": "Supercharged End 2 End Testing Framework for NodeJS",
6
6
  "keywords": [
@@ -26,7 +26,9 @@
26
26
  "lib",
27
27
  "translations",
28
28
  "typings/**/*.d.ts",
29
- "docs/webapi/**"
29
+ "docs/*.md",
30
+ "docs/helpers/**",
31
+ "docs/plugins/**"
30
32
  ],
31
33
  "main": "lib/index.js",
32
34
  "module": "lib/index.js",
@@ -41,12 +43,15 @@
41
43
  "./els": "./lib/els.js",
42
44
  "./effects": "./lib/effects.js",
43
45
  "./steps": "./lib/steps.js",
44
- "./store": "./lib/store.js"
46
+ "./store": "./lib/store.js",
47
+ "./assertions": "./lib/assertions.js"
45
48
  },
46
49
  "bin": {
47
- "codeceptjs": "./bin/codecept.js"
50
+ "codeceptjs": "./bin/codecept.js",
51
+ "codeceptjs-mcp": "./bin/mcp-server.js",
52
+ "codeceptq": "./bin/codeceptq.js"
48
53
  },
49
- "repository": "Codeception/codeceptjs",
54
+ "repository": "codeceptjs/CodeceptJS",
50
55
  "scripts": {
51
56
  "test-server": "node bin/test-server.js test/data/rest/db.json --host 0.0.0.0 -p 8010 --read-only",
52
57
  "test-server:writable": "node bin/test-server.js test/data/rest/db.json --host 0.0.0.0 -p 8010",
@@ -85,34 +90,34 @@
85
90
  "publish-beta": "./runok.cjs publish:next-beta-version"
86
91
  },
87
92
  "dependencies": {
88
- "@codeceptjs/configure": "1.0.6",
93
+ "@codeceptjs/configure": "^4.0.0-beta.4",
89
94
  "@codeceptjs/helper": "2.0.4",
90
95
  "@cucumber/cucumber-expressions": "18",
91
- "@cucumber/gherkin": "35.1.0",
92
- "@cucumber/messages": "29.0.1",
96
+ "@cucumber/gherkin": "38.0.0",
97
+ "@cucumber/messages": "32.0.1",
98
+ "@modelcontextprotocol/sdk": "^1.26.0",
93
99
  "@xmldom/xmldom": "0.9.8",
94
100
  "acorn": "8.15.0",
95
- "ai": "^5.0.60",
101
+ "ai": "^6.0.43",
96
102
  "arrify": "3.0.0",
97
- "axios": "1.12.2",
103
+ "axios": "1.13.2",
98
104
  "chalk": "4.1.2",
99
105
  "cheerio": "^1.0.0",
100
- "chokidar": "^4.0.3",
101
- "commander": "11.1.0",
106
+ "chokidar": "^5.0.0",
107
+ "commander": "14.0.3",
102
108
  "cross-spawn": "7.0.6",
103
109
  "css-to-xpath": "0.1.0",
104
110
  "csstoxpath": "1.6.0",
105
- "envinfo": "7.20.0",
111
+ "envinfo": "7.21.0",
106
112
  "escape-string-regexp": "4.0.0",
107
113
  "figures": "3.2.0",
108
114
  "fn-args": "4.0.0",
109
- "fs-extra": "11.3.2",
115
+ "fs-extra": "11.3.3",
110
116
  "fuse.js": "^7.0.0",
111
- "glob": ">=9.0.0 <12",
117
+ "glob": ">=9.0.0 <14",
112
118
  "html-minifier-terser": "7.2.0",
113
119
  "inquirer": "^8.2.7",
114
120
  "invisi-data": "^1.0.0",
115
- "joi": "18.0.1",
116
121
  "js-beautify": "1.15.4",
117
122
  "lodash.clonedeep": "4.5.0",
118
123
  "lodash.merge": "4.6.2",
@@ -126,71 +131,70 @@
126
131
  "parse-function": "5.6.10",
127
132
  "parse5": "7.3.0",
128
133
  "promise-retry": "1.1.1",
129
- "resq": "1.11.0",
130
134
  "sprintf-js": "1.1.3",
131
- "uuid": "11.1.0"
135
+ "uuid": "11.1.0",
136
+ "xpath": "0.0.34",
137
+ "zod": "^4.1.11"
132
138
  },
133
139
  "optionalDependencies": {
134
140
  "@codeceptjs/detox-helper": "1.1.13"
135
141
  },
136
142
  "devDependencies": {
137
143
  "@apollo/server": "^5",
138
- "@codeceptjs/expect-helper": "^1.0.2",
144
+ "@codeceptjs/expect-helper": "^4.0.0-beta.5",
139
145
  "@codeceptjs/mock-request": "0.3.1",
140
- "@eslint/eslintrc": "3.3.1",
141
- "@eslint/js": "9.36.0",
142
- "@faker-js/faker": "9.8.0",
143
- "@inquirer/testing": "^2.1.49",
146
+ "@eslint/eslintrc": "3.3.3",
147
+ "@eslint/js": "9.39.2",
148
+ "@faker-js/faker": "10.2.0",
149
+ "@inquirer/testing": "^3.0.3",
144
150
  "@pollyjs/adapter-puppeteer": "6.0.6",
145
151
  "@pollyjs/core": "6.0.6",
146
152
  "@testomatio/reporter": "^2.3.1",
147
- "@types/chai": "5.2.2",
153
+ "@types/chai": "5.2.3",
148
154
  "@types/inquirer": "9.0.9",
149
- "@types/node": "^24.9.2",
155
+ "@types/node": "^25.0.3",
150
156
  "@wdio/sauce-service": "9.12.5",
151
157
  "@wdio/selenium-standalone-service": "8.15.0",
152
- "@wdio/utils": "9.20.0",
158
+ "@wdio/utils": "9.23.3",
153
159
  "@xmldom/xmldom": "0.9.8",
154
160
  "bunosh": "latest",
155
- "chai": "^4.5.0",
156
- "chai-as-promised": "7.1.2",
161
+ "chai": "^6.2.1",
162
+ "chai-as-promised": "^8.0.2",
157
163
  "chai-subset": "1.6.0",
158
164
  "documentation": "14.0.3",
159
- "electron": "38.2.0",
165
+ "electron": "40.1.0",
160
166
  "eslint": "^9.36.0",
161
167
  "eslint-plugin-import": "2.32.0",
162
- "eslint-plugin-mocha": "11.1.0",
168
+ "eslint-plugin-mocha": "11.2.0",
163
169
  "expect": "30.2.0",
164
170
  "express": "^5.1.0",
165
- "globals": "16.4.0",
166
- "graphql": "16.11.0",
171
+ "globals": "17.3.0",
172
+ "graphql": "16.12.0",
167
173
  "graphql-tag": "^2.12.6",
168
174
  "husky": "9.1.7",
169
175
  "jsdoc": "^3.6.11",
170
176
  "jsdoc-typeof-plugin": "1.0.0",
171
177
  "json-server": "0.17.4",
172
- "mochawesome": "^7.1.3",
173
- "playwright": "1.55.1",
178
+ "playwright": "^1.59.0",
174
179
  "prettier": "^3.3.2",
175
- "puppeteer": "24.15.0",
180
+ "puppeteer": "24.36.0",
176
181
  "qrcode-terminal": "0.12.0",
177
182
  "rosie": "2.1.1",
178
183
  "runok": "^0.9.3",
179
184
  "semver": "7.7.3",
180
- "sinon": "21.0.0",
181
- "sinon-chai": "3.7.0",
185
+ "sinon": "21.0.1",
186
+ "sinon-chai": "^4.0.1",
182
187
  "ts-morph": "27.0.2",
183
188
  "ts-node": "10.9.2",
184
189
  "tsd": "^0.33.0",
185
190
  "tsd-jsdoc": "2.5.0",
186
191
  "tsx": "^4.19.2",
187
- "typedoc": "0.28.13",
192
+ "typedoc": "0.28.16",
188
193
  "typedoc-plugin-markdown": "4.9.0",
189
- "typescript": "5.8.3",
194
+ "typescript": "5.9.3",
190
195
  "wdio-docker-service": "3.2.1",
191
- "webdriverio": "9.12.5",
192
- "xml2js": "0.6.2",
193
- "xpath": "0.0.34"
196
+ "webdriverio": "9.23.0",
197
+ "xml2js": "0.6.2"
194
198
  },
195
199
  "peerDependencies": {
196
200
  "tsx": "^4.0.0"
@@ -212,6 +216,7 @@
212
216
  }
213
217
  },
214
218
  "overrides": {
215
- "tmp": "0.2.5"
219
+ "tmp": "0.2.5",
220
+ "js-yaml": "^4.1.1"
216
221
  }
217
222
  }
@@ -4,7 +4,6 @@
4
4
  /// <reference path="./promiseBasedTypes.d.ts" />
5
5
  /// <reference types="webdriverio" />
6
6
  /// <reference path="./Mocha.d.ts" />
7
- /// <reference types="joi" />
8
7
  /// <reference types="playwright" />
9
8
 
10
9
  declare namespace CodeceptJS {
@@ -211,9 +210,6 @@ declare namespace CodeceptJS {
211
210
  */
212
211
  JSONResponse?: any
213
212
 
214
- /** Enable AI features for development purposes */
215
- AI?: any
216
-
217
213
  [key: string]: any
218
214
  }
219
215
  /**
@@ -465,7 +461,6 @@ declare namespace CodeceptJS {
465
461
  | { ios: string }
466
462
  | { android: string; ios: string }
467
463
  | { react: string }
468
- | { vue: string }
469
464
  | { shadow: string[] }
470
465
  | { custom: string }
471
466
  | { pw: string }
@@ -519,12 +514,12 @@ declare namespace CodeceptJS {
519
514
  retry(retries?: number): HookConfig
520
515
  }
521
516
 
522
- function addStep(step: string, fn: Function): Promise<void>
517
+ function addStep(step: string | RegExp, fn: Function): Promise<void>
523
518
  }
524
519
 
525
- type TryTo = <T>(fn: () => Promise<T> | T) => Promise<T | false>
526
- type HopeThat = <T>(fn: () => Promise<T> | T) => Promise<T | false>
527
- type RetryTo = <T>(fn: () => Promise<T> | T, retries?: number) => Promise<T>
520
+ type TryTo = (fn: () => void) => Promise<boolean>
521
+ type HopeThat = (fn: () => void) => Promise<boolean>
522
+ type RetryTo = (fn: (tries: number) => Promise<void> | void, maxTries: number, pollInterval?: number) => Promise<boolean>
528
523
 
529
524
  // Globals
530
525
  declare const codecept_dir: string
@@ -635,8 +630,105 @@ declare namespace Mocha {
635
630
  }
636
631
  }
637
632
 
633
+ // Internal API types
638
634
  declare module 'codeceptjs' {
639
635
  export default codeceptjs
636
+
637
+ /**
638
+ * Dependency Injection Container
639
+ * Provides access to helpers, support objects, plugins, and translation
640
+ */
641
+ export const container: typeof CodeceptJS.Container
642
+
643
+ /**
644
+ * Test runner class
645
+ */
646
+ export const codecept: typeof CodeceptJS.Codecept
647
+
648
+ /**
649
+ * Output module for printing messages
650
+ */
651
+ export const output: typeof CodeceptJS.output
652
+
653
+ /**
654
+ * Event dispatcher for listening to CodeceptJS events
655
+ */
656
+ export const event: typeof CodeceptJS.event
657
+
658
+ /**
659
+ * Global promise chain recorder
660
+ */
661
+ export const recorder: CodeceptJS.recorder
662
+
663
+ /**
664
+ * Configuration module
665
+ */
666
+ export const config: typeof CodeceptJS.Config
667
+
668
+ /**
669
+ * Actor (I) constructor
670
+ */
671
+ export const actor: CodeceptJS.actor
672
+
673
+ /**
674
+ * Base Helper class
675
+ */
676
+ export const helper: typeof CodeceptJS.Helper
677
+
678
+ /**
679
+ * Pause execution until user input
680
+ */
681
+ export const pause: typeof CodeceptJS.pause
682
+
683
+ /**
684
+ * Execute steps within specific context
685
+ */
686
+ export const within: typeof CodeceptJS.within
687
+
688
+ /**
689
+ * Create data tables for data-driven tests
690
+ */
691
+ export const dataTable: typeof CodeceptJS.DataTable
692
+
693
+ /**
694
+ * Create data table arguments
695
+ */
696
+ export const dataTableArgument: typeof CodeceptJS.DataTableArgument
697
+
698
+ /**
699
+ * Shared store for test data
700
+ */
701
+ export const store: typeof CodeceptJS.store
702
+
703
+ /**
704
+ * Locator builder
705
+ */
706
+ export const locator: typeof CodeceptJS.Locator
707
+
708
+ /**
709
+ * Auto-healing module
710
+ */
711
+ export const heal: any
712
+
713
+ /**
714
+ * AI assistant module
715
+ */
716
+ export const ai: any
717
+
718
+ /**
719
+ * Workers for parallel execution
720
+ */
721
+ export const Workers: any
722
+
723
+ /**
724
+ * Secret value type for sensitive data
725
+ */
726
+ export const Secret: typeof CodeceptJS.Secret
727
+
728
+ /**
729
+ * Create a secret value
730
+ */
731
+ export const secret: typeof CodeceptJS.Secret.secret
640
732
  }
641
733
 
642
734
  declare module '@codeceptjs/helper' {
@@ -648,3 +740,22 @@ declare module 'codeceptjs/effects' {
648
740
  export const retryTo: RetryTo
649
741
  export const hopeThat: HopeThat
650
742
  }
743
+
744
+ declare module 'codeceptjs/steps' {
745
+ const step: {
746
+ opts(opts: CodeceptJS.StepOptions): CodeceptJS.StepConfig;
747
+ timeout(timeout: number): CodeceptJS.StepConfig;
748
+ retry(retry: number): CodeceptJS.StepConfig;
749
+ stepOpts(opts: CodeceptJS.StepOptions): CodeceptJS.StepConfig;
750
+ stepTimeout(timeout: number): CodeceptJS.StepConfig;
751
+ stepRetry(retry: number): CodeceptJS.StepConfig;
752
+ section(name: string): any;
753
+ endSection(): any;
754
+ Section(name: string): any;
755
+ EndSection(): any;
756
+ Given(): any;
757
+ When(): any;
758
+ Then(): any;
759
+ }
760
+ export default step
761
+ }