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/mocha/cli.js CHANGED
@@ -8,7 +8,9 @@ import { dirname, join } from 'path'
8
8
  import event from '../event.js'
9
9
  import AssertionFailedError from '../assert/error.js'
10
10
  import output from '../output.js'
11
+ import store from '../store.js'
11
12
  import test, { cloneTest } from './test.js'
13
+ import { fixErrorStack } from '../utils/typescript.js'
12
14
 
13
15
  // Get version from package.json to avoid circular dependency
14
16
  const __filename = fileURLToPath(import.meta.url)
@@ -40,7 +42,7 @@ class Cli extends Base {
40
42
  if (opts.verbose) level = 3
41
43
  output.level(level)
42
44
  output.print(`CodeceptJS v${codeceptVersion} ${output.standWithUkraine()}`)
43
- output.print(`Using test root "${global.codecept_dir}"`)
45
+ output.print(`Using test root "${store.codeceptDir}"`)
44
46
 
45
47
  const showSteps = level >= 1
46
48
 
@@ -201,7 +203,18 @@ class Cli extends Base {
201
203
 
202
204
  // failures
203
205
  if (stats.failures) {
206
+ for (const test of this.failures) {
207
+ if (test.err && typeof test.err.fetchDetails === 'function') {
208
+ try {
209
+ await test.err.fetchDetails()
210
+ } catch (e) {
211
+ // ignore fetch errors
212
+ }
213
+ }
214
+ }
215
+
204
216
  // append step traces
217
+ const Container = await getContainer()
205
218
  this.failures = this.failures.map(test => {
206
219
  // we will change the stack trace, so we need to clone the test
207
220
  const err = test.err
@@ -264,6 +277,11 @@ class Cli extends Base {
264
277
  }
265
278
 
266
279
  try {
280
+ const fileMapping = Container?.tsFileMapping?.()
281
+ if (fileMapping) {
282
+ fixErrorStack(err, fileMapping)
283
+ }
284
+
267
285
  let stack = err.stack
268
286
  stack = (stack || '').replace(originalMessage, '')
269
287
  stack = stack ? stack.split('\n') : []
@@ -7,6 +7,8 @@ import gherkinParser, { loadTranslations } from './gherkin.js'
7
7
  import output from '../output.js'
8
8
  import scenarioUiFunction from './ui.js'
9
9
  import { initMochaGlobals } from '../globals.js'
10
+ import { fixErrorStack } from '../utils/typescript.js'
11
+ import container from '../container.js'
10
12
 
11
13
  const __filename = fileURLToPath(import.meta.url)
12
14
  const __dirname = fsPath.dirname(__filename)
@@ -15,7 +17,11 @@ let mocha
15
17
 
16
18
  class MochaFactory {
17
19
  static create(config, opts) {
18
- mocha = new Mocha(Object.assign(config, opts))
20
+ const merged = Object.assign({}, config, opts)
21
+ mocha = new Mocha(merged)
22
+ if (merged.cleanReferencesAfterRun !== true) {
23
+ mocha.cleanReferencesAfterRun(false)
24
+ }
19
25
  output.process(opts.child)
20
26
  mocha.ui(scenarioUiFunction)
21
27
 
@@ -34,6 +40,10 @@ class MochaFactory {
34
40
  // Handle ECONNREFUSED without dynamic import for now
35
41
  err = new Error('Connection refused: ' + err.toString())
36
42
  }
43
+ const fileMapping = container?.tsFileMapping?.()
44
+ if (fileMapping) {
45
+ fixErrorStack(err, fileMapping)
46
+ }
37
47
  output.error(err)
38
48
  output.print(err.stack)
39
49
  process.exit(1)
@@ -62,34 +72,9 @@ class MochaFactory {
62
72
  const jsFiles = this.files.filter(file => !file.match(/\.feature$/))
63
73
  this.files = this.files.filter(file => !file.match(/\.feature$/))
64
74
 
65
- // Load JavaScript test files using ESM imports
75
+ // Load JavaScript test files using original loadFiles
66
76
  if (jsFiles.length > 0) {
67
- try {
68
- // Try original loadFiles first for compatibility
69
- originalLoadFiles.call(this, fn)
70
- } catch (e) {
71
- // If original loadFiles fails, load ESM files manually
72
- if (e.message.includes('not in cache') || e.message.includes('ESM') || e.message.includes('getStatus')) {
73
- // Load ESM files by importing them synchronously using top-level await workaround
74
- for (const file of jsFiles) {
75
- try {
76
- // Convert file path to file:// URL for dynamic import
77
- const fileUrl = `file://${file}`
78
- // Use import() but don't await it - let it load in the background
79
- import(fileUrl).catch(importErr => {
80
- // If dynamic import fails, the file may have syntax errors or other issues
81
- console.error(`Failed to load test file ${file}:`, importErr.message)
82
- })
83
- if (fn) fn()
84
- } catch (fileErr) {
85
- console.error(`Error processing test file ${file}:`, fileErr.message)
86
- if (fn) fn(fileErr)
87
- }
88
- }
89
- } else {
90
- throw e
91
- }
92
- }
77
+ originalLoadFiles.call(this, fn)
93
78
  }
94
79
 
95
80
  // add ids for each test and check uniqueness
@@ -5,7 +5,7 @@ const getInjectedArguments = async (fn, test, suite) => {
5
5
  const container = containerModule.default || containerModule
6
6
 
7
7
  const testArgs = {}
8
- const params = getParams(fn) || []
8
+ const params = getParams(fn, { warnOnLegacyFormat: true }) || []
9
9
  const objects = container.support()
10
10
 
11
11
  for (const key of params) {
@@ -1,4 +1,5 @@
1
1
  import { isAsyncFunction } from '../utils.js'
2
+ import store from '../store.js'
2
3
 
3
4
  /** @class */
4
5
  class ScenarioConfig {
@@ -40,7 +41,7 @@ class ScenarioConfig {
40
41
  * @returns {this}
41
42
  */
42
43
  retry(retries) {
43
- if (process.env.SCENARIO_ONLY) retries = -retries
44
+ if (store.scenarioOnly) retries = -retries
44
45
  this.test.retries(retries)
45
46
  return this
46
47
  }
package/lib/mocha/test.js CHANGED
@@ -154,14 +154,16 @@ function cloneTest(test) {
154
154
  function testToFileName(test, { suffix = '', unique = false } = {}) {
155
155
  let fileName = test.title
156
156
 
157
- if (unique) fileName = `${fileName}_${test?.uid || Math.floor(new Date().getTime() / 1000)}`
158
- if (suffix) fileName = `${fileName}_${suffix}`
159
157
  // remove tags with empty string (disable for now)
160
158
  // fileName = fileName.replace(/\@\w+/g, '')
161
159
  fileName = fileName.slice(0, 100)
162
160
  if (fileName.indexOf('{') !== -1) {
163
161
  fileName = fileName.substr(0, fileName.indexOf('{') - 3).trim()
164
162
  }
163
+
164
+ // Apply unique suffix AFTER removing data part to ensure uniqueness
165
+ if (unique) fileName = `${fileName}_${test?.uid || Math.floor(new Date().getTime())}`
166
+ if (suffix) fileName = `${fileName}_${suffix}`
165
167
  if (test.ctx && test.ctx.test && test.ctx.test.type === 'hook') fileName = clearString(`${test.title}_${test.ctx.test.title}`)
166
168
  // TODO: add suite title to file name
167
169
  // if (test.parent && test.parent.title) {
package/lib/mocha/ui.js CHANGED
@@ -9,13 +9,12 @@ import { HookConfig, AfterSuiteHook, AfterHook, BeforeSuiteHook, BeforeHook } fr
9
9
  import { initMochaGlobals } from '../globals.js'
10
10
  import common from 'mocha/lib/interfaces/common.js'
11
11
  import container from '../container.js'
12
+ import store from '../store.js'
12
13
 
13
14
  const setContextTranslation = context => {
14
- // Try global container first, then local container instance
15
- const containerToUse = global.container || container
16
- if (!containerToUse) return
15
+ if (!container) return
17
16
 
18
- const translation = containerToUse.translation?.() || containerToUse.translation
17
+ const translation = container.translation?.() || container.translation
19
18
  const contexts = translation?.value?.('contexts')
20
19
 
21
20
  if (contexts) {
@@ -119,7 +118,7 @@ export default function (suite) {
119
118
  context.Feature.only = function (title, opts) {
120
119
  const reString = `^${escapeRe(`${title}:`)}`
121
120
  mocha.grep(new RegExp(reString))
122
- process.env.FEATURE_ONLY = true
121
+ store.featureOnly = true
123
122
  return context.Feature(title, opts)
124
123
  }
125
124
 
@@ -171,7 +170,7 @@ export default function (suite) {
171
170
  context.Scenario.only = function (title, opts, fn) {
172
171
  const reString = `^${escapeRe(`${suites[0].title}: ${title}`.replace(/( \| {.+})?$/g, ''))}`
173
172
  mocha.grep(new RegExp(reString))
174
- process.env.SCENARIO_ONLY = true
173
+ store.scenarioOnly = true
175
174
  return addScenario(title, opts, fn)
176
175
  }
177
176
 
package/lib/output.js CHANGED
@@ -222,12 +222,10 @@ const output = {
222
222
  /**
223
223
  * @param {Mocha.Test} test
224
224
  */
225
-
226
225
  started(test) {
227
226
  if (outputLevel < 1) return
228
227
  print(` ${colors.dim.bold('Scenario()')}`)
229
228
  },
230
-
231
229
  /**
232
230
  * @param {Mocha.Test} test
233
231
  */
@@ -273,10 +271,12 @@ const output = {
273
271
  },
274
272
 
275
273
  /**
274
+ * Prints the stats of a test run to the console.
276
275
  * @param {number} passed
277
276
  * @param {number} failed
278
277
  * @param {number} skipped
279
278
  * @param {number|string} duration
279
+ * @param {number} [failedHooks]
280
280
  */
281
281
  result(passed, failed, skipped, duration, failedHooks = 0) {
282
282
  let style = colors.bgGreen
package/lib/parser.js CHANGED
@@ -14,11 +14,11 @@ export const getParamsToString = function (fn) {
14
14
  return getParams(newFn).join(', ')
15
15
  }
16
16
 
17
- function getParams(fn) {
17
+ function getParams(fn, { warnOnLegacyFormat = false } = {}) {
18
18
  if (fn.isSinonProxy) return []
19
19
  try {
20
20
  const reflected = parser.parse(fn)
21
- if (reflected.args.length > 1 || reflected.args[0] === 'I') {
21
+ if (warnOnLegacyFormat && (reflected.args.length > 1 || reflected.args[0] === 'I')) {
22
22
  output.error('Error: old CodeceptJS v2 format detected. Upgrade your project to the new format -> https://bit.ly/codecept3Up')
23
23
  }
24
24
  if (reflected.destructuredArgs.length > 0) reflected.args = [...reflected.destructuredArgs]
package/lib/pause.js CHANGED
@@ -18,6 +18,8 @@ let nextStep
18
18
  let finish
19
19
  let next
20
20
  let registeredVariables = {}
21
+ let externalHandler = null
22
+
21
23
  /**
22
24
  * Pauses test execution and starts interactive shell
23
25
  * @param {Object<string, *>} [passedObject]
@@ -37,10 +39,10 @@ const pause = function (passedObject = {}) {
37
39
  })
38
40
 
39
41
  event.dispatcher.on(event.test.finished, () => {
40
- finish()
42
+ if (typeof finish === 'function') finish()
41
43
  recorder.session.restore('pause')
42
- rl.close()
43
- history.save()
44
+ if (rl) rl.close()
45
+ if (!externalHandler) history.save()
44
46
  })
45
47
 
46
48
  recorder.add('Start new session', () => pauseSession(passedObject))
@@ -49,6 +51,15 @@ const pause = function (passedObject = {}) {
49
51
  function pauseSession(passedObject = {}) {
50
52
  registeredVariables = passedObject
51
53
  recorder.session.start('pause')
54
+
55
+ if (externalHandler) {
56
+ store.onPause = true
57
+ return externalHandler({ registeredVariables }).then(() => {
58
+ store.onPause = false
59
+ recorder.session.restore('pause')
60
+ })
61
+ }
62
+
52
63
  if (!next) {
53
64
  let vars = Object.keys(registeredVariables).join(', ')
54
65
  if (vars) vars = `(vars: ${vars})`
@@ -234,5 +245,28 @@ function registerVariable(name, value) {
234
245
  registeredVariables[name] = value
235
246
  }
236
247
 
248
+ /**
249
+ * Hook for external pause drivers (e.g. the MCP server). When set, pauseSession
250
+ * delegates to the handler instead of opening a readline REPL. The handler
251
+ * receives `{ registeredVariables }` and returns a Promise that resolves when
252
+ * the driver decides to continue (resume) or step.
253
+ *
254
+ * The driver controls step-vs-resume by mutating `next` via setNextStep before
255
+ * resolving its Promise.
256
+ */
257
+ function setPauseHandler(handler) {
258
+ externalHandler = handler
259
+ }
260
+
261
+ /**
262
+ * Trigger a one-shot pause from outside the test (e.g. the MCP server,
263
+ * pausing the test at a specific step index without modifying the test).
264
+ * Schedules pauseSession through the recorder so it slots between steps.
265
+ */
266
+ function pauseNow(passedObject = {}) {
267
+ if (store.dryRun) return
268
+ recorder.add('Triggered pause', () => pauseSession(passedObject))
269
+ }
270
+
237
271
  export default pause
238
- export { registerVariable }
272
+ export { registerVariable, setPauseHandler, pauseNow }