codeceptjs 4.0.2-beta.9 → 4.0.2
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 +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 +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/cli.js +19 -1
- package/lib/mocha/factory.js +11 -1
- 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 +35 -30
- 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
|
|
@@ -81,6 +82,10 @@ class WebElement {
|
|
|
81
82
|
async getProperty(name) {
|
|
82
83
|
switch (this.helperType) {
|
|
83
84
|
case 'playwright':
|
|
85
|
+
// For Locator objects, use inputValue() for the 'value' property
|
|
86
|
+
if (name === 'value' && this.element.inputValue) {
|
|
87
|
+
return this.element.inputValue()
|
|
88
|
+
}
|
|
84
89
|
return this.element.evaluate((el, propName) => el[propName], name)
|
|
85
90
|
case 'webdriver':
|
|
86
91
|
return this.element.getProperty(name)
|
|
@@ -236,16 +241,149 @@ class WebElement {
|
|
|
236
241
|
async type(text, options = {}) {
|
|
237
242
|
switch (this.helperType) {
|
|
238
243
|
case 'playwright':
|
|
244
|
+
// Playwright Locator objects use fill() instead of type()
|
|
245
|
+
if (this.element.fill) {
|
|
246
|
+
return this.element.fill(text, options)
|
|
247
|
+
}
|
|
239
248
|
return this.element.type(text, options)
|
|
240
249
|
case 'webdriver':
|
|
241
250
|
return this.element.setValue(text)
|
|
242
251
|
case 'puppeteer':
|
|
252
|
+
await this.element.evaluate(el => { el.value = '' })
|
|
243
253
|
return this.element.type(text, options)
|
|
244
254
|
default:
|
|
245
255
|
throw new Error(`Unsupported helper type: ${this.helperType}`)
|
|
246
256
|
}
|
|
247
257
|
}
|
|
248
258
|
|
|
259
|
+
/**
|
|
260
|
+
* Run a function in the browser with this element as the first argument.
|
|
261
|
+
* @param {Function} fn Browser-side function. Receives the element, then extra args.
|
|
262
|
+
* @param {...any} args Additional arguments passed to the function
|
|
263
|
+
* @returns {Promise<any>} Value returned by fn
|
|
264
|
+
*/
|
|
265
|
+
async evaluate(fn, ...args) {
|
|
266
|
+
switch (this.helperType) {
|
|
267
|
+
case 'playwright':
|
|
268
|
+
case 'puppeteer':
|
|
269
|
+
return this.element.evaluate(fn, ...args)
|
|
270
|
+
case 'webdriver':
|
|
271
|
+
return this.helper.executeScript(fn, this.element, ...args)
|
|
272
|
+
default:
|
|
273
|
+
throw new Error(`Unsupported helper type: ${this.helperType}`)
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* Focus the element.
|
|
279
|
+
* @returns {Promise<void>}
|
|
280
|
+
*/
|
|
281
|
+
async focus() {
|
|
282
|
+
switch (this.helperType) {
|
|
283
|
+
case 'playwright':
|
|
284
|
+
return this.element.focus()
|
|
285
|
+
case 'puppeteer':
|
|
286
|
+
if (this.element.focus) return this.element.focus()
|
|
287
|
+
return this.element.evaluate(el => el.focus())
|
|
288
|
+
case 'webdriver':
|
|
289
|
+
return this.helper.executeScript(el => el.focus(), this.element)
|
|
290
|
+
default:
|
|
291
|
+
throw new Error(`Unsupported helper type: ${this.helperType}`)
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
/**
|
|
296
|
+
* Type characters via the page/browser keyboard into the focused element.
|
|
297
|
+
* Unlike `type()`, this does not call `.fill()`/`.setValue()`, so it works
|
|
298
|
+
* with contenteditable nodes, iframe bodies, and editor-owned hidden textareas.
|
|
299
|
+
* @param {string} text Text to send
|
|
300
|
+
* @param {Object} [options] Options (e.g. `{ delay }`)
|
|
301
|
+
* @returns {Promise<void>}
|
|
302
|
+
*/
|
|
303
|
+
async typeText(text, options = {}) {
|
|
304
|
+
const s = String(text)
|
|
305
|
+
switch (this.helperType) {
|
|
306
|
+
case 'playwright':
|
|
307
|
+
case 'puppeteer':
|
|
308
|
+
return this.helper.page.keyboard.type(s, options)
|
|
309
|
+
case 'webdriver': {
|
|
310
|
+
const ENTER = '\uE007'
|
|
311
|
+
const parts = s.split('\n')
|
|
312
|
+
for (let i = 0; i < parts.length; i++) {
|
|
313
|
+
if (parts[i]) await this.helper.browser.keys(parts[i])
|
|
314
|
+
if (i < parts.length - 1) await this.helper.browser.keys(ENTER)
|
|
315
|
+
}
|
|
316
|
+
return
|
|
317
|
+
}
|
|
318
|
+
default:
|
|
319
|
+
throw new Error(`Unsupported helper type: ${this.helperType}`)
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
/**
|
|
324
|
+
* Select all content in the focused field and delete it via keyboard input.
|
|
325
|
+
* Sends Ctrl+A and Meta+A (so it works across platforms) followed by Backspace.
|
|
326
|
+
* @returns {Promise<void>}
|
|
327
|
+
*/
|
|
328
|
+
async selectAllAndDelete() {
|
|
329
|
+
switch (this.helperType) {
|
|
330
|
+
case 'playwright':
|
|
331
|
+
await this.helper.page.keyboard.press('Control+a').catch(() => {})
|
|
332
|
+
await this.helper.page.keyboard.press('Meta+a').catch(() => {})
|
|
333
|
+
await this.helper.page.keyboard.press('Backspace')
|
|
334
|
+
return
|
|
335
|
+
case 'puppeteer':
|
|
336
|
+
for (const mod of ['Control', 'Meta']) {
|
|
337
|
+
try {
|
|
338
|
+
await this.helper.page.keyboard.down(mod)
|
|
339
|
+
await this.helper.page.keyboard.press('KeyA')
|
|
340
|
+
await this.helper.page.keyboard.up(mod)
|
|
341
|
+
} catch (e) {}
|
|
342
|
+
}
|
|
343
|
+
await this.helper.page.keyboard.press('Backspace')
|
|
344
|
+
return
|
|
345
|
+
case 'webdriver': {
|
|
346
|
+
const b = this.helper.browser
|
|
347
|
+
await b.keys(['Control', 'a']).catch(() => {})
|
|
348
|
+
await b.keys(['Meta', 'a']).catch(() => {})
|
|
349
|
+
await b.keys(['Backspace'])
|
|
350
|
+
return
|
|
351
|
+
}
|
|
352
|
+
default:
|
|
353
|
+
throw new Error(`Unsupported helper type: ${this.helperType}`)
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
/**
|
|
358
|
+
* Treat this element as an iframe; invoke `fn` with a WebElement wrapping
|
|
359
|
+
* the iframe body. For WebDriver this switches the browser into the frame
|
|
360
|
+
* for the duration of the callback and switches back on exit.
|
|
361
|
+
* @param {(body: WebElement) => Promise<any>} fn
|
|
362
|
+
* @returns {Promise<any>} Return value of fn
|
|
363
|
+
*/
|
|
364
|
+
async inIframe(fn) {
|
|
365
|
+
switch (this.helperType) {
|
|
366
|
+
case 'playwright':
|
|
367
|
+
case 'puppeteer': {
|
|
368
|
+
const frame = await this.element.contentFrame()
|
|
369
|
+
const body = await frame.$('body')
|
|
370
|
+
return fn(new WebElement(body, this.helper))
|
|
371
|
+
}
|
|
372
|
+
case 'webdriver': {
|
|
373
|
+
const browser = this.helper.browser
|
|
374
|
+
await browser.switchFrame(this.element)
|
|
375
|
+
try {
|
|
376
|
+
const body = await browser.$('body')
|
|
377
|
+
return await fn(new WebElement(body, this.helper))
|
|
378
|
+
} finally {
|
|
379
|
+
await browser.switchFrame(null)
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
default:
|
|
383
|
+
throw new Error(`Unsupported helper type: ${this.helperType}`)
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
|
|
249
387
|
/**
|
|
250
388
|
* Find first child element matching the locator
|
|
251
389
|
* @param {string|Object} locator Element locator
|
|
@@ -256,7 +394,18 @@ class WebElement {
|
|
|
256
394
|
|
|
257
395
|
switch (this.helperType) {
|
|
258
396
|
case 'playwright':
|
|
259
|
-
|
|
397
|
+
// Playwright Locator objects use locator() method
|
|
398
|
+
if (this.element.locator) {
|
|
399
|
+
const childLocator = this.element.locator(this._normalizeLocator(locator))
|
|
400
|
+
// Get the element handle from the locator
|
|
401
|
+
try {
|
|
402
|
+
childElement = await childLocator.elementHandle()
|
|
403
|
+
} catch (e) {
|
|
404
|
+
return null
|
|
405
|
+
}
|
|
406
|
+
} else {
|
|
407
|
+
childElement = await this.element.$(this._normalizeLocator(locator))
|
|
408
|
+
}
|
|
260
409
|
break
|
|
261
410
|
case 'webdriver':
|
|
262
411
|
try {
|
|
@@ -285,7 +434,14 @@ class WebElement {
|
|
|
285
434
|
|
|
286
435
|
switch (this.helperType) {
|
|
287
436
|
case 'playwright':
|
|
288
|
-
|
|
437
|
+
// Playwright Locator objects use locator() method
|
|
438
|
+
if (this.element.locator) {
|
|
439
|
+
const childLocator = this.element.locator(this._normalizeLocator(locator))
|
|
440
|
+
// Get all element handles from the locator
|
|
441
|
+
childElements = await childLocator.elementHandles()
|
|
442
|
+
} else {
|
|
443
|
+
childElements = await this.element.$$(this._normalizeLocator(locator))
|
|
444
|
+
}
|
|
289
445
|
break
|
|
290
446
|
case 'webdriver':
|
|
291
447
|
childElements = await this.element.$$(this._normalizeLocator(locator))
|
|
@@ -306,6 +462,94 @@ class WebElement {
|
|
|
306
462
|
* @returns {string} Normalized CSS selector
|
|
307
463
|
* @private
|
|
308
464
|
*/
|
|
465
|
+
async toAbsoluteXPath() {
|
|
466
|
+
const xpathFn = (el) => {
|
|
467
|
+
const parts = []
|
|
468
|
+
let current = el
|
|
469
|
+
while (current && current.nodeType === Node.ELEMENT_NODE) {
|
|
470
|
+
let index = 0
|
|
471
|
+
let sibling = current.previousSibling
|
|
472
|
+
while (sibling) {
|
|
473
|
+
if (sibling.nodeType === Node.ELEMENT_NODE && sibling.tagName === current.tagName) {
|
|
474
|
+
index++
|
|
475
|
+
}
|
|
476
|
+
sibling = sibling.previousSibling
|
|
477
|
+
}
|
|
478
|
+
const tagName = current.tagName.toLowerCase()
|
|
479
|
+
const pathIndex = index > 0 ? `[${index + 1}]` : ''
|
|
480
|
+
parts.unshift(`${tagName}${pathIndex}`)
|
|
481
|
+
current = current.parentElement
|
|
482
|
+
}
|
|
483
|
+
return '//' + parts.join('/')
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
switch (this.helperType) {
|
|
487
|
+
case 'playwright':
|
|
488
|
+
return this.element.evaluate(xpathFn)
|
|
489
|
+
case 'puppeteer':
|
|
490
|
+
return this.element.evaluate(xpathFn)
|
|
491
|
+
case 'webdriver':
|
|
492
|
+
return this.helper.browser.execute(xpathFn, this.element)
|
|
493
|
+
default:
|
|
494
|
+
throw new Error(`Unsupported helper type: ${this.helperType}`)
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
async toOuterHTML() {
|
|
499
|
+
switch (this.helperType) {
|
|
500
|
+
case 'playwright':
|
|
501
|
+
return this.element.evaluate(el => el.outerHTML)
|
|
502
|
+
case 'puppeteer':
|
|
503
|
+
return this.element.evaluate(el => el.outerHTML)
|
|
504
|
+
case 'webdriver':
|
|
505
|
+
return this.helper.browser.execute(el => el.outerHTML, this.element)
|
|
506
|
+
default:
|
|
507
|
+
throw new Error(`Unsupported helper type: ${this.helperType}`)
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
async toSimplifiedHTML(maxLength = 300) {
|
|
512
|
+
const outerHTML = await this.toOuterHTML()
|
|
513
|
+
return simplifyHtmlElement(outerHTML, maxLength)
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
/**
|
|
517
|
+
* Plain-object snapshot of the element — text, simplified HTML, visibility,
|
|
518
|
+
* enabled state, and a curated set of attributes. Each underlying call is
|
|
519
|
+
* isolated so a single failure (e.g. detached element) doesn't poison the
|
|
520
|
+
* rest. Suitable for JSON.stringify, log output, MCP tool responses.
|
|
521
|
+
*
|
|
522
|
+
* @param {object} [opts]
|
|
523
|
+
* @param {number} [opts.maxHtmlLength=300] passed through to toSimplifiedHTML
|
|
524
|
+
* @param {string[]} [opts.attrs] attribute names to surface
|
|
525
|
+
* @returns {Promise<{text?: string, html?: string, visible?: boolean, enabled?: boolean, attrs?: object}>}
|
|
526
|
+
*/
|
|
527
|
+
async describe({ maxHtmlLength = 300, attrs = ['id', 'class', 'name', 'role', 'type', 'href', 'value', 'aria-label', 'placeholder', 'data-testid'] } = {}) {
|
|
528
|
+
const out = {}
|
|
529
|
+
await Promise.all([
|
|
530
|
+
this.toSimplifiedHTML(maxHtmlLength).then(v => { if (v) out.html = v }, () => {}),
|
|
531
|
+
this.getText().then(v => { const t = v?.trim(); if (t) out.text = t }, () => {}),
|
|
532
|
+
this.isVisible().then(v => { out.visible = v }, () => {}),
|
|
533
|
+
this.isEnabled().then(v => { out.enabled = v }, () => {}),
|
|
534
|
+
])
|
|
535
|
+
const collected = {}
|
|
536
|
+
await Promise.all(attrs.map(async name => {
|
|
537
|
+
try {
|
|
538
|
+
const v = await this.getAttribute(name)
|
|
539
|
+
if (v != null && v !== '') collected[name] = v
|
|
540
|
+
} catch {}
|
|
541
|
+
}))
|
|
542
|
+
if (Object.keys(collected).length) out.attrs = collected
|
|
543
|
+
return out
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
// Make accidental JSON.stringify (e.g. returning a WebElement from MCP run_code)
|
|
547
|
+
// produce a usable hint instead of `{}` — the underlying handle isn't
|
|
548
|
+
// serializable. Use .describe() for the real plain-object snapshot.
|
|
549
|
+
toJSON() {
|
|
550
|
+
return `[WebElement ${this.helperType} — call .describe() for a plain-object snapshot or .toSimplifiedHTML() for HTML]`
|
|
551
|
+
}
|
|
552
|
+
|
|
309
553
|
_normalizeLocator(locator) {
|
|
310
554
|
if (typeof locator === 'string') {
|
|
311
555
|
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)
|
package/lib/helper/Appium.js
CHANGED
|
@@ -1543,8 +1543,8 @@ class Appium extends Webdriver {
|
|
|
1543
1543
|
/**
|
|
1544
1544
|
* {{> dontSeeElement }}
|
|
1545
1545
|
*/
|
|
1546
|
-
async dontSeeElement(locator) {
|
|
1547
|
-
if (this.isWeb) return super.dontSeeElement(locator)
|
|
1546
|
+
async dontSeeElement(locator, context = null) {
|
|
1547
|
+
if (this.isWeb) return super.dontSeeElement(locator, context)
|
|
1548
1548
|
|
|
1549
1549
|
// For mobile native apps, use safe isDisplayed wrapper
|
|
1550
1550
|
const parsedLocator = parseLocator.call(this, locator)
|
|
@@ -1589,9 +1589,9 @@ class Appium extends Webdriver {
|
|
|
1589
1589
|
* {{> fillField }}
|
|
1590
1590
|
*
|
|
1591
1591
|
*/
|
|
1592
|
-
async fillField(field, value) {
|
|
1592
|
+
async fillField(field, value, context = null) {
|
|
1593
1593
|
value = value.toString()
|
|
1594
|
-
if (this.isWeb) return super.fillField(field, value)
|
|
1594
|
+
if (this.isWeb) return super.fillField(field, value, context)
|
|
1595
1595
|
return super.fillField(parseLocator.call(this, field), value)
|
|
1596
1596
|
}
|
|
1597
1597
|
|
|
@@ -1706,8 +1706,8 @@ class Appium extends Webdriver {
|
|
|
1706
1706
|
* {{> seeElement }}
|
|
1707
1707
|
*
|
|
1708
1708
|
*/
|
|
1709
|
-
async seeElement(locator) {
|
|
1710
|
-
if (this.isWeb) return super.seeElement(locator)
|
|
1709
|
+
async seeElement(locator, context = null) {
|
|
1710
|
+
if (this.isWeb) return super.seeElement(locator, context)
|
|
1711
1711
|
|
|
1712
1712
|
// For mobile native apps, use safe isDisplayed wrapper
|
|
1713
1713
|
const parsedLocator = parseLocator.call(this, locator)
|
|
@@ -1754,8 +1754,8 @@ class Appium extends Webdriver {
|
|
|
1754
1754
|
*
|
|
1755
1755
|
* Supported only for web testing
|
|
1756
1756
|
*/
|
|
1757
|
-
async selectOption(select, option) {
|
|
1758
|
-
if (this.isWeb) return super.selectOption(select, option)
|
|
1757
|
+
async selectOption(select, option, context = null) {
|
|
1758
|
+
if (this.isWeb) return super.selectOption(select, option, context)
|
|
1759
1759
|
throw new Error("Should be used only in Web context. In native context use 'click' method instead")
|
|
1760
1760
|
}
|
|
1761
1761
|
|
package/lib/helper/FileSystem.js
CHANGED
|
@@ -4,6 +4,7 @@ import fs from 'fs'
|
|
|
4
4
|
|
|
5
5
|
import Helper from '@codeceptjs/helper'
|
|
6
6
|
import { fileExists } from '../utils.js'
|
|
7
|
+
import store from '../store.js'
|
|
7
8
|
import { fileIncludes } from '../assert/include.js'
|
|
8
9
|
import { fileEquals } from '../assert/equal.js'
|
|
9
10
|
|
|
@@ -33,7 +34,7 @@ import { fileEquals } from '../assert/equal.js'
|
|
|
33
34
|
class FileSystem extends Helper {
|
|
34
35
|
constructor() {
|
|
35
36
|
super()
|
|
36
|
-
this.dir =
|
|
37
|
+
this.dir = store.codeceptDir
|
|
37
38
|
this.file = ''
|
|
38
39
|
}
|
|
39
40
|
|
|
@@ -52,7 +53,7 @@ class FileSystem extends Helper {
|
|
|
52
53
|
* @param {string} openPath
|
|
53
54
|
*/
|
|
54
55
|
amInPath(openPath) {
|
|
55
|
-
this.dir = path.join(
|
|
56
|
+
this.dir = path.join(store.codeceptDir, openPath)
|
|
56
57
|
try {
|
|
57
58
|
this.debugSection('Dir', this.dir)
|
|
58
59
|
} catch (e) {
|
|
@@ -2,6 +2,7 @@ import path from 'path'
|
|
|
2
2
|
|
|
3
3
|
import HelperModule from '@codeceptjs/helper'
|
|
4
4
|
import GraphQL from './GraphQL.js'
|
|
5
|
+
import store from '../store.js'
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
8
|
* Helper for managing remote data using GraphQL queries.
|
|
@@ -251,7 +252,7 @@ class GraphQLDataFactory extends Helper {
|
|
|
251
252
|
try {
|
|
252
253
|
require.resolve(modulePath)
|
|
253
254
|
} catch (e) {
|
|
254
|
-
modulePath = path.join(
|
|
255
|
+
modulePath = path.join(store.codeceptDir, modulePath)
|
|
255
256
|
}
|
|
256
257
|
const builder = require(modulePath)
|
|
257
258
|
return builder.build(data)
|