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
@@ -1,13 +1,68 @@
1
1
  import fs from 'fs'
2
2
  import path from 'path'
3
+ import { pathToFileURL } from 'url'
4
+
5
+ /**
6
+ * Load tsconfig.json if it exists
7
+ * @param {string} tsConfigPath - Path to tsconfig.json
8
+ * @returns {object|null} - Parsed tsconfig or null
9
+ */
10
+ function loadTsConfig(tsConfigPath) {
11
+ if (!fs.existsSync(tsConfigPath)) {
12
+ return null
13
+ }
14
+
15
+ try {
16
+ const tsConfigContent = fs.readFileSync(tsConfigPath, 'utf8')
17
+ return JSON.parse(tsConfigContent)
18
+ } catch (err) {
19
+ return null
20
+ }
21
+ }
22
+
23
+ /**
24
+ * Resolve TypeScript path alias to actual file path
25
+ * @param {string} importPath - Import path with alias (e.g., '#config/urls')
26
+ * @param {object} tsConfig - Parsed tsconfig.json
27
+ * @param {string} configDir - Directory containing tsconfig.json
28
+ * @returns {string|null} - Resolved file path or null if not an alias
29
+ */
30
+ function resolveTsPathAlias(importPath, tsConfig, configDir) {
31
+ if (!tsConfig || !tsConfig.compilerOptions || !tsConfig.compilerOptions.paths) {
32
+ return null
33
+ }
34
+
35
+ const paths = tsConfig.compilerOptions.paths
36
+
37
+ for (const [pattern, targets] of Object.entries(paths)) {
38
+ if (!targets || targets.length === 0) {
39
+ continue
40
+ }
41
+
42
+ const patternRegex = new RegExp(
43
+ '^' + pattern.replace(/\*/g, '(.*)') + '$'
44
+ )
45
+ const match = importPath.match(patternRegex)
46
+
47
+ if (match) {
48
+ const wildcard = match[1] || ''
49
+ const target = targets[0]
50
+ const resolvedTarget = target.replace(/\*/g, wildcard)
51
+
52
+ return path.resolve(configDir, resolvedTarget)
53
+ }
54
+ }
55
+
56
+ return null
57
+ }
3
58
 
4
59
  /**
5
60
  * Transpile TypeScript files to ES modules with CommonJS shim support
6
61
  * Handles recursive transpilation of imported TypeScript files
7
- *
62
+ *
8
63
  * @param {string} mainFilePath - Path to the main TypeScript file to transpile
9
64
  * @param {object} typescript - TypeScript compiler instance
10
- * @returns {Promise<{tempFile: string, allTempFiles: string[]}>} - Main temp file and all temp files created
65
+ * @returns {Promise<{tempFile: string, allTempFiles: string[], fileMapping: any}>} - Main temp file and all temp files created
11
66
  */
