codeceptjs 4.0.2-beta.9 → 4.0.3
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 +15 -2
- 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 +6 -7
- 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 -1
- package/lib/command/run-workers.js +2 -14
- package/lib/command/run.js +3 -17
- package/lib/command/utils.js +14 -0
- package/lib/command/workers/runTests.js +84 -41
- package/lib/config.js +96 -18
- package/lib/container.js +115 -17
- package/lib/effects.js +17 -0
- package/lib/element/WebElement.js +253 -6
- 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 +33 -12
- package/lib/helper/FileSystem.js +3 -2
- package/lib/helper/GraphQLDataFactory.js +2 -1
- package/lib/helper/Playwright.js +358 -467
- package/lib/helper/Puppeteer.js +335 -192
- 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 +4 -1
- 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/asyncWrapper.js +2 -2
- package/lib/mocha/cli.js +19 -1
- package/lib/mocha/factory.js +11 -1
- package/lib/mocha/gherkin.js +5 -6
- package/lib/mocha/inject.js +1 -1
- package/lib/mocha/scenarioConfig.js +2 -1
- package/lib/mocha/ui.js +5 -6
- 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 +10 -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 +5 -5
- package/lib/store.js +72 -3
- package/lib/translation.js +2 -1
- package/lib/utils/loaderCheck.js +28 -0
- 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 +188 -23
- package/lib/utils.js +77 -3
- package/lib/workers.js +65 -40
- package/package.json +37 -32
- package/typings/index.d.ts +119 -8
- package/typings/promiseBasedTypes.d.ts +3158 -6065
- package/typings/types.d.ts +3453 -6494
- 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/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
|
*
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import assert from 'assert'
|
|
2
|
+
import { simplifyHtmlElement } from '../html.js'
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
5
|
* Unified WebElement class that wraps native element instances from different helpers
|
|
@@ -14,10 +15,13 @@ class WebElement {
|
|
|
14
15
|
_detectHelperType(helper) {
|
|
15
16
|
if (!helper) return 'unknown'
|
|
16
17
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
18
|
+
let ctor = helper.constructor
|
|
19
|
+
while (ctor && ctor.name) {
|
|
20
|
+
if (ctor.name === 'Playwright') return 'playwright'
|
|
21
|
+
if (ctor.name === 'WebDriver') return 'webdriver'
|
|
22
|
+
if (ctor.name === 'Puppeteer') return 'puppeteer'
|
|
23
|
+
ctor = Object.getPrototypeOf(ctor)
|
|
24
|
+
}
|
|
21
25
|
|
|
22
26
|
return 'unknown'
|
|
23
27
|
}
|
|
@@ -81,6 +85,10 @@ class WebElement {
|
|
|
81
85
|
async getProperty(name) {
|
|
82
86
|
switch (this.helperType) {
|
|
83
87
|
case 'playwright':
|
|
88
|
+
// For Locator objects, use inputValue() for the 'value' property
|
|
89
|
+
if (name === 'value' && this.element.inputValue) {
|
|
90
|
+
return this.element.inputValue()
|
|
91
|
+
}
|
|
84
92
|
return this.element.evaluate((el, propName) => el[propName], name)
|
|
85
93
|
case 'webdriver':
|
|
86
94
|
return this.element.getProperty(name)
|
|
@@ -236,16 +244,149 @@ class WebElement {
|
|
|
236
244
|
async type(text, options = {}) {
|
|
237
245
|
switch (this.helperType) {
|
|
238
246
|
case 'playwright':
|
|
247
|
+
// Playwright Locator objects use fill() instead of type()
|
|
248
|
+
if (this.element.fill) {
|
|
249
|
+
return this.element.fill(text, options)
|
|
250
|
+
}
|
|
239
251
|
return this.element.type(text, options)
|
|
240
252
|
case 'webdriver':
|
|
241
253
|
return this.element.setValue(text)
|
|
242
254
|
case 'puppeteer':
|
|
255
|
+
await this.element.evaluate(el => { el.value = '' })
|
|
243
256
|
return this.element.type(text, options)
|
|
244
257
|
default:
|
|
245
258
|
throw new Error(`Unsupported helper type: ${this.helperType}`)
|
|
246
259
|
}
|
|
247
260
|
}
|
|
248
261
|
|
|
262
|
+
/**
|
|
263
|
+
* Run a function in the browser with this element as the first argument.
|
|
264
|
+
* @param {Function} fn Browser-side function. Receives the element, then extra args.
|
|
265
|
+
* @param {...any} args Additional arguments passed to the function
|
|
266
|
+
* @returns {Promise<any>} Value returned by fn
|
|
267
|
+
*/
|
|
268
|
+
async evaluate(fn, ...args) {
|
|
269
|
+
switch (this.helperType) {
|
|
270
|
+
case 'playwright':
|
|
271
|
+
case 'puppeteer':
|
|
272
|
+
return this.element.evaluate(fn, ...args)
|
|
273
|
+
case 'webdriver':
|
|
274
|
+
return this.helper.executeScript(fn, this.element, ...args)
|
|
275
|
+
default:
|
|
276
|
+
throw new Error(`Unsupported helper type: ${this.helperType}`)
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
/**
|
|
281
|
+
* Focus the element.
|
|
282
|
+
* @returns {Promise<void>}
|
|
283
|
+
*/
|
|
284
|
+
async focus() {
|
|
285
|
+
switch (this.helperType) {
|
|
286
|
+
case 'playwright':
|
|
287
|
+
return this.element.focus()
|
|
288
|
+
case 'puppeteer':
|
|
289
|
+
if (this.element.focus) return this.element.focus()
|
|
290
|
+
return this.element.evaluate(el => el.focus())
|
|
291
|
+
case 'webdriver':
|
|
292
|
+
return this.helper.executeScript(el => el.focus(), this.element)
|
|
293
|
+
default:
|
|
294
|
+
throw new Error(`Unsupported helper type: ${this.helperType}`)
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
/**
|
|
299
|
+
* Type characters via the page/browser keyboard into the focused element.
|
|
300
|
+
* Unlike `type()`, this does not call `.fill()`/`.setValue()`, so it works
|
|
301
|
+
* with contenteditable nodes, iframe bodies, and editor-owned hidden textareas.
|
|
302
|
+
* @param {string} text Text to send
|
|
303
|
+
* @param {Object} [options] Options (e.g. `{ delay }`)
|
|
304
|
+
* @returns {Promise<void>}
|
|
305
|
+
*/
|
|
306
|
+
async typeText(text, options = {}) {
|
|
307
|
+
const s = String(text)
|
|
308
|
+
switch (this.helperType) {
|
|
309
|
+
case 'playwright':
|
|
310
|
+
case 'puppeteer':
|
|
311
|
+
return this.helper.page.keyboard.type(s, options)
|
|
312
|
+
case 'webdriver': {
|
|
313
|
+
const ENTER = '\uE007'
|
|
314
|
+
const parts = s.split('\n')
|
|
315
|
+
for (let i = 0; i < parts.length; i++) {
|
|
316
|
+
if (parts[i]) await this.helper.browser.keys(parts[i])
|
|
317
|
+
if (i < parts.length - 1) await this.helper.browser.keys(ENTER)
|
|
318
|
+
}
|
|
319
|
+
return
|
|
320
|
+
}
|
|
321
|
+
default:
|
|
322
|
+
throw new Error(`Unsupported helper type: ${this.helperType}`)
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
/**
|
|
327
|
+
* Select all content in the focused field and delete it via keyboard input.
|
|
328
|
+
* Sends Ctrl+A and Meta+A (so it works across platforms) followed by Backspace.
|
|
329
|
+
* @returns {Promise<void>}
|
|
330
|
+
*/
|
|
331
|
+
async selectAllAndDelete() {
|
|
332
|
+
switch (this.helperType) {
|
|
333
|
+
case 'playwright':
|
|
334
|
+
await this.helper.page.keyboard.press('Control+a').catch(() => {})
|
|
335
|
+
await this.helper.page.keyboard.press('Meta+a').catch(() => {})
|
|
336
|
+
await this.helper.page.keyboard.press('Backspace')
|
|
337
|
+
return
|
|
338
|
+
case 'puppeteer':
|
|
339
|
+
for (const mod of ['Control', 'Meta']) {
|
|
340
|
+
try {
|
|
341
|
+
await this.helper.page.keyboard.down(mod)
|
|
342
|
+
await this.helper.page.keyboard.press('KeyA')
|
|
343
|
+
await this.helper.page.keyboard.up(mod)
|
|
344
|
+
} catch (e) {}
|
|
345
|
+
}
|
|
346
|
+
await this.helper.page.keyboard.press('Backspace')
|
|
347
|
+
return
|
|
348
|
+
case 'webdriver': {
|
|
349
|
+
const b = this.helper.browser
|
|
350
|
+
await b.keys(['Control', 'a']).catch(() => {})
|
|
351
|
+
await b.keys(['Meta', 'a']).catch(() => {})
|
|
352
|
+
await b.keys(['Backspace'])
|
|
353
|
+
return
|
|
354
|
+
}
|
|
355
|
+
default:
|
|
356
|
+
throw new Error(`Unsupported helper type: ${this.helperType}`)
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
/**
|
|
361
|
+
* Treat this element as an iframe; invoke `fn` with a WebElement wrapping
|
|
362
|
+
* the iframe body. For WebDriver this switches the browser into the frame
|
|
363
|
+
* for the duration of the callback and switches back on exit.
|
|
364
|
+
* @param {(body: WebElement) => Promise<any>} fn
|
|
365
|
+
* @returns {Promise<any>} Return value of fn
|
|
366
|
+
*/
|
|
367
|
+
async inIframe(fn) {
|
|
368
|
+
switch (this.helperType) {
|
|
369
|
+
case 'playwright':
|
|
370
|
+
case 'puppeteer': {
|
|
371
|
+
const frame = await this.element.contentFrame()
|
|
372
|
+
const body = await frame.$('body')
|
|
373
|
+
return fn(new WebElement(body, this.helper))
|
|
374
|
+
}
|
|
375
|
+
case 'webdriver': {
|
|
376
|
+
const browser = this.helper.browser
|
|
377
|
+
await browser.switchFrame(this.element)
|
|
378
|
+
try {
|
|
379
|
+
const body = await browser.$('body')
|
|
380
|
+
return await fn(new WebElement(body, this.helper))
|
|
381
|
+
} finally {
|
|
382
|
+
await browser.switchFrame(null)
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
default:
|
|
386
|
+
throw new Error(`Unsupported helper type: ${this.helperType}`)
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
|
|
249
390
|
/**
|
|
250
391
|
* Find first child element matching the locator
|
|
251
392
|
* @param {string|Object} locator Element locator
|
|
@@ -256,7 +397,18 @@ class WebElement {
|
|
|
256
397
|
|
|
257
398
|
switch (this.helperType) {
|
|
258
399
|
case 'playwright':
|
|
259
|
-
|
|
400
|
+
// Playwright Locator objects use locator() method
|
|
401
|
+
if (this.element.locator) {
|
|
402
|
+
const childLocator = this.element.locator(this._normalizeLocator(locator))
|
|
403
|
+
// Get the element handle from the locator
|
|
404
|
+
try {
|
|
405
|
+
childElement = await childLocator.elementHandle()
|
|
406
|
+
} catch (e) {
|
|
407
|
+
return null
|
|
408
|
+
}
|
|
409
|
+
} else {
|
|
410
|
+
childElement = await this.element.$(this._normalizeLocator(locator))
|
|
411
|
+
}
|
|
260
412
|
break
|
|
261
413
|
case 'webdriver':
|
|
262
414
|
try {
|
|
@@ -285,7 +437,14 @@ class WebElement {
|
|
|
285
437
|
|
|
286
438
|
switch (this.helperType) {
|
|
287
439
|
case 'playwright':
|
|
288
|
-
|
|
440
|
+
// Playwright Locator objects use locator() method
|
|
441
|
+
if (this.element.locator) {
|
|
442
|
+
const childLocator = this.element.locator(this._normalizeLocator(locator))
|
|
443
|
+
// Get all element handles from the locator
|
|
444
|
+
childElements = await childLocator.elementHandles()
|
|
445
|
+
} else {
|
|
446
|
+
childElements = await this.element.$$(this._normalizeLocator(locator))
|
|
447
|
+
}
|
|
289
448
|
break
|
|
290
449
|
case 'webdriver':
|
|
291
450
|
childElements = await this.element.$$(this._normalizeLocator(locator))
|
|
@@ -306,6 +465,94 @@ class WebElement {
|
|
|
306
465
|
* @returns {string} Normalized CSS selector
|
|
307
466
|
* @private
|
|
308
467
|
*/
|
|
468
|
+
async toAbsoluteXPath() {
|
|
469
|
+
const xpathFn = (el) => {
|
|
470
|
+
const parts = []
|
|
471
|
+
let current = el
|
|
472
|
+
while (current && current.nodeType === Node.ELEMENT_NODE) {
|
|
473
|
+
let index = 0
|
|
474
|
+
let sibling = current.previousSibling
|
|
475
|
+
while (sibling) {
|
|
476
|
+
if (sibling.nodeType === Node.ELEMENT_NODE && sibling.tagName === current.tagName) {
|
|
477
|
+
index++
|
|
478
|
+
}
|
|
479
|
+
sibling = sibling.previousSibling
|
|
480
|
+
}
|
|
481
|
+
const tagName = current.tagName.toLowerCase()
|
|
482
|
+
const pathIndex = index > 0 ? `[${index + 1}]` : ''
|
|
483
|
+
parts.unshift(`${tagName}${pathIndex}`)
|
|
484
|
+
current = current.parentElement
|
|
485
|
+
}
|
|
486
|
+
return '//' + parts.join('/')
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
switch (this.helperType) {
|
|
490
|
+
case 'playwright':
|
|
491
|
+
return this.element.evaluate(xpathFn)
|
|
492
|
+
case 'puppeteer':
|
|
493
|
+
return this.element.evaluate(xpathFn)
|
|
494
|
+
case 'webdriver':
|
|
495
|
+
return this.helper.browser.execute(xpathFn, this.element)
|
|
496
|
+
default:
|
|
497
|
+
throw new Error(`Unsupported helper type: ${this.helperType}`)
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
async toOuterHTML() {
|
|
502
|
+
switch (this.helperType) {
|
|
503
|
+
case 'playwright':
|
|
504
|
+
return this.element.evaluate(el => el.outerHTML)
|
|
505
|
+
case 'puppeteer':
|
|
506
|
+
return this.element.evaluate(el => el.outerHTML)
|
|
507
|
+
case 'webdriver':
|
|
508
|
+
return this.helper.browser.execute(el => el.outerHTML, this.element)
|
|
509
|
+
default:
|
|
510
|
+
throw new Error(`Unsupported helper type: ${this.helperType}`)
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
async toSimplifiedHTML(maxLength = 300) {
|
|
515
|
+
const outerHTML = await this.toOuterHTML()
|
|
516
|
+
return simplifyHtmlElement(outerHTML, maxLength)
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
/**
|
|
520
|
+
* Plain-object snapshot of the element — text, simplified HTML, visibility,
|
|
521
|
+
* enabled state, and a curated set of attributes. Each underlying call is
|
|
522
|
+
* isolated so a single failure (e.g. detached element) doesn't poison the
|
|
523
|
+
* rest. Suitable for JSON.stringify, log output, MCP tool responses.
|
|
524
|
+
*
|
|
525
|
+
* @param {object} [opts]
|
|
526
|
+
* @param {number} [opts.maxHtmlLength=300] passed through to toSimplifiedHTML
|
|
527
|
+
* @param {string[]} [opts.attrs] attribute names to surface
|
|
528
|
+
* @returns {Promise<{text?: string, html?: string, visible?: boolean, enabled?: boolean, attrs?: object}>}
|
|
529
|
+
*/
|
|
530
|
+
async describe({ maxHtmlLength = 300, attrs = ['id', 'class', 'name', 'role', 'type', 'href', 'value', 'aria-label', 'placeholder', 'data-testid'] } = {}) {
|
|
531
|
+
const out = {}
|
|
532
|
+
await Promise.all([
|
|
533
|
+
this.toSimplifiedHTML(maxHtmlLength).then(v => { if (v) out.html = v }, () => {}),
|
|
534
|
+
this.getText().then(v => { const t = v?.trim(); if (t) out.text = t }, () => {}),
|
|
535
|
+
this.isVisible().then(v => { out.visible = v }, () => {}),
|
|
536
|
+
this.isEnabled().then(v => { out.enabled = v }, () => {}),
|
|
537
|
+
])
|
|
538
|
+
const collected = {}
|
|
539
|
+
await Promise.all(attrs.map(async name => {
|
|
540
|
+
try {
|
|
541
|
+
const v = await this.getAttribute(name)
|
|
542
|
+
if (v != null && v !== '') collected[name] = v
|
|
543
|
+
} catch {}
|
|
544
|
+
}))
|
|
545
|
+
if (Object.keys(collected).length) out.attrs = collected
|
|
546
|
+
return out
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
// Make accidental JSON.stringify (e.g. returning a WebElement from MCP run_code)
|
|
550
|
+
// produce a usable hint instead of `{}` — the underlying handle isn't
|
|
551
|
+
// serializable. Use .describe() for the real plain-object snapshot.
|
|
552
|
+
toJSON() {
|
|
553
|
+
return `[WebElement ${this.helperType} — call .describe() for a plain-object snapshot or .toSimplifiedHTML() for HTML]`
|
|
554
|
+
}
|
|
555
|
+
|
|
309
556
|
_normalizeLocator(locator) {
|
|
310
557
|
if (typeof locator === 'string') {
|
|
311
558
|
return locator
|
package/lib/els.js
CHANGED
|
@@ -6,10 +6,11 @@ import recordStep from './step/record.js'
|
|
|
6
6
|
import FuncStep from './step/func.js'
|
|
7
7
|
import { truth } from './assert/truth.js'
|
|
8
8
|
import { isAsyncFunction, humanizeFunction } from './utils.js'
|
|
9
|
+
import WebElement from './element/WebElement.js'
|
|
9
10
|
|
|
10
11
|
function element(purpose, locator, fn) {
|
|
11
12
|
let stepConfig
|
|
12
|
-
if (arguments[arguments.length - 1]
|
|
13
|
+
if (StepConfig.isStepConfig(arguments[arguments.length - 1])) {
|
|
13
14
|
stepConfig = arguments[arguments.length - 1]
|
|
14
15
|
}
|
|
15
16
|
|
|
@@ -28,7 +29,8 @@ function element(purpose, locator, fn) {
|
|
|
28
29
|
const els = await step.helper._locate(locator)
|
|
29
30
|
output.debug(`Found ${els.length} elements, using first element`)
|
|
30
31
|
|
|
31
|
-
|
|
32
|
+
const wrapped = new WebElement(els[0], step.helper)
|
|
33
|
+
return fn(wrapped)
|
|
32
34
|
},
|
|
33
35
|
stepConfig,
|
|
34
36
|
)
|
|
@@ -52,7 +54,8 @@ function eachElement(purpose, locator, fn) {
|
|
|
52
54
|
let i = 0
|
|
53
55
|
for (const el of els) {
|
|
54
56
|
try {
|
|
55
|
-
|
|
57
|
+
const wrapped = new WebElement(el, step.helper)
|
|
58
|
+
await fn(wrapped, i)
|
|
56
59
|
} catch (err) {
|
|
57
60
|
output.error(`eachElement: failed operation on element #${i} ${el}`)
|
|
58
61
|
errs.push(err)
|
|
@@ -74,7 +77,8 @@ function expectElement(locator, fn) {
|
|
|
74
77
|
const els = await step.helper._locate(locator)
|
|
75
78
|
output.debug(`Found ${els.length} elements, first will be used for assertion`)
|
|
76
79
|
|
|
77
|
-
const
|
|
80
|
+
const wrapped = new WebElement(els[0], step.helper)
|
|
81
|
+
const result = await fn(wrapped)
|
|
78
82
|
const assertion = truth(`element (${locator})`, fn.toString())
|
|
79
83
|
assertion.assert(result)
|
|
80
84
|
})
|
|
@@ -92,7 +96,8 @@ function expectAnyElement(locator, fn) {
|
|
|
92
96
|
|
|
93
97
|
let found = false
|
|
94
98
|
for (const el of els) {
|
|
95
|
-
const
|
|
99
|
+
const wrapped = new WebElement(el, step.helper)
|
|
100
|
+
const result = await fn(wrapped)
|
|
96
101
|
if (result) {
|
|
97
102
|
found = true
|
|
98
103
|
break
|
|
@@ -113,7 +118,8 @@ function expectAllElements(locator, fn) {
|
|
|
113
118
|
let i = 1
|
|
114
119
|
for (const el of els) {
|
|
115
120
|
output.debug(`checking element #${i}: ${el}`)
|
|
116
|
-
const
|
|
121
|
+
const wrapped = new WebElement(el, step.helper)
|
|
122
|
+
const result = await fn(wrapped)
|
|
117
123
|
const assertion = truth(`element #${i} of (${locator})`, humanizeFunction(fn))
|
|
118
124
|
assertion.assert(result)
|
|
119
125
|
i++
|
package/lib/globals.js
CHANGED
|
@@ -8,27 +8,47 @@ import fsPath from 'path'
|
|
|
8
8
|
import ActorFactory from './actor.js'
|
|
9
9
|
import output from './output.js'
|
|
10
10
|
import locator from './locator.js'
|
|
11
|
+
import store from './store.js'
|
|
11
12
|
|
|
12
13
|
/**
|
|
13
14
|
* Initialize CodeceptJS core globals
|
|
14
15
|
* Called from Codecept.initGlobals()
|
|
15
16
|
*/
|
|
16
17
|
export async function initCodeceptGlobals(dir, config, container) {
|
|
18
|
+
store.initialize({
|
|
19
|
+
codeceptDir: dir,
|
|
20
|
+
outputDir: fsPath.resolve(dir, config.output),
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
store.noGlobals = config.noGlobals || false
|
|
24
|
+
store.maskSensitiveData = config.maskSensitiveData || false
|
|
25
|
+
|
|
26
|
+
// Keep globals for backward compat with external plugins
|
|
17
27
|
global.codecept_dir = dir
|
|
18
28
|
global.output_dir = fsPath.resolve(dir, config.output)
|
|
19
29
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
return ActorFactory(obj, global.container || container)
|
|
24
|
-
}
|
|
25
|
-
global.Actor = global.actor
|
|
26
|
-
|
|
27
|
-
// Use dynamic imports for modules to avoid circular dependencies
|
|
30
|
+
// pause/inject/share stay global even under noGlobals — they're the everyday
|
|
31
|
+
// debugging/wiring entry points and have no useful import alternative for
|
|
32
|
+
// page-object code that runs before the container is available.
|
|
28
33
|
global.pause = async (...args) => {
|
|
29
34
|
const pauseModule = await import('./pause.js')
|
|
30
35
|
return (pauseModule.default || pauseModule)(...args)
|
|
31
36
|
}
|
|
37
|
+
global.inject = () => container.support()
|
|
38
|
+
global.share = container.share
|
|
39
|
+
|
|
40
|
+
if (config.noGlobals) return;
|
|
41
|
+
|
|
42
|
+
output.print(output.styles.debug('Global functions are deprecated. Use `import { Helper, within, session } from "codeceptjs"` instead. Set `noGlobals: true` in config to disable globals.'));
|
|
43
|
+
|
|
44
|
+
const HelperModule = await import('@codeceptjs/helper')
|
|
45
|
+
global.Helper = global.codecept_helper = HelperModule.default || HelperModule
|
|
46
|
+
|
|
47
|
+
// Set up actor global - will use container when available
|
|
48
|
+
global.actor = global.codecept_actor = (obj) => {
|
|
49
|
+
return ActorFactory(obj, container)
|
|
50
|
+
}
|
|
51
|
+
global.Actor = global.actor
|
|
32
52
|
|
|
33
53
|
global.within = async (...args) => {
|
|
34
54
|
return (await import('./effects.js')).within(...args)
|
|
@@ -46,14 +66,9 @@ export async function initCodeceptGlobals(dir, config, container) {
|
|
|
46
66
|
return locator.build(locatorQuery)
|
|
47
67
|
}
|
|
48
68
|
|
|
49
|
-
global.inject = () => container.support()
|
|
50
|
-
global.share = container.share
|
|
51
|
-
|
|
52
69
|
const secretModule = await import('./secret.js')
|
|
53
70
|
global.secret = secretModule.secret || (secretModule.default && secretModule.default.secret)
|
|
54
71
|
|
|
55
|
-
global.codecept_debug = output.debug
|
|
56
|
-
|
|
57
72
|
const codeceptjsModule = await import('./index.js') // load all objects
|
|
58
73
|
global.codeceptjs = codeceptjsModule.default || codeceptjsModule
|
|
59
74
|
|
|
@@ -65,9 +80,6 @@ export async function initCodeceptGlobals(dir, config, container) {
|
|
|
65
80
|
global.Then = stepDefinitions.Then
|
|
66
81
|
global.DefineParameterType = stepDefinitions.defineParameterType
|
|
67
82
|
|
|
68
|
-
// debug mode
|
|
69
|
-
global.debugMode = false
|
|
70
|
-
|
|
71
83
|
// mask sensitive data
|
|
72
84
|
global.maskSensitiveData = config.maskSensitiveData || false
|
|
73
85
|
|
|
@@ -78,6 +90,8 @@ export async function initCodeceptGlobals(dir, config, container) {
|
|
|
78
90
|
* Called from mocha/ui.js pre-require event
|
|
79
91
|
*/
|
|
80
92
|
export function initMochaGlobals(context) {
|
|
93
|
+
if (store.noGlobals) return;
|
|
94
|
+
|
|
81
95
|
// Mocha test framework globals
|
|
82
96
|
global.BeforeAll = context.BeforeAll
|
|
83
97
|
global.AfterAll = context.AfterAll
|
|
@@ -106,6 +120,8 @@ export function getGlobalNames() {
|
|
|
106
120
|
return [
|
|
107
121
|
'codecept_dir',
|
|
108
122
|
'output_dir',
|
|
123
|
+
'Helper',
|
|
124
|
+
'codecept_helper',
|
|
109
125
|
'actor',
|
|
110
126
|
'codecept_actor',
|
|
111
127
|
'Actor',
|
|
@@ -117,13 +133,11 @@ export function getGlobalNames() {
|
|
|
117
133
|
'inject',
|
|
118
134
|
'share',
|
|
119
135
|
'secret',
|
|
120
|
-
'codecept_debug',
|
|
121
136
|
'codeceptjs',
|
|
122
137
|
'Given',
|
|
123
138
|
'When',
|
|
124
139
|
'Then',
|
|
125
140
|
'DefineParameterType',
|
|
126
|
-
'debugMode',
|
|
127
141
|
'maskSensitiveData',
|
|
128
142
|
'BeforeAll',
|
|
129
143
|
'AfterAll',
|
|
@@ -136,6 +150,5 @@ export function getGlobalNames() {
|
|
|
136
150
|
'After',
|
|
137
151
|
'Scenario',
|
|
138
152
|
'xScenario',
|
|
139
|
-
'container'
|
|
140
153
|
]
|
|
141
154
|
}
|
package/lib/heal.js
CHANGED
|
@@ -4,6 +4,7 @@ import colors from 'chalk'
|
|
|
4
4
|
import recorder from './recorder.js'
|
|
5
5
|
import output from './output.js'
|
|
6
6
|
import event from './event.js'
|
|
7
|
+
import container from './container.js'
|
|
7
8
|
|
|
8
9
|
/**
|
|
9
10
|
* @class
|
|
@@ -48,12 +49,14 @@ class Heal {
|
|
|
48
49
|
}
|
|
49
50
|
|
|
50
51
|
hasCorrespondingRecipes(step) {
|
|
51
|
-
return matchRecipes(this.recipes, this.contextName).filter(r => !r.steps || r.steps.includes(step.
|
|
52
|
+
return matchRecipes(this.recipes, this.contextName).filter(r => !r.steps || r.steps.includes(step.title)).length > 0
|
|
52
53
|
}
|
|
53
54
|
|
|
54
55
|
async getCodeSuggestions(context) {
|
|
55
56
|
const suggestions = []
|
|
57
|
+
const stepName = context.step?.title
|
|
56
58
|
const recipes = matchRecipes(this.recipes, this.contextName)
|
|
59
|
+
.filter(r => !r.steps || !stepName || r.steps.includes(stepName))
|
|
57
60
|
|
|
58
61
|
debug('Recipes', recipes)
|
|
59
62
|
|
|
@@ -69,7 +72,7 @@ class Heal {
|
|
|
69
72
|
if (!prepareFn) continue
|
|
70
73
|
|
|
71
74
|
if (context[property]) continue
|
|
72
|
-
context[property] = await prepareFn(
|
|
75
|
+
context[property] = await prepareFn(container.support())
|
|
73
76
|
}
|
|
74
77
|
|
|
75
78
|
output.level(currentOutputLevel)
|
|
@@ -116,10 +119,10 @@ class Heal {
|
|
|
116
119
|
})
|
|
117
120
|
|
|
118
121
|
if (typeof codeSnippet === 'string') {
|
|
119
|
-
const I =
|
|
122
|
+
const I = container.support('I')
|
|
120
123
|
await eval(codeSnippet)
|
|
121
124
|
} else if (typeof codeSnippet === 'function') {
|
|
122
|
-
await codeSnippet(
|
|
125
|
+
await codeSnippet(container.support())
|
|
123
126
|
}
|
|
124
127
|
|
|
125
128
|
this.fixes.push({
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import path from 'path'
|
|
2
2
|
import Helper from '@codeceptjs/helper'
|
|
3
3
|
import REST from './REST.js'
|
|
4
|
+
import store from '../store.js'
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
7
|
* Helper for managing remote data using REST API.
|
|
@@ -324,7 +325,7 @@ class ApiDataFactory extends Helper {
|
|
|
324
325
|
await import.meta.resolve(modulePath)
|
|
325
326
|
} catch (e) {
|
|
326
327
|
// If not found, try relative to codecept_dir
|
|
327
|
-
modulePath = path.join(
|
|
328
|
+
modulePath = path.join(store.codeceptDir, modulePath)
|
|
328
329
|
}
|
|
329
330
|
// check if the new syntax `export default new Factory()` is used and loads the builder, otherwise loads the module that used old syntax `module.exports = new Factory()`.
|
|
330
331
|
const module = await import(modulePath)
|