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.
- package/README.md +39 -28
- package/bin/codecept.js +17 -4
- package/bin/codeceptq.js +49 -0
- package/bin/mcp-server.js +1189 -0
- package/docs/advanced.md +201 -0
- package/docs/agents.md +181 -0
- package/docs/ai.md +489 -0
- package/docs/aitrace.md +266 -0
- package/docs/api.md +332 -0
- package/docs/architecture.md +235 -0
- package/docs/assertions.md +415 -0
- package/docs/auth.md +318 -0
- package/docs/basics.md +424 -0
- package/docs/bdd.md +539 -0
- package/docs/best.md +240 -0
- package/docs/bootstrap.md +132 -0
- package/docs/commands.md +352 -0
- package/docs/community-helpers.md +63 -0
- package/docs/configuration.md +185 -0
- package/docs/continuous-integration.md +431 -0
- package/docs/custom-helpers.md +297 -0
- package/docs/data.md +448 -0
- package/docs/debugging.md +332 -0
- package/docs/detox.md +235 -0
- package/docs/docker.md +107 -0
- package/docs/effects.md +179 -0
- package/docs/element-based-testing.md +295 -0
- package/docs/element-selection.md +125 -0
- package/docs/els.md +328 -0
- package/docs/environment-variables.md +131 -0
- package/docs/examples.md +160 -0
- package/docs/heal.md +213 -0
- package/docs/helpers/ApiDataFactory.md +267 -0
- package/docs/helpers/Appium.md +1419 -0
- package/docs/helpers/Detox.md +665 -0
- package/docs/helpers/ExpectHelper.md +275 -0
- package/docs/helpers/FileSystem.md +152 -0
- package/docs/helpers/GraphQL.md +152 -0
- package/docs/helpers/GraphQLDataFactory.md +226 -0
- package/docs/helpers/JSONResponse.md +255 -0
- package/docs/helpers/MockRequest.md +377 -0
- package/docs/helpers/Playwright.md +2970 -0
- package/docs/helpers/Puppeteer-firefox.md +86 -0
- package/docs/helpers/Puppeteer.md +2583 -0
- package/docs/helpers/REST.md +289 -0
- package/docs/helpers/WebDriver.md +2639 -0
- package/docs/hooks.md +148 -0
- package/docs/index.md +111 -0
- package/docs/installation.md +121 -0
- package/docs/internal-test-server.md +89 -0
- package/docs/locators.md +355 -0
- package/docs/mcp.md +485 -0
- package/docs/migrate-from-cypress.md +98 -0
- package/docs/migrate-from-java.md +108 -0
- package/docs/migrate-from-protractor.md +101 -0
- package/docs/migrate-from-testcafe.md +99 -0
- package/docs/migration-4.md +745 -0
- package/docs/mobile.md +338 -0
- package/docs/pageobjects.md +399 -0
- package/docs/parallel.md +187 -0
- package/docs/playwright.md +714 -0
- package/docs/plugins/aiTrace.md +49 -0
- package/docs/plugins/analyze.md +66 -0
- package/docs/plugins/auth.md +241 -0
- package/docs/plugins/autoDelay.md +48 -0
- package/docs/plugins/browser.md +41 -0
- package/docs/plugins/coverage.md +39 -0
- package/docs/plugins/customLocator.md +119 -0
- package/docs/plugins/customReporter.md +16 -0
- package/docs/plugins/expose.md +75 -0
- package/docs/plugins/heal.md +44 -0
- package/docs/plugins/junitReporter.md +51 -0
- package/docs/plugins/pageInfo.md +34 -0
- package/docs/plugins/pause.md +43 -0
- package/docs/plugins/pauseOnFail.md +18 -0
- package/docs/plugins/retryFailedStep.md +75 -0
- package/docs/plugins/screencast.md +55 -0
- package/docs/plugins/screenshot.md +58 -0
- package/docs/plugins/screenshotOnFail.md +18 -0
- package/docs/plugins/stepTimeout.md +65 -0
- package/docs/plugins.md +87 -0
- package/docs/puppeteer.md +314 -0
- package/docs/quickstart.md +120 -0
- package/docs/reports.md +195 -0
- package/docs/retry.md +311 -0
- package/docs/secrets.md +150 -0
- package/docs/sessions.md +80 -0
- package/docs/shadow.md +68 -0
- package/docs/store.md +94 -0
- package/docs/test-structure.md +275 -0
- package/docs/timeouts.md +183 -0
- package/docs/translation.md +247 -0
- package/docs/tutorial.md +323 -0
- package/docs/typescript.md +159 -0
- package/docs/web-element.md +251 -0
- package/docs/webdriver.md +641 -0
- package/docs/within.md +55 -0
- package/lib/actor.js +1 -36
- package/lib/ai.js +3 -2
- package/lib/aria.js +260 -0
- package/lib/assertions.js +18 -0
- package/lib/codecept.js +34 -25
- package/lib/command/check.js +2 -1
- package/lib/command/definitions.js +14 -10
- package/lib/command/dryRun.js +24 -5
- package/lib/command/generate.js +3 -1
- package/lib/command/gherkin/snippets.js +5 -4
- package/lib/command/init.js +249 -270
- package/lib/command/list.js +150 -10
- package/lib/command/query.js +218 -0
- package/lib/command/run-multiple.js +3 -2
- package/lib/command/run-workers.js +14 -16
- package/lib/command/run.js +3 -17
- package/lib/command/utils.js +14 -0
- package/lib/command/workers/runTests.js +117 -9
- package/lib/config.js +98 -19
- package/lib/container.js +188 -19
- package/lib/effects.js +17 -0
- package/lib/element/WebElement.js +246 -2
- package/lib/els.js +12 -6
- package/lib/globals.js +32 -19
- package/lib/heal.js +7 -4
- package/lib/helper/ApiDataFactory.js +2 -1
- package/lib/helper/Appium.js +8 -8
- package/lib/helper/FileSystem.js +3 -2
- package/lib/helper/GraphQLDataFactory.js +2 -1
- package/lib/helper/Playwright.js +367 -516
- package/lib/helper/Puppeteer.js +343 -197
- package/lib/helper/WebDriver.js +324 -111
- package/lib/helper/errors/ElementNotFound.js +5 -2
- package/lib/helper/errors/MultipleElementsFound.js +52 -0
- package/lib/helper/errors/NonFocusedType.js +8 -0
- package/lib/helper/extras/Download.js +45 -0
- package/lib/helper/extras/PlaywrightLocator.js +7 -107
- package/lib/helper/extras/elementSelection.js +58 -0
- package/lib/helper/extras/focusCheck.js +43 -0
- package/lib/helper/extras/richTextEditor.js +178 -0
- package/lib/helper/scripts/dropFile.js +11 -0
- package/lib/history.js +3 -2
- package/lib/html.js +103 -16
- package/lib/index.js +9 -1
- package/lib/listener/config.js +6 -4
- package/lib/listener/emptyRun.js +2 -1
- package/lib/listener/globalRetry.js +32 -6
- package/lib/listener/helpers.js +6 -15
- package/lib/listener/mocha.js +2 -1
- package/lib/listener/pageobjects.js +43 -0
- package/lib/listener/result.js +3 -2
- package/lib/locator.js +158 -16
- package/lib/mocha/cli.js +19 -1
- package/lib/mocha/factory.js +13 -28
- package/lib/mocha/inject.js +1 -1
- package/lib/mocha/scenarioConfig.js +2 -1
- package/lib/mocha/test.js +4 -2
- package/lib/mocha/ui.js +5 -6
- package/lib/output.js +2 -2
- package/lib/parser.js +2 -2
- package/lib/pause.js +38 -4
- package/lib/plugin/aiTrace.js +457 -0
- package/lib/plugin/analyze.js +9 -9
- package/lib/plugin/auth.js +5 -4
- package/lib/plugin/browser.js +77 -0
- package/lib/plugin/expose.js +159 -0
- package/lib/plugin/heal.js +47 -3
- package/lib/plugin/junitReporter.js +303 -0
- package/lib/plugin/pageInfo.js +54 -52
- package/lib/plugin/pause.js +131 -0
- package/lib/plugin/pauseOnFail.js +11 -33
- package/lib/plugin/retryFailedStep.js +43 -32
- package/lib/plugin/screencast.js +289 -0
- package/lib/plugin/screenshot.js +558 -0
- package/lib/plugin/screenshotOnFail.js +9 -170
- package/lib/plugin/stepTimeout.js +3 -2
- package/lib/recorder.js +1 -1
- package/lib/rerun.js +2 -1
- package/lib/result.js +2 -1
- package/lib/step/base.js +23 -9
- package/lib/step/comment.js +2 -2
- package/lib/step/config.js +15 -2
- package/lib/step/helper.js +4 -4
- package/lib/step/meta.js +3 -3
- package/lib/step/record.js +12 -4
- package/lib/store.js +72 -3
- package/lib/translation.js +2 -1
- package/lib/utils/loaderCheck.js +41 -3
- package/lib/utils/mask_data.js +2 -1
- package/lib/utils/pluginParser.js +151 -0
- package/lib/utils/trace.js +297 -0
- package/lib/utils/typescript.js +261 -49
- package/lib/utils.js +77 -3
- package/lib/workers.js +123 -17
- package/package.json +48 -43
- package/typings/index.d.ts +120 -9
- package/typings/promiseBasedTypes.d.ts +3243 -6057
- package/typings/types.d.ts +3541 -6506
- package/docs/webapi/amOnPage.mustache +0 -11
- package/docs/webapi/appendField.mustache +0 -11
- package/docs/webapi/attachFile.mustache +0 -12
- package/docs/webapi/blur.mustache +0 -18
- package/docs/webapi/checkOption.mustache +0 -13
- package/docs/webapi/clearCookie.mustache +0 -9
- package/docs/webapi/clearField.mustache +0 -9
- package/docs/webapi/click.mustache +0 -29
- package/docs/webapi/clickLink.mustache +0 -8
- package/docs/webapi/closeCurrentTab.mustache +0 -7
- package/docs/webapi/closeOtherTabs.mustache +0 -8
- package/docs/webapi/dontSee.mustache +0 -11
- package/docs/webapi/dontSeeCheckboxIsChecked.mustache +0 -10
- package/docs/webapi/dontSeeCookie.mustache +0 -8
- package/docs/webapi/dontSeeCurrentUrlEquals.mustache +0 -10
- package/docs/webapi/dontSeeElement.mustache +0 -8
- package/docs/webapi/dontSeeElementInDOM.mustache +0 -8
- package/docs/webapi/dontSeeInCurrentUrl.mustache +0 -4
- package/docs/webapi/dontSeeInField.mustache +0 -11
- package/docs/webapi/dontSeeInSource.mustache +0 -8
- package/docs/webapi/dontSeeInTitle.mustache +0 -8
- package/docs/webapi/dontSeeTraffic.mustache +0 -13
- package/docs/webapi/doubleClick.mustache +0 -13
- package/docs/webapi/downloadFile.mustache +0 -12
- package/docs/webapi/dragAndDrop.mustache +0 -9
- package/docs/webapi/dragSlider.mustache +0 -11
- package/docs/webapi/executeAsyncScript.mustache +0 -24
- package/docs/webapi/executeScript.mustache +0 -26
- package/docs/webapi/fillField.mustache +0 -16
- package/docs/webapi/flushNetworkTraffics.mustache +0 -5
- package/docs/webapi/focus.mustache +0 -13
- package/docs/webapi/forceClick.mustache +0 -28
- package/docs/webapi/forceRightClick.mustache +0 -18
- package/docs/webapi/grabAllWindowHandles.mustache +0 -7
- package/docs/webapi/grabAttributeFrom.mustache +0 -10
- package/docs/webapi/grabAttributeFromAll.mustache +0 -9
- package/docs/webapi/grabBrowserLogs.mustache +0 -9
- package/docs/webapi/grabCookie.mustache +0 -11
- package/docs/webapi/grabCssPropertyFrom.mustache +0 -11
- package/docs/webapi/grabCssPropertyFromAll.mustache +0 -10
- package/docs/webapi/grabCurrentUrl.mustache +0 -9
- package/docs/webapi/grabCurrentWindowHandle.mustache +0 -6
- package/docs/webapi/grabDataFromPerformanceTiming.mustache +0 -20
- package/docs/webapi/grabElementBoundingRect.mustache +0 -20
- package/docs/webapi/grabGeoLocation.mustache +0 -8
- package/docs/webapi/grabHTMLFrom.mustache +0 -10
- package/docs/webapi/grabHTMLFromAll.mustache +0 -9
- package/docs/webapi/grabNumberOfOpenTabs.mustache +0 -8
- package/docs/webapi/grabNumberOfVisibleElements.mustache +0 -9
- package/docs/webapi/grabPageScrollPosition.mustache +0 -8
- package/docs/webapi/grabPopupText.mustache +0 -5
- package/docs/webapi/grabRecordedNetworkTraffics.mustache +0 -10
- package/docs/webapi/grabSource.mustache +0 -8
- package/docs/webapi/grabTextFrom.mustache +0 -10
- package/docs/webapi/grabTextFromAll.mustache +0 -9
- package/docs/webapi/grabTitle.mustache +0 -8
- package/docs/webapi/grabValueFrom.mustache +0 -9
- package/docs/webapi/grabValueFromAll.mustache +0 -8
- package/docs/webapi/grabWebElement.mustache +0 -9
- package/docs/webapi/grabWebElements.mustache +0 -9
- package/docs/webapi/moveCursorTo.mustache +0 -12
- package/docs/webapi/openNewTab.mustache +0 -7
- package/docs/webapi/pressKey.mustache +0 -12
- package/docs/webapi/pressKeyDown.mustache +0 -12
- package/docs/webapi/pressKeyUp.mustache +0 -12
- package/docs/webapi/pressKeyWithKeyNormalization.mustache +0 -60
- package/docs/webapi/refreshPage.mustache +0 -6
- package/docs/webapi/resizeWindow.mustache +0 -6
- package/docs/webapi/rightClick.mustache +0 -14
- package/docs/webapi/saveElementScreenshot.mustache +0 -10
- package/docs/webapi/saveScreenshot.mustache +0 -12
- package/docs/webapi/say.mustache +0 -10
- package/docs/webapi/scrollIntoView.mustache +0 -11
- package/docs/webapi/scrollPageToBottom.mustache +0 -6
- package/docs/webapi/scrollPageToTop.mustache +0 -6
- package/docs/webapi/scrollTo.mustache +0 -12
- package/docs/webapi/see.mustache +0 -11
- package/docs/webapi/seeAttributesOnElements.mustache +0 -9
- package/docs/webapi/seeCheckboxIsChecked.mustache +0 -10
- package/docs/webapi/seeCookie.mustache +0 -8
- package/docs/webapi/seeCssPropertiesOnElements.mustache +0 -9
- package/docs/webapi/seeCurrentUrlEquals.mustache +0 -11
- package/docs/webapi/seeElement.mustache +0 -8
- package/docs/webapi/seeElementInDOM.mustache +0 -8
- package/docs/webapi/seeInCurrentUrl.mustache +0 -8
- package/docs/webapi/seeInField.mustache +0 -12
- package/docs/webapi/seeInPopup.mustache +0 -8
- package/docs/webapi/seeInSource.mustache +0 -7
- package/docs/webapi/seeInTitle.mustache +0 -8
- package/docs/webapi/seeNumberOfElements.mustache +0 -11
- package/docs/webapi/seeNumberOfVisibleElements.mustache +0 -10
- package/docs/webapi/seeTextEquals.mustache +0 -9
- package/docs/webapi/seeTitleEquals.mustache +0 -8
- package/docs/webapi/seeTraffic.mustache +0 -36
- package/docs/webapi/selectOption.mustache +0 -21
- package/docs/webapi/setCookie.mustache +0 -16
- package/docs/webapi/setGeoLocation.mustache +0 -12
- package/docs/webapi/startRecordingTraffic.mustache +0 -8
- package/docs/webapi/startRecordingWebSocketMessages.mustache +0 -8
- package/docs/webapi/stopRecordingTraffic.mustache +0 -5
- package/docs/webapi/stopRecordingWebSocketMessages.mustache +0 -7
- package/docs/webapi/switchTo.mustache +0 -9
- package/docs/webapi/switchToNextTab.mustache +0 -10
- package/docs/webapi/switchToPreviousTab.mustache +0 -10
- package/docs/webapi/type.mustache +0 -21
- package/docs/webapi/uncheckOption.mustache +0 -13
- package/docs/webapi/wait.mustache +0 -8
- package/docs/webapi/waitForClickable.mustache +0 -11
- package/docs/webapi/waitForCookie.mustache +0 -9
- package/docs/webapi/waitForDetached.mustache +0 -10
- package/docs/webapi/waitForDisabled.mustache +0 -6
- package/docs/webapi/waitForElement.mustache +0 -11
- package/docs/webapi/waitForEnabled.mustache +0 -6
- package/docs/webapi/waitForFunction.mustache +0 -17
- package/docs/webapi/waitForInvisible.mustache +0 -10
- package/docs/webapi/waitForNumberOfTabs.mustache +0 -9
- package/docs/webapi/waitForText.mustache +0 -13
- package/docs/webapi/waitForValue.mustache +0 -10
- package/docs/webapi/waitForVisible.mustache +0 -10
- package/docs/webapi/waitInUrl.mustache +0 -9
- package/docs/webapi/waitNumberOfVisibleElements.mustache +0 -10
- package/docs/webapi/waitToHide.mustache +0 -10
- package/docs/webapi/waitUrlEquals.mustache +0 -10
- package/lib/helper/AI.js +0 -214
- package/lib/helper/Mochawesome.js +0 -96
- package/lib/helper/extras/PlaywrightReactVueLocator.js +0 -52
- package/lib/helper/extras/React.js +0 -65
- package/lib/listener/enhancedGlobalRetry.js +0 -110
- package/lib/plugin/enhancedRetryFailedStep.js +0 -99
- package/lib/plugin/htmlReporter.js +0 -3648
- package/lib/plugin/stepByStepReport.js +0 -427
- package/lib/plugin/subtitles.js +0 -89
- package/lib/retryCoordinator.js +0 -207
package/lib/utils/typescript.js
CHANGED
|
@@ -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
|
|
122
|
-
const importRegex = /from\s+['"](
|
|
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
|
|
135
|
-
let importedPath =
|
|
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
|
-
//
|
|
146
|
-
|
|
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 .
|
|
152
|
-
const
|
|
153
|
-
if (fs.existsSync(
|
|
154
|
-
|
|
155
|
-
|
|
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+['"](
|
|
265
|
+
/from\s+['"]([^'"]+?)['"]/g,
|
|
169
266
|
(match, importPath) => {
|
|
170
|
-
let resolvedPath =
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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:
|
|
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(
|
|
339
|
+
return path.resolve(store.outputDir, fileName)
|
|
321
340
|
}
|
|
322
|
-
return path.resolve(
|
|
341
|
+
return path.resolve(store.codeceptDir, fileName)
|
|
323
342
|
}
|
|
324
343
|
|
|
325
344
|
export const relativeDir = function (fileName) {
|
|
326
|
-
return fileName.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
|