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/container.js
CHANGED
|
@@ -5,7 +5,7 @@ import debugModule from 'debug'
|
|
|
5
5
|
const debug = debugModule('codeceptjs:container')
|
|
6
6
|
import { MetaStep } from './step.js'
|
|
7
7
|
import { methodsOfObject, fileExists, isFunction, isAsyncFunction, installedLocally, deepMerge } from './utils.js'
|
|
8
|
-
import { transpileTypeScript, cleanupTempFiles } from './utils/typescript.js'
|
|
8
|
+
import { transpileTypeScript, cleanupTempFiles, fixErrorStack } from './utils/typescript.js'
|
|
9
9
|
import Translation from './translation.js'
|
|
10
10
|
import MochaFactory from './mocha/factory.js'
|
|
11
11
|
import recorder from './recorder.js'
|
|
@@ -15,9 +15,15 @@ import store from './store.js'
|
|
|
15
15
|
import Result from './result.js'
|
|
16
16
|
import ai from './ai.js'
|
|
17
17
|
import actorFactory from './actor.js'
|
|
18
|
+
import Config from './config.js'
|
|
18
19
|
|
|
19
20
|
let asyncHelperPromise
|
|
20
21
|
|
|
22
|
+
let beforeCalledSet = new Set()
|
|
23
|
+
|
|
24
|
+
export function getBeforeCalledSet() { return beforeCalledSet }
|
|
25
|
+
export function resetBeforeCalledSet() { beforeCalledSet = new Set() }
|
|
26
|
+
|
|
21
27
|
let container = {
|
|
22
28
|
helpers: {},
|
|
23
29
|
support: {},
|
|
@@ -34,6 +40,7 @@ let container = {
|
|
|
34
40
|
/** @type {Result | null} */
|
|
35
41
|
result: null,
|
|
36
42
|
sharedKeys: new Set(), // Track keys shared via share() function
|
|
43
|
+
tsFileMapping: null, // TypeScript file mapping for error stack fixing
|
|
37
44
|
}
|
|
38
45
|
|
|
39
46
|
/**
|
|
@@ -88,7 +95,7 @@ class Container {
|
|
|
88
95
|
container.support.I = mod
|
|
89
96
|
}
|
|
90
97
|
} catch (e) {
|
|
91
|
-
throw
|
|
98
|
+
throw e
|
|
92
99
|
}
|
|
93
100
|
} else {
|
|
94
101
|
// Create default actor - this sets up the callback in asyncHelperPromise
|
|
@@ -115,6 +122,18 @@ class Container {
|
|
|
115
122
|
// Wait for all async helpers to finish loading and populate the actor
|
|
116
123
|
await asyncHelperPromise
|
|
117
124
|
|
|
125
|
+
// Plugins may have registered Config hooks during their boot. Run anything
|
|
126
|
+
// that hasn't been applied yet and re-feed the mutated helper config to the
|
|
127
|
+
// already-instantiated helpers.
|
|
128
|
+
if (Config.runPendingHooks(config)) {
|
|
129
|
+
for (const name of Object.keys(container.helpers)) {
|
|
130
|
+
const helper = container.helpers[name]
|
|
131
|
+
if (helper && typeof helper._setConfig === 'function' && config.helpers && config.helpers[name]) {
|
|
132
|
+
helper._setConfig(config.helpers[name])
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
118
137
|
if (opts && opts.ai) ai.enable(config.ai) // enable AI Assistant
|
|
119
138
|
if (config.gherkin) await loadGherkinStepsAsync(config.gherkin.steps || [])
|
|
120
139
|
if (opts && typeof opts.timeouts === 'boolean') store.timeouts = opts.timeouts
|
|
@@ -149,10 +168,23 @@ class Container {
|
|
|
149
168
|
if (!name) {
|
|
150
169
|
return container.proxySupport
|
|
151
170
|
}
|
|
152
|
-
|
|
171
|
+
if (typeof container.support[name] === 'function') {
|
|
172
|
+
return container.support[name]
|
|
173
|
+
}
|
|
153
174
|
return container.proxySupport[name]
|
|
154
175
|
}
|
|
155
176
|
|
|
177
|
+
/**
|
|
178
|
+
* Get raw (non-proxied) support objects for direct access.
|
|
179
|
+
* Used by listeners to call lifecycle hooks without MetaStep wrapping.
|
|
180
|
+
*
|
|
181
|
+
* @api
|
|
182
|
+
* @returns {object}
|
|
183
|
+
*/
|
|
184
|
+
static supportObjects() {
|
|
185
|
+
return container.support
|
|
186
|
+
}
|
|
187
|
+
|
|
156
188
|
/**
|
|
157
189
|
* Get all helpers or get a helper by name
|
|
158
190
|
*
|
|
@@ -176,6 +208,15 @@ class Container {
|
|
|
176
208
|
return container.translation
|
|
177
209
|
}
|
|
178
210
|
|
|
211
|
+
/**
|
|
212
|
+
* Get TypeScript file mapping for error stack fixing
|
|
213
|
+
*
|
|
214
|
+
* @api
|
|
215
|
+
*/
|
|
216
|
+
static tsFileMapping() {
|
|
217
|
+
return store.tsFileMapping
|
|
218
|
+
}
|
|
219
|
+
|
|
179
220
|
/**
|
|
180
221
|
* Get Mocha instance
|
|
181
222
|
*
|
|
@@ -398,20 +439,66 @@ async function requireHelperFromModule(helperName, config, HelperClass) {
|
|
|
398
439
|
throw err
|
|
399
440
|
}
|
|
400
441
|
} else {
|
|
442
|
+
// Handle TypeScript files
|
|
443
|
+
let importPath = moduleName
|
|
444
|
+
let tempJsFile = null
|
|
445
|
+
let fileMapping = null
|
|
446
|
+
const ext = path.extname(moduleName)
|
|
447
|
+
|
|
448
|
+
if (ext === '.ts') {
|
|
449
|
+
try {
|
|
450
|
+
// Use the TypeScript transpilation utility
|
|
451
|
+
const typescript = await import('typescript')
|
|
452
|
+
const { tempFile, allTempFiles, fileMapping: mapping } = await transpileTypeScript(importPath, typescript)
|
|
453
|
+
|
|
454
|
+
debug(`Transpiled TypeScript helper: ${importPath} -> ${tempFile}`)
|
|
455
|
+
|
|
456
|
+
importPath = tempFile
|
|
457
|
+
tempJsFile = allTempFiles
|
|
458
|
+
fileMapping = mapping
|
|
459
|
+
// Store file mapping in container for runtime error fixing (merge with existing)
|
|
460
|
+
if (!store.tsFileMapping) {
|
|
461
|
+
store.tsFileMapping = new Map()
|
|
462
|
+
}
|
|
463
|
+
for (const [key, value] of mapping.entries()) {
|
|
464
|
+
store.tsFileMapping.set(key, value)
|
|
465
|
+
}
|
|
466
|
+
} catch (tsError) {
|
|
467
|
+
throw new Error(`Failed to load TypeScript helper ${importPath}: ${tsError.message}. Make sure 'typescript' package is installed.`)
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
|
|
401
471
|
// check if the new syntax export default HelperName is used and loads the Helper, otherwise loads the module that used old syntax export = HelperName.
|
|
402
472
|
try {
|
|
403
473
|
// Try dynamic import for both CommonJS and ESM modules
|
|
404
|
-
const mod = await import(
|
|
474
|
+
const mod = await import(importPath)
|
|
405
475
|
if (!mod && !mod.default) {
|
|
406
476
|
throw new Error(`Helper module '${moduleName}' was not found. Make sure you have installed the package correctly.`)
|
|
407
477
|
}
|
|
408
478
|
HelperClass = mod.default || mod
|
|
479
|
+
|
|
480
|
+
// Clean up temp files if created
|
|
481
|
+
if (tempJsFile) {
|
|
482
|
+
const filesToClean = Array.isArray(tempJsFile) ? tempJsFile : [tempJsFile]
|
|
483
|
+
cleanupTempFiles(filesToClean)
|
|
484
|
+
}
|
|
409
485
|
} catch (err) {
|
|
486
|
+
// Fix error stack to point to original .ts files
|
|
487
|
+
if (fileMapping) {
|
|
488
|
+
fixErrorStack(err, fileMapping)
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
// Clean up temp files before rethrowing
|
|
492
|
+
if (tempJsFile) {
|
|
493
|
+
const filesToClean = Array.isArray(tempJsFile) ? tempJsFile : [tempJsFile]
|
|
494
|
+
cleanupTempFiles(filesToClean)
|
|
495
|
+
}
|
|
496
|
+
|
|
410
497
|
if (err.code === 'ERR_REQUIRE_ESM' || (err.message && err.message.includes('ES module'))) {
|
|
411
498
|
// This is an ESM module, use dynamic import
|
|
412
499
|
try {
|
|
413
500
|
const pathModule = await import('path')
|
|
414
|
-
const absolutePath = pathModule.default.resolve(
|
|
501
|
+
const absolutePath = pathModule.default.resolve(importPath)
|
|
415
502
|
const mod = await import(absolutePath)
|
|
416
503
|
HelperClass = mod.default || mod
|
|
417
504
|
debug(`helper ${helperName} loaded via ESM import`)
|
|
@@ -486,6 +573,19 @@ function createSupportObjects(config) {
|
|
|
486
573
|
let currentValue = currentObject[prop]
|
|
487
574
|
|
|
488
575
|
if (isFunction(currentValue) || isAsyncFunction(currentValue)) {
|
|
576
|
+
if (prop.toString().charAt(0) !== '_' && currentObject._before && !beforeCalledSet.has(name)) {
|
|
577
|
+
beforeCalledSet.add(name)
|
|
578
|
+
const originalValue = currentValue
|
|
579
|
+
const wrappedValue = async function (...args) {
|
|
580
|
+
await currentObject._before()
|
|
581
|
+
return originalValue.apply(currentObject, args)
|
|
582
|
+
}
|
|
583
|
+
const ms = new MetaStep(name, prop)
|
|
584
|
+
ms.setContext(currentObject)
|
|
585
|
+
debug(`metastep is created for ${name}.${prop.toString()}() (with _before)`)
|
|
586
|
+
return ms.run.bind(ms, asyncWrapper(wrappedValue))
|
|
587
|
+
}
|
|
588
|
+
|
|
489
589
|
const ms = new MetaStep(name, prop)
|
|
490
590
|
ms.setContext(currentObject)
|
|
491
591
|
if (isAsyncFunction(currentValue)) currentValue = asyncWrapper(currentValue)
|
|
@@ -544,6 +644,8 @@ function createSupportObjects(config) {
|
|
|
544
644
|
let value
|
|
545
645
|
if (container.sharedKeys.has(prop) && prop in container.support) {
|
|
546
646
|
value = container.support[prop]
|
|
647
|
+
} else if (prop in container.support && typeof container.support[prop] === 'function') {
|
|
648
|
+
value = container.support[prop]
|
|
547
649
|
} else {
|
|
548
650
|
value = lazyLoad(prop)
|
|
549
651
|
}
|
|
@@ -558,6 +660,9 @@ function createSupportObjects(config) {
|
|
|
558
660
|
if (container.sharedKeys.has(key) && key in container.support) {
|
|
559
661
|
return container.support[key]
|
|
560
662
|
}
|
|
663
|
+
if (key in container.support && typeof container.support[key] === 'function') {
|
|
664
|
+
return container.support[key]
|
|
665
|
+
}
|
|
561
666
|
return lazyLoad(key)
|
|
562
667
|
},
|
|
563
668
|
},
|
|
@@ -598,26 +703,55 @@ async function loadPluginFallback(modulePath, config) {
|
|
|
598
703
|
async function createPlugins(config, options = {}) {
|
|
599
704
|
const plugins = {}
|
|
600
705
|
|
|
601
|
-
const
|
|
706
|
+
const pluginOptionMap = new Map()
|
|
707
|
+
for (const token of (options.plugins || '').split(',').filter(Boolean)) {
|
|
708
|
+
const parts = token.split(':')
|
|
709
|
+
pluginOptionMap.set(parts[0], parts.slice(1))
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
for (const [name] of pluginOptionMap) {
|
|
713
|
+
if (!config[name]) config[name] = {}
|
|
714
|
+
}
|
|
715
|
+
|
|
602
716
|
for (const pluginName in config) {
|
|
603
717
|
if (!config[pluginName]) config[pluginName] = {}
|
|
604
|
-
|
|
718
|
+
const pluginConfig = config[pluginName]
|
|
719
|
+
const enabledByCli = pluginOptionMap.has(pluginName)
|
|
720
|
+
if (!pluginConfig.enabled && !enabledByCli) {
|
|
605
721
|
continue // plugin is disabled
|
|
606
722
|
}
|
|
723
|
+
|
|
724
|
+
if (enabledByCli && pluginOptionMap.get(pluginName).length > 0) {
|
|
725
|
+
pluginConfig._args = pluginOptionMap.get(pluginName)
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
// Generic workers gate:
|
|
729
|
+
// - runInWorker / runInWorkers controls plugin execution inside worker threads.
|
|
730
|
+
// - runInParent / runInMain can disable plugin in workers parent process.
|
|
731
|
+
const runInWorker = pluginConfig.runInWorker ?? pluginConfig.runInWorkers ?? (pluginName === 'testomatio' ? false : true)
|
|
732
|
+
const runInParent = pluginConfig.runInParent ?? pluginConfig.runInMain ?? true
|
|
733
|
+
|
|
734
|
+
if (options.child && !runInWorker) {
|
|
735
|
+
continue
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
if (!options.child && store.workerMode && !runInParent) {
|
|
739
|
+
continue
|
|
740
|
+
}
|
|
607
741
|
let module
|
|
608
742
|
try {
|
|
609
|
-
if (
|
|
610
|
-
module =
|
|
743
|
+
if (pluginConfig.require) {
|
|
744
|
+
module = pluginConfig.require
|
|
611
745
|
if (module.startsWith('.')) {
|
|
612
746
|
// local
|
|
613
|
-
module = path.resolve(
|
|
747
|
+
module = path.resolve(store.codeceptDir, module) // custom plugin
|
|
614
748
|
}
|
|
615
749
|
} else {
|
|
616
750
|
module = `./plugin/${pluginName}.js`
|
|
617
751
|
}
|
|
618
752
|
|
|
619
753
|
// Use async loading for all plugins (ESM and CJS)
|
|
620
|
-
plugins[pluginName] = await loadPluginAsync(module,
|
|
754
|
+
plugins[pluginName] = await loadPluginAsync(module, pluginConfig)
|
|
621
755
|
debug(`plugin ${pluginName} loaded via async import`)
|
|
622
756
|
} catch (err) {
|
|
623
757
|
throw new Error(`Could not load plugin ${pluginName} from module '${module}':\n${err.message}\n${err.stack}`)
|
|
@@ -627,12 +761,24 @@ async function createPlugins(config, options = {}) {
|
|
|
627
761
|
}
|
|
628
762
|
|
|
629
763
|
async function loadGherkinStepsAsync(paths) {
|
|
764
|
+
// Import BDD module to access step file tracking functions and step DSL
|
|
765
|
+
const bddModule = await import('./mocha/bdd.js')
|
|
766
|
+
|
|
630
767
|
global.Before = fn => event.dispatcher.on(event.test.started, fn)
|
|
631
768
|
global.After = fn => event.dispatcher.on(event.test.finished, fn)
|
|
632
769
|
global.Fail = fn => event.dispatcher.on(event.test.failed, fn)
|
|
633
770
|
|
|
634
|
-
//
|
|
635
|
-
|
|
771
|
+
// Scope-inject Given/When/Then/And while loading step files so they work
|
|
772
|
+
// with noGlobals: true. When noGlobals: false, globals.js has already set
|
|
773
|
+
// them as permanent globals — skip to avoid deleting them at the end.
|
|
774
|
+
const injectStepDsl = !!store.noGlobals
|
|
775
|
+
if (injectStepDsl) {
|
|
776
|
+
global.Given = bddModule.Given
|
|
777
|
+
global.When = bddModule.When
|
|
778
|
+
global.Then = bddModule.Then
|
|
779
|
+
global.And = bddModule.And
|
|
780
|
+
global.DefineParameterType = bddModule.defineParameterType
|
|
781
|
+
}
|
|
636
782
|
|
|
637
783
|
// If gherkin.steps is string, then this will iterate through that folder and send all step def js files to loadSupportObject
|
|
638
784
|
// If gherkin.steps is Array, it will go the old way
|
|
@@ -645,7 +791,7 @@ async function loadGherkinStepsAsync(paths) {
|
|
|
645
791
|
bddModule.clearCurrentStepFile()
|
|
646
792
|
}
|
|
647
793
|
} else {
|
|
648
|
-
const folderPath = paths.startsWith('.') ? normalizeAndJoin(
|
|
794
|
+
const folderPath = paths.startsWith('.') ? normalizeAndJoin(store.codeceptDir, paths) : ''
|
|
649
795
|
if (folderPath !== '') {
|
|
650
796
|
const files = globSync(folderPath)
|
|
651
797
|
for (const file of files) {
|
|
@@ -660,6 +806,13 @@ async function loadGherkinStepsAsync(paths) {
|
|
|
660
806
|
delete global.Before
|
|
661
807
|
delete global.After
|
|
662
808
|
delete global.Fail
|
|
809
|
+
if (injectStepDsl) {
|
|
810
|
+
delete global.Given
|
|
811
|
+
delete global.When
|
|
812
|
+
delete global.Then
|
|
813
|
+
delete global.And
|
|
814
|
+
delete global.DefineParameterType
|
|
815
|
+
}
|
|
663
816
|
}
|
|
664
817
|
|
|
665
818
|
function loadGherkinSteps(paths) {
|
|
@@ -693,12 +846,13 @@ async function loadSupportObject(modulePath, supportObjectName) {
|
|
|
693
846
|
}
|
|
694
847
|
}
|
|
695
848
|
if (typeof modulePath === 'string' && modulePath.charAt(0) === '.') {
|
|
696
|
-
modulePath = path.join(
|
|
849
|
+
modulePath = path.join(store.codeceptDir, modulePath)
|
|
697
850
|
}
|
|
698
851
|
try {
|
|
699
852
|
// Use dynamic import for both ESM and CJS modules
|
|
700
853
|
let importPath = modulePath
|
|
701
854
|
let tempJsFile = null
|
|
855
|
+
let fileMapping = null
|
|
702
856
|
|
|
703
857
|
if (typeof importPath === 'string') {
|
|
704
858
|
const ext = path.extname(importPath)
|
|
@@ -708,7 +862,7 @@ async function loadSupportObject(modulePath, supportObjectName) {
|
|
|
708
862
|
try {
|
|
709
863
|
// Use the TypeScript transpilation utility
|
|
710
864
|
const typescript = await import('typescript')
|
|
711
|
-
const { tempFile, allTempFiles } = await transpileTypeScript(importPath, typescript)
|
|
865
|
+
const { tempFile, allTempFiles, fileMapping: mapping } = await transpileTypeScript(importPath, typescript)
|
|
712
866
|
|
|
713
867
|
debug(`Transpiled TypeScript file: ${importPath} -> ${tempFile}`)
|
|
714
868
|
|
|
@@ -716,6 +870,14 @@ async function loadSupportObject(modulePath, supportObjectName) {
|
|
|
716
870
|
importPath = tempFile
|
|
717
871
|
// Store temp files list in a way that cleanup can access them
|
|
718
872
|
tempJsFile = allTempFiles
|
|
873
|
+
fileMapping = mapping
|
|
874
|
+
// Store file mapping in container for runtime error fixing (merge with existing)
|
|
875
|
+
if (!container.tsFileMapping) {
|
|
876
|
+
container.tsFileMapping = new Map()
|
|
877
|
+
}
|
|
878
|
+
for (const [key, value] of mapping.entries()) {
|
|
879
|
+
container.tsFileMapping.set(key, value)
|
|
880
|
+
}
|
|
719
881
|
} catch (tsError) {
|
|
720
882
|
throw new Error(`Failed to load TypeScript file ${importPath}: ${tsError.message}. Make sure 'typescript' package is installed.`)
|
|
721
883
|
}
|
|
@@ -729,6 +891,11 @@ async function loadSupportObject(modulePath, supportObjectName) {
|
|
|
729
891
|
try {
|
|
730
892
|
obj = await import(importPath)
|
|
731
893
|
} catch (importError) {
|
|
894
|
+
// Fix error stack to point to original .ts files
|
|
895
|
+
if (fileMapping) {
|
|
896
|
+
fixErrorStack(importError, fileMapping)
|
|
897
|
+
}
|
|
898
|
+
|
|
732
899
|
// Clean up temp files if created before rethrowing
|
|
733
900
|
if (tempJsFile) {
|
|
734
901
|
const filesToClean = Array.isArray(tempJsFile) ? tempJsFile : [tempJsFile]
|
|
@@ -777,7 +944,9 @@ async function loadSupportObject(modulePath, supportObjectName) {
|
|
|
777
944
|
|
|
778
945
|
throw new Error(`Support object "${supportObjectName}" should be an object, class, or function, but got ${typeof actualObj}`)
|
|
779
946
|
} catch (err) {
|
|
780
|
-
|
|
947
|
+
const newErr = new Error(`Could not include object ${supportObjectName} from module '${modulePath}': ${err.message}`)
|
|
948
|
+
newErr.stack = err.stack
|
|
949
|
+
throw newErr
|
|
781
950
|
}
|
|
782
951
|
}
|
|
783
952
|
|
|
@@ -801,7 +970,7 @@ async function loadTranslation(locale, vocabularies) {
|
|
|
801
970
|
const langs = await Translation.getLangs()
|
|
802
971
|
if (langs[locale]) {
|
|
803
972
|
translation = new Translation(langs[locale])
|
|
804
|
-
} else if (fileExists(path.join(
|
|
973
|
+
} else if (fileExists(path.join(store.codeceptDir, locale))) {
|
|
805
974
|
// get from a provided file instead
|
|
806
975
|
translation = Translation.createDefault()
|
|
807
976
|
translation.loadVocabulary(locale)
|
|
@@ -818,7 +987,7 @@ function getHelperModuleName(helperName, config) {
|
|
|
818
987
|
// classical require
|
|
819
988
|
if (config[helperName].require) {
|
|
820
989
|
if (config[helperName].require.startsWith('.')) {
|
|
821
|
-
let helperPath = path.resolve(
|
|
990
|
+
let helperPath = path.resolve(store.codeceptDir, config[helperName].require)
|
|
822
991
|
// Add .js extension if not present for ESM compatibility
|
|
823
992
|
if (!path.extname(helperPath)) {
|
|
824
993
|
helperPath += '.js'
|
package/lib/effects.js
CHANGED
|
@@ -4,6 +4,7 @@ import store from './store.js'
|
|
|
4
4
|
import event from './event.js'
|
|
5
5
|
import container from './container.js'
|
|
6
6
|
import MetaStep from './step/meta.js'
|
|
7
|
+
import { empty } from './assert/empty.js'
|
|
7
8
|
import { isAsyncFunction } from './utils.js'
|
|
8
9
|
|
|
9
10
|
/**
|
|
@@ -111,6 +112,11 @@ class WithinStep extends MetaStep {
|
|
|
111
112
|
*
|
|
112
113
|
* @throws Will handle errors that occur during the callback execution. Errors are logged and attached as notes to the test.
|
|
113
114
|
*/
|
|
115
|
+
let hopeThatFailures = []
|
|
116
|
+
event.dispatcher.on(event.test.before, () => {
|
|
117
|
+
hopeThatFailures = []
|
|
118
|
+
})
|
|
119
|
+
|
|
114
120
|
async function hopeThat(callback) {
|
|
115
121
|
if (store.dryRun) return
|
|
116
122
|
const sessionName = 'hopeThat'
|
|
@@ -131,6 +137,7 @@ async function hopeThat(callback) {
|
|
|
131
137
|
result = false
|
|
132
138
|
const msg = err.inspect ? err.inspect() : err.toString()
|
|
133
139
|
output.debug(`Unsuccessful assertion > ${msg}`)
|
|
140
|
+
hopeThatFailures.push(msg)
|
|
134
141
|
event.dispatcher.once(event.test.finished, test => {
|
|
135
142
|
if (!test.notes) test.notes = []
|
|
136
143
|
test.notes.push({ type: 'conditionalError', text: msg })
|
|
@@ -153,6 +160,16 @@ async function hopeThat(callback) {
|
|
|
153
160
|
)
|
|
154
161
|
}
|
|
155
162
|
|
|
163
|
+
/**
|
|
164
|
+
* Asserts that no `hopeThat` soft assertion has failed in the current test.
|
|
165
|
+
* Call once at the end of a scenario to fail it when any soft assertion failed.
|
|
166
|
+
*/
|
|
167
|
+
hopeThat.noErrors = function () {
|
|
168
|
+
const failures = hopeThatFailures
|
|
169
|
+
hopeThatFailures = []
|
|
170
|
+
empty('soft assertions').assert(failures)
|
|
171
|
+
}
|
|
172
|
+
|
|
156
173
|
/**
|
|
157
174
|
* A CodeceptJS utility function to retry a step or callback multiple times with a specified polling interval.
|
|
158
175
|
*
|