12
67
  export async function transpileTypeScript(mainFilePath, typescript) {
13
68
  const { transpile } = typescript
@@ -18,7 +73,7 @@ export async function transpileTypeScript(mainFilePath, typescript) {
18
73
  */
19
74
  const transpileTS = (filePath) => {
20
75
  const tsContent = fs.readFileSync(filePath, 'utf8')
21
-
76
+
22
77
  // Transpile TypeScript to JavaScript with ES module output
23
78
  let jsContent = transpile(tsContent, {
24
79
  module: 99, // ModuleKind.ESNext
@@ -29,16 +84,16 @@ export async function transpileTypeScript(mainFilePath, typescript) {
29
84
  suppressOutputPathCheck: true,
30
85
  skipLibCheck: true,
31
86
  })
32
-
87
+
33
88
  // Check if the code uses CommonJS globals
34
89
  const usesCommonJSGlobals = /__dirname|__filename/.test(jsContent)
35
90
  const usesRequire = /\brequire\s*\(/.test(jsContent)
36
91
  const usesModuleExports = /\b(module\.exports|exports\.)/.test(jsContent)
37
-
92
+
38
93
  if (usesCommonJSGlobals || usesRequire || usesModuleExports) {
39
94
  // Inject ESM equivalents at the top of the file
40
95
  let esmGlobals = ''
41
-
96
+
42
97
  if (usesRequire || usesModuleExports) {
43
98
  // IMPORTANT: Use the original .ts file path as the base for require()
44
99
  // This ensures dynamic require() calls work with relative paths from the original file location
@@ -81,7 +136,7 @@ const exports = module.exports;
81
136
 
82
137
  `
83
138
  }
84
-
139
+
85
140
  if (usesCommonJSGlobals) {
86
141
  // For __dirname and __filename, also use the original file path
87
142
  const originalFileUrl = `file://${filePath.replace(/\\/g, '/')}`
@@ -92,48 +147,79 @@ const __dirname = __dirname_fn(__filename);
92
147
 
93
148
  `
94
149
  }
95
-
150
+
96
151
  jsContent = esmGlobals + jsContent
97
-
152
+
98
153
  // If module.exports is used, we need to export it as default
99
154
  if (usesModuleExports) {
100
155
  jsContent += `\nexport default module.exports;\n`
101
156
  }
102
157
  }
103
-
158
+
104
159
  return jsContent
105
160
  }
106
-
161
+
107
162
  // Create a map to track transpiled files
108
163
  const transpiledFiles = new Map()
109
164
  const baseDir = path.dirname(mainFilePath)
110
-
165
+
166
+ // Try to find tsconfig.json by walking up the directory tree
167
+ let tsConfigPath = path.join(baseDir, 'tsconfig.json')
168
+ let configDir = baseDir
169
+ let searchDir = baseDir
170
+
171
+ while (!fs.existsSync(tsConfigPath) && searchDir !== path.dirname(searchDir)) {
172
+ searchDir = path.dirname(searchDir)
173
+ tsConfigPath = path.join(searchDir, 'tsconfig.json')
174
+ if (fs.existsSync(tsConfigPath)) {
175
+ configDir = searchDir
176
+ break
177
+ }
178
+ }
179
+
180
+ const tsConfig = loadTsConfig(tsConfigPath)
181
+
111
182
  // Recursive function to transpile a file and all its TypeScript dependencies
112
183
  const transpileFileAndDeps = (filePath) => {
113
184
  // Already transpiled, skip
114
185
  if (transpiledFiles.has(filePath)) {
115
186
  return
116
187
  }
117
-
188
+
118
189
  // Transpile this file
119
190
  let jsContent = transpileTS(filePath)
120
-
121
- // Find all relative TypeScript imports in this file
122
- const importRegex = /from\s+['"](\..+?)(?:\.ts)?['"]/g
191
+
192
+ // Find all TypeScript imports in this file (both ESM imports and require() calls)
193
+ const importRegex = /from\s+['"]([^'"]+?)['"]/g
194
+ const requireRegex = /require\s*\(\s*['"]([^'"]+?)['"]\s*\)/g
123
195
  let match
124
196
  const imports = []
125
-
197
+
126
198
  while ((match = importRegex.exec(jsContent)) !== null) {
127
- imports.push(match[1])
199
+ imports.push({ path: match[1], type: 'import' })
128
200
  }
129
-
201
+
202
+ while ((match = requireRegex.exec(jsContent)) !== null) {
203
+ imports.push({ path: match[1], type: 'require' })
204
+ }
205
+
130
206
  // Get the base directory for this file
131
207
  const fileBaseDir = path.dirname(filePath)
132
-
208
+
133
209
  // Recursively transpile each imported TypeScript file
134
- for (const relativeImport of imports) {
135
- let importedPath = path.resolve(fileBaseDir, relativeImport)
136
-
210
+ for (const { path: importPath } of imports) {
211
+ let importedPath = importPath
212
+
213
+ // Check if this is a path alias
214
+ const resolvedAlias = resolveTsPathAlias(importPath, tsConfig, configDir)
215
+ if (resolvedAlias) {
216
+ importedPath = resolvedAlias
217
+ } else if (importPath.startsWith('.')) {
218
+ importedPath = path.resolve(fileBaseDir, importPath)
219
+ } else {
220
+ continue
221
+ }
222
+
137
223
  // Handle .js extensions that might actually be .ts files
138
224
  if (importedPath.endsWith('.js')) {
139
225
  const tsVersion = importedPath.replace(/\.js$/, '.ts')
@@ -141,83 +227,209 @@ const __dirname = __dirname_fn(__filename);
141
227
  importedPath = tsVersion
142
228
  }
143
229
  }
144
-
145
- // Try adding .ts extension if file doesn't exist and no extension provided
146
- if (!path.extname(importedPath)) {
230
+
231
+ // Check for standard module extensions to determine if we should try adding .ts
232
+ const ext = path.extname(importedPath)
233
+ const standardExtensions = ['.js', '.mjs', '.cjs', '.json', '.node']
234
+ const hasStandardExtension = standardExtensions.includes(ext.toLowerCase())
235
+
236
+ // If it doesn't end with .ts and doesn't have a standard extension, try adding .ts
237
+ if (!importedPath.endsWith('.ts') && !hasStandardExtension) {
147
238
  const tsPath = importedPath + '.ts'
148
239
  if (fs.existsSync(tsPath)) {
149
240
  importedPath = tsPath
150
241
  } else {
151
- // Try .js extension as well
152
- const jsPath = importedPath + '.js'
153
- if (fs.existsSync(jsPath)) {
154
- // Skip .js files, they don't need transpilation
155
- continue
242
+ // Try index.ts for directory imports
243
+ const indexTsPath = path.join(importedPath, 'index.ts')
244
+ if (fs.existsSync(indexTsPath)) {
245
+ importedPath = indexTsPath
246
+ } else {
247
+ // Try .js extension as well
248
+ const jsPath = importedPath + '.js'
249
+ if (fs.existsSync(jsPath)) {
250
+ // Skip .js files, they don't need transpilation
251
+ continue
252
+ }
156
253
  }
157
254
  }
158
255
  }
159
-
256
+
160
257
  // If it's a TypeScript file, recursively transpile it and its dependencies
161
258
  if (importedPath.endsWith('.ts') && fs.existsSync(importedPath)) {
162
259
  transpileFileAndDeps(importedPath)
163
260
  }
164
261
  }
165
-
262
+
166
263
  // After all dependencies are transpiled, rewrite imports in this file
167
264
  jsContent = jsContent.replace(
168
- /from\s+['"](\..+?)(?:\.ts)?['"]/g,
265
+ /from\s+['"]([^'"]+?)['"]/g,
169
266
  (match, importPath) => {
170
- let resolvedPath = path.resolve(fileBaseDir, importPath)
171
-
267
+ let resolvedPath = importPath
268
+ const originalExt = path.extname(importPath)
269
+
270
+ // Check if this is a path alias
271
+ const resolvedAlias = resolveTsPathAlias(importPath, tsConfig, configDir)
272
+ if (resolvedAlias) {
273
+ resolvedPath = resolvedAlias
274
+ } else if (importPath.startsWith('.')) {
275
+ resolvedPath = path.resolve(fileBaseDir, importPath)
276
+ } else {
277
+ return match
278
+ }
279
+
280
+ // If resolved path is a directory, try index.ts
281
+ if (fs.existsSync(resolvedPath) && fs.statSync(resolvedPath).isDirectory()) {
282
+ const indexPath = path.join(resolvedPath, 'index.ts')
283
+ if (fs.existsSync(indexPath) && transpiledFiles.has(indexPath)) {
284
+ const tempFile = transpiledFiles.get(indexPath)
285
+ const relPath = path.relative(fileBaseDir, tempFile).replace(/\\/g, '/')
286
+ if (!relPath.startsWith('.')) {
287
+ return `from './${relPath}'`
288
+ }
289
+ return `from '${relPath}'`
290
+ }
291
+ }
292
+
172
293
  // Handle .js extension that might be .ts
173
294
  if (resolvedPath.endsWith('.js')) {
174
295
  const tsVersion = resolvedPath.replace(/\.js$/, '.ts')
175
296
  if (transpiledFiles.has(tsVersion)) {
176
297
  const tempFile = transpiledFiles.get(tsVersion)
177
298
  const relPath = path.relative(fileBaseDir, tempFile).replace(/\\/g, '/')
178
- // Ensure the path starts with ./
179
299
  if (!relPath.startsWith('.')) {
180
300
  return `from './${relPath}'`
181
301
  }
182
302
  return `from '${relPath}'`
183
303
  }
304
+ return match
184
305
  }
185
-
306
+
186
307
  // Try with .ts extension
187
308
  const tsPath = resolvedPath.endsWith('.ts') ? resolvedPath : resolvedPath + '.ts'
188
-
309
+
189
310
  // If we transpiled this file, use the temp file
190
311
  if (transpiledFiles.has(tsPath)) {
191
312
  const tempFile = transpiledFiles.get(tsPath)
192
313
  const relPath = path.relative(fileBaseDir, tempFile).replace(/\\/g, '/')
193
- // Ensure the path starts with ./
194
314
  if (!relPath.startsWith('.')) {
195
315
  return `from './${relPath}'`
196
316
  }
197
317
  return `from '${relPath}'`
198
318
  }
199
-
200
- // Otherwise, keep the import as-is
319
+
320
+ // Try index.ts for directory imports
321
+ const indexTsPath = path.join(resolvedPath, 'index.ts')
322
+ if (transpiledFiles.has(indexTsPath)) {
323
+ const tempFile = transpiledFiles.get(indexTsPath)
324
+ const relPath = path.relative(fileBaseDir, tempFile).replace(/\\/g, '/')
325
+ if (!relPath.startsWith('.')) {
326
+ return `from './${relPath}'`
327
+ }
328
+ return `from '${relPath}'`
329
+ }
330
+
331
+ // If the import doesn't have a standard module extension, add .js for ESM compatibility
332
+ const standardExtensions = ['.js', '.mjs', '.cjs', '.json', '.node']
333
+ const hasStandardExtension = standardExtensions.includes(originalExt.toLowerCase())
334
+
335
+ if (!hasStandardExtension) {
336
+ return match.replace(importPath, importPath + '.js')
337
+ }
338
+
201
339
  return match
202
340
  }
203
341
  )
204
-
342
+
343
+ // Also rewrite require() calls to point to transpiled TypeScript files
344
+ jsContent = jsContent.replace(
345
+ /require\s*\(\s*['"]([^'"]+?)['"]\s*\)/g,
346
+ (match, requirePath) => {
347
+ let resolvedPath = requirePath
348
+
349
+ // Check if this is a path alias
350
+ const resolvedAlias = resolveTsPathAlias(requirePath, tsConfig, configDir)
351
+ if (resolvedAlias) {
352
+ resolvedPath = resolvedAlias
353
+ } else if (requirePath.startsWith('.')) {
354
+ resolvedPath = path.resolve(fileBaseDir, requirePath)
355
+ } else {
356
+ return match
357
+ }
358
+
359
+ // Handle .js extension that might be .ts
360
+ if (resolvedPath.endsWith('.js')) {
361
+ const tsVersion = resolvedPath.replace(/\.js$/, '.ts')
362
+ if (transpiledFiles.has(tsVersion)) {
363
+ const tempFile = transpiledFiles.get(tsVersion)
364
+ const relPath = path.relative(fileBaseDir, tempFile).replace(/\\/g, '/')
365
+ const finalPath = relPath.startsWith('.') ? relPath : './' + relPath
366
+ return `require('${finalPath}')`
367
+ }
368
+ return match
369
+ }
370
+
371
+ // Try with .ts extension
372
+ const tsPath = resolvedPath.endsWith('.ts') ? resolvedPath : resolvedPath + '.ts'
373
+
374
+ // If we transpiled this file, use the temp file
375
+ if (transpiledFiles.has(tsPath)) {
376
+ const tempFile = transpiledFiles.get(tsPath)
377
+ const relPath = path.relative(fileBaseDir, tempFile).replace(/\\/g, '/')
378
+ const finalPath = relPath.startsWith('.') ? relPath : './' + relPath
379
+ return `require('${finalPath}')`
380
+ }
381
+
382
+ // Otherwise, keep the require as-is
383
+ return match
384
+ }
385
+ )
386
+
205
387
  // Write the transpiled file with updated imports
206
388
  const tempFile = filePath.replace(/\.ts$/, '.temp.mjs')
207
389
  fs.writeFileSync(tempFile, jsContent)
208
390
  transpiledFiles.set(filePath, tempFile)
209
391
  }
210
-
392
+
211
393
  // Start recursive transpilation from the main file
212
394
  transpileFileAndDeps(mainFilePath)
213
-
395
+
214
396
  // Get the main transpiled file
215
397
  const tempJsFile = transpiledFiles.get(mainFilePath)
216
-
217
- // Store all temp files for cleanup
398
+
399
+ // Convert to file:// URL for dynamic import() (required on Windows)
400
+ const tempFileUrl = pathToFileURL(tempJsFile).href
401
+
402
+ // Store all temp files for cleanup (keep as paths, not URLs)
218
403
  const allTempFiles = Array.from(transpiledFiles.values())
219
-
220
- return { tempFile: tempJsFile, allTempFiles }
404
+
405
+ return { tempFile: tempFileUrl, allTempFiles, fileMapping: transpiledFiles }
406
+ }
407
+
408
+ /**
409
+ * Map error stack traces from temp .mjs files back to original .ts files
410
+ * @param {Error} error - The error object to fix
411
+ * @param {Map<string, string>} fileMapping - Map of original .ts files to temp .mjs files
412
+ * @returns {Error} - Error with fixed stack trace
413
+ */
414
+ export function fixErrorStack(error, fileMapping) {
415
+ if (!error.stack || !fileMapping) return error
416
+
417
+ let stack = error.stack
418
+
419
+ // Create reverse mapping (temp.mjs -> original.ts)
420
+ const reverseMap = new Map()
421
+ for (const [tsFile, mjsFile] of fileMapping.entries()) {
422
+ reverseMap.set(mjsFile, tsFile)
423
+ }
424
+
425
+ // Replace all temp.mjs references with original .ts files
426
+ for (const [mjsFile, tsFile] of reverseMap.entries()) {
427
+ const mjsPattern = mjsFile.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
428
+ stack = stack.replace(new RegExp(mjsPattern, 'g'), tsFile)
429
+ }
430
+
431
+ error.stack = stack
432
+ return error
221
433
  }
222
434
 
223
435
  /**
package/lib/utils.js CHANGED
@@ -7,6 +7,7 @@ import getFunctionArguments from 'fn-args'
7
7
  import deepClone from 'lodash.clonedeep'
8
8
  import merge from 'lodash.merge'
9
9
  import { convertColorToRGBA, isColorProperty } from './colorUtils.js'
10
+ import store from './store.js'
10
11
  import Fuse from 'fuse.js'
11
12
  import crypto from 'crypto'
12
13
  import jsBeautify from 'js-beautify'
@@ -150,6 +151,24 @@ export const decodeUrl = function (url) {
150
151
  return decodeURIComponent(decodeURIComponent(decodeURIComponent(url)))
151
152
  }
152
153
 
154
+ export const normalizePath = function (path) {
155
+ if (path === '' || path === '/') return '/'
156
+ return path
157
+ .replace(/\/+/g, '/')
158
+ .replace(/\/$/, '') || '/'
159
+ }
160
+
161
+ export const resolveUrl = function (url, baseUrl) {
162
+ if (!url) return url
163
+ if (url.indexOf('http') === 0) return url
164
+ if (!baseUrl) return url
165
+ try {
166
+ return new URL(url, baseUrl).href
167
+ } catch (e) {
168
+ return url
169
+ }
170
+ }
171
+
153
172
  export const xpathLocator = {
154
173
  /**
155
174
  * @param {string} string
@@ -317,13 +336,13 @@ export const screenshotOutputFolder = function (fileName) {
317
336
  const fileSep = path.sep
318
337
 
319
338
  if (!fileName.includes(fileSep) || fileName.includes('record_')) {
320
- return path.resolve(global.output_dir, fileName)
339
+ return path.resolve(store.outputDir, fileName)
321
340
  }
322
- return path.resolve(global.codecept_dir, fileName)
341
+ return path.resolve(store.codeceptDir, fileName)
323
342
  }
324
343
 
325
344
  export const relativeDir = function (fileName) {
326
- return fileName.replace(global.codecept_dir, '').replace(/^\//, '')
345
+ return fileName.replace(store.codeceptDir, '').replace(/^\//, '')
327
346
  }
328
347
 
329
348
  export const beautify = function (code) {
@@ -598,6 +617,12 @@ function createCircularSafeReplacer(keysToSkip = []) {
598
617
  return undefined
599
618
  }
600
619
 
620
+ // Coerce types that JSON.stringify can't handle natively
621
+ if (typeof value === 'function') return `[Function: ${value.name || 'anonymous'}]`
622
+ if (typeof value === 'bigint') return `${value.toString()}n`
623
+ if (typeof value === 'symbol') return value.toString()
624
+ if (value instanceof Error) return { name: value.name, message: value.message, stack: value.stack }
625
+
601
626
  if (value === null || typeof value !== 'object') {
602
627
  return value
603
628
  }
@@ -628,6 +653,25 @@ export const safeStringify = function (obj, keysToSkip = [], space = 0) {
628
653
  }
629
654
  }
630
655
 
656
+ /**
657
+ * Truncate a string at a byte cap, returning structured info.
658
+ * @param {string} str
659
+ * @param {number} maxBytes
660
+ * @returns {{ value: string, truncated: boolean, fullLength: number }}
661
+ */
662
+ export const truncateString = function (str, maxBytes) {
663
+ if (typeof str !== 'string') str = String(str)
664
+ if (str.length <= maxBytes) {
665
+ return { value: str, truncated: false, fullLength: str.length }
666
+ }
667
+ const dropped = str.length - maxBytes
668
+ return {
669
+ value: `${str.slice(0, maxBytes)}\n...[truncated ${dropped} more chars]`,
670
+ truncated: true,
671
+ fullLength: str.length,
672
+ }
673
+ }
674
+
631
675
  export const serializeError = function (error) {
632
676
  if (error) {
633
677
  const { stack, uncaught, message, actual, expected } = error
@@ -640,6 +684,36 @@ export const base64EncodeFile = function (filePath) {
640
684
  return Buffer.from(fs.readFileSync(filePath)).toString('base64')
641
685
  }
642
686
 
687
+ export const getMimeType = function (fileName) {
688
+ const ext = path.extname(fileName).toLowerCase()
689
+ const mimeTypes = {
690
+ '.jpg': 'image/jpeg',
691
+ '.jpeg': 'image/jpeg',
692
+ '.png': 'image/png',
693
+ '.gif': 'image/gif',
694
+ '.bmp': 'image/bmp',
695
+ '.svg': 'image/svg+xml',
696
+ '.webp': 'image/webp',
697
+ '.pdf': 'application/pdf',
698
+ '.txt': 'text/plain',
699
+ '.html': 'text/html',
700
+ '.css': 'text/css',
701
+ '.js': 'application/javascript',
702
+ '.json': 'application/json',
703
+ '.xml': 'application/xml',
704
+ '.zip': 'application/zip',
705
+ '.csv': 'text/csv',
706
+ '.doc': 'application/msword',
707
+ '.docx': 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
708
+ '.xls': 'application/vnd.ms-excel',
709
+ '.xlsx': 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
710
+ '.mp3': 'audio/mpeg',
711
+ '.mp4': 'video/mp4',
712
+ '.wav': 'audio/wav',
713
+ }
714
+ return mimeTypes[ext] || 'application/octet-stream'
715
+ }
716
+
643
717
  export const markdownToAnsi = function (markdown) {
644
718
  return (
645
719
  markdown