codeceptjs 4.0.0-beta.5 → 4.0.0-beta.6.esm-aria
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 +0 -45
- package/bin/codecept.js +46 -57
- package/lib/actor.js +15 -11
- package/lib/ai.js +6 -5
- package/lib/assert/empty.js +9 -8
- package/lib/assert/equal.js +15 -17
- package/lib/assert/error.js +2 -2
- package/lib/assert/include.js +9 -11
- package/lib/assert/throws.js +1 -1
- package/lib/assert/truth.js +8 -5
- package/lib/assert.js +18 -18
- package/lib/codecept.js +66 -107
- package/lib/colorUtils.js +48 -50
- package/lib/command/check.js +32 -27
- package/lib/command/configMigrate.js +11 -10
- package/lib/command/definitions.js +16 -10
- package/lib/command/dryRun.js +16 -16
- package/lib/command/generate.js +29 -26
- package/lib/command/gherkin/init.js +36 -38
- package/lib/command/gherkin/snippets.js +14 -14
- package/lib/command/gherkin/steps.js +21 -18
- package/lib/command/info.js +8 -8
- package/lib/command/init.js +34 -31
- package/lib/command/interactive.js +11 -10
- package/lib/command/list.js +10 -9
- package/lib/command/run-multiple/chunk.js +5 -5
- package/lib/command/run-multiple/collection.js +5 -5
- package/lib/command/run-multiple/run.js +3 -3
- package/lib/command/run-multiple.js +16 -13
- package/lib/command/run-rerun.js +6 -7
- package/lib/command/run-workers.js +10 -24
- package/lib/command/run.js +8 -8
- package/lib/command/utils.js +20 -18
- package/lib/command/workers/runTests.js +117 -269
- package/lib/config.js +111 -49
- package/lib/container.js +299 -102
- package/lib/data/context.js +6 -5
- package/lib/data/dataScenarioConfig.js +1 -1
- package/lib/data/dataTableArgument.js +1 -1
- package/lib/data/table.js +1 -1
- package/lib/effects.js +94 -10
- package/lib/els.js +11 -9
- package/lib/event.js +11 -10
- package/lib/globals.js +141 -0
- package/lib/heal.js +12 -12
- package/lib/helper/AI.js +1 -1
- package/lib/helper/ApiDataFactory.js +16 -13
- package/lib/helper/FileSystem.js +32 -12
- package/lib/helper/GraphQL.js +1 -1
- package/lib/helper/GraphQLDataFactory.js +1 -1
- package/lib/helper/JSONResponse.js +19 -30
- package/lib/helper/Mochawesome.js +9 -28
- package/lib/helper/Playwright.js +668 -265
- package/lib/helper/Puppeteer.js +284 -169
- package/lib/helper/REST.js +29 -12
- package/lib/helper/WebDriver.js +191 -71
- package/lib/helper/errors/ConnectionRefused.js +6 -6
- package/lib/helper/errors/ElementAssertion.js +11 -16
- package/lib/helper/errors/ElementNotFound.js +5 -9
- package/lib/helper/errors/RemoteBrowserConnectionRefused.js +5 -5
- package/lib/helper/extras/Console.js +11 -11
- package/lib/helper/extras/PlaywrightLocator.js +110 -0
- package/lib/helper/extras/PlaywrightPropEngine.js +18 -18
- package/lib/helper/extras/PlaywrightRestartOpts.js +23 -23
- package/lib/helper/extras/Popup.js +1 -1
- package/lib/helper/extras/React.js +29 -30
- package/lib/helper/network/actions.js +33 -48
- package/lib/helper/network/utils.js +76 -83
- package/lib/helper/scripts/blurElement.js +6 -6
- package/lib/helper/scripts/focusElement.js +6 -6
- package/lib/helper/scripts/highlightElement.js +9 -9
- package/lib/helper/scripts/isElementClickable.js +34 -34
- package/lib/helper.js +2 -1
- package/lib/history.js +23 -20
- package/lib/hooks.js +10 -10
- package/lib/html.js +90 -100
- package/lib/index.js +48 -21
- package/lib/listener/config.js +8 -9
- package/lib/listener/emptyRun.js +6 -7
- package/lib/listener/exit.js +4 -3
- package/lib/listener/globalRetry.js +5 -5
- package/lib/listener/globalTimeout.js +11 -10
- package/lib/listener/helpers.js +33 -14
- package/lib/listener/mocha.js +3 -4
- package/lib/listener/result.js +4 -5
- package/lib/listener/steps.js +7 -18
- package/lib/listener/store.js +3 -3
- package/lib/locator.js +213 -192
- package/lib/mocha/asyncWrapper.js +108 -75
- package/lib/mocha/bdd.js +99 -13
- package/lib/mocha/cli.js +60 -27
- package/lib/mocha/factory.js +75 -19
- package/lib/mocha/featureConfig.js +1 -1
- package/lib/mocha/gherkin.js +57 -25
- package/lib/mocha/hooks.js +12 -3
- package/lib/mocha/index.js +13 -4
- package/lib/mocha/inject.js +22 -5
- package/lib/mocha/scenarioConfig.js +2 -2
- package/lib/mocha/suite.js +9 -2
- package/lib/mocha/test.js +10 -13
- package/lib/mocha/ui.js +28 -31
- package/lib/output.js +11 -9
- package/lib/parser.js +44 -44
- package/lib/pause.js +15 -16
- package/lib/plugin/analyze.js +19 -12
- package/lib/plugin/auth.js +20 -21
- package/lib/plugin/autoDelay.js +12 -8
- package/lib/plugin/coverage.js +12 -8
- package/lib/plugin/customLocator.js +3 -3
- package/lib/plugin/customReporter.js +3 -2
- package/lib/plugin/heal.js +14 -9
- package/lib/plugin/pageInfo.js +10 -10
- package/lib/plugin/pauseOnFail.js +4 -3
- package/lib/plugin/retryFailedStep.js +47 -5
- package/lib/plugin/screenshotOnFail.js +75 -37
- package/lib/plugin/stepByStepReport.js +14 -14
- package/lib/plugin/stepTimeout.js +4 -3
- package/lib/plugin/subtitles.js +6 -5
- package/lib/recorder.js +33 -23
- package/lib/rerun.js +69 -26
- package/lib/result.js +4 -4
- package/lib/secret.js +18 -17
- package/lib/session.js +95 -89
- package/lib/step/base.js +6 -6
- package/lib/step/config.js +1 -1
- package/lib/step/func.js +3 -3
- package/lib/step/helper.js +3 -3
- package/lib/step/meta.js +4 -4
- package/lib/step/record.js +11 -11
- package/lib/step/retry.js +3 -3
- package/lib/step/section.js +3 -3
- package/lib/step.js +7 -10
- package/lib/steps.js +9 -5
- package/lib/store.js +1 -1
- package/lib/timeout.js +1 -7
- package/lib/transform.js +8 -8
- package/lib/translation.js +32 -18
- package/lib/utils.js +68 -97
- package/lib/workerStorage.js +16 -17
- package/lib/workers.js +145 -171
- package/package.json +63 -57
- package/translations/de-DE.js +2 -2
- package/translations/fr-FR.js +2 -2
- package/translations/index.js +23 -10
- package/translations/it-IT.js +2 -2
- package/translations/ja-JP.js +2 -2
- package/translations/nl-NL.js +2 -2
- package/translations/pl-PL.js +2 -2
- package/translations/pt-BR.js +2 -2
- package/translations/ru-RU.js +2 -2
- package/translations/utils.js +11 -2
- package/translations/zh-CN.js +2 -2
- package/translations/zh-TW.js +2 -2
- package/typings/index.d.ts +7 -18
- package/typings/promiseBasedTypes.d.ts +3769 -5450
- package/typings/types.d.ts +3953 -5778
- package/bin/test-server.js +0 -53
- package/lib/element/WebElement.js +0 -327
- package/lib/helper/Nightmare.js +0 -1486
- package/lib/helper/Protractor.js +0 -1840
- package/lib/helper/TestCafe.js +0 -1391
- package/lib/helper/clientscripts/nightmare.js +0 -213
- package/lib/helper/extras/PlaywrightReactVueLocator.js +0 -43
- package/lib/helper/testcafe/testControllerHolder.js +0 -42
- package/lib/helper/testcafe/testcafe-utils.js +0 -61
- package/lib/listener/retryEnhancer.js +0 -85
- package/lib/plugin/allure.js +0 -15
- package/lib/plugin/autoLogin.js +0 -5
- package/lib/plugin/commentStep.js +0 -141
- package/lib/plugin/eachElement.js +0 -127
- package/lib/plugin/fakerTransform.js +0 -49
- package/lib/plugin/htmlReporter.js +0 -1947
- package/lib/plugin/retryTo.js +0 -16
- package/lib/plugin/selenoid.js +0 -364
- package/lib/plugin/standardActingHelpers.js +0 -6
- package/lib/plugin/tryTo.js +0 -16
- package/lib/plugin/wdio.js +0 -247
- package/lib/test-server.js +0 -323
- package/lib/within.js +0 -90
package/lib/helper/REST.js
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
const { beautify } = require('../utils')
|
|
1
|
+
import axios from 'axios'
|
|
2
|
+
import Helper from '@codeceptjs/helper'
|
|
3
|
+
import { Agent } from 'https'
|
|
4
|
+
import Secret from '../secret.js'
|
|
5
|
+
import { beautify } from '../utils.js'
|
|
7
6
|
|
|
8
7
|
/**
|
|
9
8
|
* ## Configuration
|
|
@@ -143,7 +142,9 @@ class REST extends Helper {
|
|
|
143
142
|
|
|
144
143
|
static _checkRequirements() {
|
|
145
144
|
try {
|
|
146
|
-
|
|
145
|
+
// In ESM, axios is already imported at the top, so no need to check
|
|
146
|
+
// The import will fail at module load time if axios is missing
|
|
147
|
+
return null
|
|
147
148
|
} catch (e) {
|
|
148
149
|
return ['axios']
|
|
149
150
|
}
|
|
@@ -218,10 +219,18 @@ class REST extends Helper {
|
|
|
218
219
|
await this.config.onRequest(request)
|
|
219
220
|
}
|
|
220
221
|
|
|
221
|
-
|
|
222
|
+
try {
|
|
223
|
+
this.options.prettyPrintJson ? this.debugSection('Request', beautify(JSON.stringify(_debugRequest))) : this.debugSection('Request', JSON.stringify(_debugRequest))
|
|
224
|
+
} catch (e) {
|
|
225
|
+
console.log('[REST] Request:', JSON.stringify(_debugRequest))
|
|
226
|
+
}
|
|
222
227
|
|
|
223
228
|
if (this.options.printCurl) {
|
|
224
|
-
|
|
229
|
+
try {
|
|
230
|
+
this.debugSection('CURL Request', curlize(request))
|
|
231
|
+
} catch (e) {
|
|
232
|
+
console.log('[REST] CURL Request:', curlize(request))
|
|
233
|
+
}
|
|
225
234
|
}
|
|
226
235
|
|
|
227
236
|
let response
|
|
@@ -229,13 +238,21 @@ class REST extends Helper {
|
|
|
229
238
|
response = await this.axios(request)
|
|
230
239
|
} catch (err) {
|
|
231
240
|
if (!err.response) throw err
|
|
232
|
-
|
|
241
|
+
try {
|
|
242
|
+
this.debugSection('Response', `Response error. Status code: ${err.response.status}`)
|
|
243
|
+
} catch (e) {
|
|
244
|
+
console.log('[REST] Response error. Status code:', err.response.status)
|
|
245
|
+
}
|
|
233
246
|
response = err.response
|
|
234
247
|
}
|
|
235
248
|
if (this.config.onResponse) {
|
|
236
249
|
await this.config.onResponse(response)
|
|
237
250
|
}
|
|
238
|
-
|
|
251
|
+
try {
|
|
252
|
+
this.options.prettyPrintJson ? this.debugSection('Response', beautify(JSON.stringify(response.data))) : this.debugSection('Response', JSON.stringify(response.data))
|
|
253
|
+
} catch (e) {
|
|
254
|
+
console.log('[REST] Response:', JSON.stringify(response.data))
|
|
255
|
+
}
|
|
239
256
|
return response
|
|
240
257
|
}
|
|
241
258
|
|
|
@@ -427,7 +444,7 @@ class REST extends Helper {
|
|
|
427
444
|
}
|
|
428
445
|
}
|
|
429
446
|
|
|
430
|
-
|
|
447
|
+
export { REST as default }
|
|
431
448
|
|
|
432
449
|
function curlize(request) {
|
|
433
450
|
if (request.data?.constructor.name.toLowerCase() === 'formdata') return 'cURL is not printed as the request body is not a JSON'
|
package/lib/helper/WebDriver.js
CHANGED
|
@@ -1,32 +1,45 @@
|
|
|
1
1
|
let webdriverio
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
const {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
3
|
+
import assert from 'assert'
|
|
4
|
+
import path from 'path'
|
|
5
|
+
import crypto from 'crypto'
|
|
6
|
+
import Helper from '@codeceptjs/helper'
|
|
7
|
+
import promiseRetry from 'promise-retry'
|
|
8
|
+
import { includes as stringIncludes } from '../assert/include.js'
|
|
9
|
+
import { urlEquals, equals } from '../assert/equal.js'
|
|
10
|
+
import store from '../store.js'
|
|
11
|
+
import output from '../output.js'
|
|
12
|
+
const { debug } = output
|
|
13
|
+
import { empty } from '../assert/empty.js'
|
|
14
|
+
import { truth } from '../assert/truth.js'
|
|
15
|
+
import { xpathLocator, fileExists, decodeUrl, chunkArray, convertCssPropertiesToCamelCase, screenshotOutputFolder, getNormalizedKeyAttributeValue, modifierKeys } from '../utils.js'
|
|
16
|
+
import { isColorProperty, convertColorToRGBA } from '../colorUtils.js'
|
|
17
|
+
import ElementNotFound from './errors/ElementNotFound.js'
|
|
18
|
+
import ConnectionRefused from './errors/ConnectionRefused.js'
|
|
19
|
+
import Locator from '../locator.js'
|
|
20
|
+
import { highlightElement } from './scripts/highlightElement.js'
|
|
21
|
+
import { focusElement } from './scripts/focusElement.js'
|
|
22
|
+
import { blurElement } from './scripts/blurElement.js'
|
|
23
|
+
import { dontSeeElementError, seeElementError, seeElementInDOMError, dontSeeElementInDOMError } from './errors/ElementAssertion.js'
|
|
24
|
+
import { dontSeeTraffic, seeTraffic, grabRecordedNetworkTraffics, stopRecordingTraffic, flushNetworkTraffics } from './network/actions.js'
|
|
25
25
|
|
|
26
26
|
const SHADOW = 'shadow'
|
|
27
27
|
const webRoot = 'body'
|
|
28
28
|
let browserLogs = []
|
|
29
29
|
|
|
30
|
+
/**
|
|
31
|
+
* Wraps error objects that don't have a proper message property
|
|
32
|
+
* This is needed for ESM compatibility with WebdriverIO error handling
|
|
33
|
+
*/
|
|
34
|
+
function wrapError(e) {
|
|
35
|
+
if (e && typeof e === 'object' && !e.message) {
|
|
36
|
+
const err = new Error(e.error || e.timeoutMsg || String(e))
|
|
37
|
+
err.stack = e.stack
|
|
38
|
+
return err
|
|
39
|
+
}
|
|
40
|
+
return e
|
|
41
|
+
}
|
|
42
|
+
|
|
30
43
|
/**
|
|
31
44
|
* ## Configuration
|
|
32
45
|
*
|
|
@@ -428,7 +441,7 @@ const config = {}
|
|
|
428
441
|
class WebDriver extends Helper {
|
|
429
442
|
constructor(config) {
|
|
430
443
|
super(config)
|
|
431
|
-
webdriverio
|
|
444
|
+
// webdriverio will be loaded dynamically in _init method
|
|
432
445
|
|
|
433
446
|
// set defaults
|
|
434
447
|
this.root = webRoot
|
|
@@ -534,12 +547,26 @@ class WebDriver extends Helper {
|
|
|
534
547
|
|
|
535
548
|
static _checkRequirements() {
|
|
536
549
|
try {
|
|
537
|
-
|
|
550
|
+
// In ESM, webdriverio will be checked via dynamic import in _init
|
|
551
|
+
// The import will fail at module load time if webdriverio is missing
|
|
552
|
+
return null
|
|
538
553
|
} catch (e) {
|
|
539
554
|
return ['webdriverio@^6.12.1']
|
|
540
555
|
}
|
|
541
556
|
}
|
|
542
557
|
|
|
558
|
+
async _init() {
|
|
559
|
+
// Load webdriverio dynamically
|
|
560
|
+
if (!webdriverio) {
|
|
561
|
+
try {
|
|
562
|
+
webdriverio = await import('webdriverio')
|
|
563
|
+
webdriverio = webdriverio.default || webdriverio
|
|
564
|
+
} catch (e) {
|
|
565
|
+
throw new Error('webdriverio could not be loaded. Please install webdriverio.')
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
|
|
543
570
|
static _config() {
|
|
544
571
|
return [
|
|
545
572
|
{
|
|
@@ -644,6 +671,7 @@ class WebDriver extends Helper {
|
|
|
644
671
|
}
|
|
645
672
|
|
|
646
673
|
async _before() {
|
|
674
|
+
if (!webdriverio) await this._init()
|
|
647
675
|
this.context = this.root
|
|
648
676
|
if (this.options.restart && !this.options.manualStart) return this._startBrowser()
|
|
649
677
|
if (!this.isRunning && !this.options.manualStart) return this._startBrowser()
|
|
@@ -842,7 +870,7 @@ class WebDriver extends Helper {
|
|
|
842
870
|
* @param {CodeceptJS.LocatorOrString} locator element located by CSS|XPath|strict locator.
|
|
843
871
|
*/
|
|
844
872
|
async _locate(locator, smartWait = false) {
|
|
845
|
-
if (
|
|
873
|
+
if (store.debugMode) smartWait = false
|
|
846
874
|
|
|
847
875
|
// special locator type for Shadow DOM
|
|
848
876
|
if (this._isShadowLocator(locator)) {
|
|
@@ -862,6 +890,17 @@ class WebDriver extends Helper {
|
|
|
862
890
|
return els
|
|
863
891
|
}
|
|
864
892
|
|
|
893
|
+
// special locator type for ARIA roles
|
|
894
|
+
if (locator.role) {
|
|
895
|
+
return this._locateByRole(locator)
|
|
896
|
+
}
|
|
897
|
+
|
|
898
|
+
// Handle role locators passed as Locator instances
|
|
899
|
+
const matchedLocator = new Locator(locator)
|
|
900
|
+
if (matchedLocator.isRole()) {
|
|
901
|
+
return this._locateByRole(matchedLocator.locator)
|
|
902
|
+
}
|
|
903
|
+
|
|
865
904
|
if (!this.options.smartWait || !smartWait) {
|
|
866
905
|
if (this._isCustomLocator(locator)) {
|
|
867
906
|
const locatorObj = new Locator(locator)
|
|
@@ -933,24 +972,39 @@ class WebDriver extends Helper {
|
|
|
933
972
|
}
|
|
934
973
|
|
|
935
974
|
/**
|
|
936
|
-
*
|
|
975
|
+
* Locate elements by ARIA role using WebdriverIO accessibility selectors
|
|
937
976
|
*
|
|
977
|
+
* @param {object} locator - role locator object { role: string, text?: string, exact?: boolean }
|
|
938
978
|
*/
|
|
939
|
-
async
|
|
940
|
-
const
|
|
941
|
-
|
|
979
|
+
async _locateByRole(locator) {
|
|
980
|
+
const role = locator.role
|
|
981
|
+
|
|
982
|
+
if (!locator.text) {
|
|
983
|
+
return this.browser.$$(`[role="${role}"]`)
|
|
984
|
+
}
|
|
985
|
+
|
|
986
|
+
const elements = await this.browser.$$(`[role="${role}"]`)
|
|
987
|
+
const filteredElements = []
|
|
988
|
+
const matchFn = locator.exact === true
|
|
989
|
+
? t => t === locator.text
|
|
990
|
+
: t => t && t.includes(locator.text)
|
|
991
|
+
|
|
992
|
+
for (const element of elements) {
|
|
993
|
+
const texts = await getElementTextAttributes.call(this, element)
|
|
994
|
+
if (texts.some(matchFn)) {
|
|
995
|
+
filteredElements.push(element)
|
|
996
|
+
}
|
|
997
|
+
}
|
|
998
|
+
|
|
999
|
+
return filteredElements
|
|
942
1000
|
}
|
|
943
1001
|
|
|
944
1002
|
/**
|
|
945
|
-
* {{>
|
|
1003
|
+
* {{> grabWebElements }}
|
|
946
1004
|
*
|
|
947
1005
|
*/
|
|
948
|
-
async
|
|
949
|
-
|
|
950
|
-
if (elements.length === 0) {
|
|
951
|
-
throw new ElementNotFound(locator, 'Element')
|
|
952
|
-
}
|
|
953
|
-
return new WebElement(elements[0], this)
|
|
1006
|
+
async grabWebElements(locator) {
|
|
1007
|
+
return this._locate(locator)
|
|
954
1008
|
}
|
|
955
1009
|
|
|
956
1010
|
/**
|
|
@@ -1224,7 +1278,8 @@ class WebDriver extends Helper {
|
|
|
1224
1278
|
const elementId = getElementId(elem)
|
|
1225
1279
|
highlightActiveElement.call(this, elem)
|
|
1226
1280
|
|
|
1227
|
-
const isSelected = await this.browser
|
|
1281
|
+
const isSelected = await isElementChecked(this.browser, elementId)
|
|
1282
|
+
|
|
1228
1283
|
if (isSelected) return Promise.resolve(true)
|
|
1229
1284
|
return this.browser[clickMethod](elementId)
|
|
1230
1285
|
}
|
|
@@ -1244,7 +1299,8 @@ class WebDriver extends Helper {
|
|
|
1244
1299
|
const elementId = getElementId(elem)
|
|
1245
1300
|
highlightActiveElement.call(this, elem)
|
|
1246
1301
|
|
|
1247
|
-
const isSelected = await this.browser
|
|
1302
|
+
const isSelected = await isElementChecked(this.browser, elementId)
|
|
1303
|
+
|
|
1248
1304
|
if (!isSelected) return Promise.resolve(true)
|
|
1249
1305
|
return this.browser[clickMethod](elementId)
|
|
1250
1306
|
}
|
|
@@ -1773,7 +1829,7 @@ class WebDriver extends Helper {
|
|
|
1773
1829
|
try {
|
|
1774
1830
|
await elem.moveTo({ xOffset, yOffset })
|
|
1775
1831
|
} catch (e) {
|
|
1776
|
-
debug(e.message)
|
|
1832
|
+
output.debug(e.message)
|
|
1777
1833
|
}
|
|
1778
1834
|
}
|
|
1779
1835
|
|
|
@@ -2302,10 +2358,14 @@ class WebDriver extends Helper {
|
|
|
2302
2358
|
res = usingFirstElement(res)
|
|
2303
2359
|
assertElementExists(res, locator)
|
|
2304
2360
|
|
|
2305
|
-
return res
|
|
2306
|
-
|
|
2307
|
-
|
|
2308
|
-
|
|
2361
|
+
return res
|
|
2362
|
+
.waitForClickable({
|
|
2363
|
+
timeout: waitTimeout * 1000,
|
|
2364
|
+
timeoutMsg: `element ${res.selector} still not clickable after ${waitTimeout} sec`,
|
|
2365
|
+
})
|
|
2366
|
+
.catch(e => {
|
|
2367
|
+
throw wrapError(e)
|
|
2368
|
+
})
|
|
2309
2369
|
}
|
|
2310
2370
|
|
|
2311
2371
|
/**
|
|
@@ -2327,6 +2387,7 @@ class WebDriver extends Helper {
|
|
|
2327
2387
|
{ timeout: aSec * 1000 },
|
|
2328
2388
|
)
|
|
2329
2389
|
.catch(e => {
|
|
2390
|
+
e = wrapError(e)
|
|
2330
2391
|
if (e.message.indexOf('timeout')) {
|
|
2331
2392
|
throw new Error(`expected url to include ${urlPart}, but found ${currUrl}`)
|
|
2332
2393
|
}
|
|
@@ -2352,6 +2413,7 @@ class WebDriver extends Helper {
|
|
|
2352
2413
|
})
|
|
2353
2414
|
}, aSec * 1000)
|
|
2354
2415
|
.catch(e => {
|
|
2416
|
+
e = wrapError(e)
|
|
2355
2417
|
if (e.message.indexOf('timeout')) {
|
|
2356
2418
|
throw new Error(`expected url to be ${urlPart}, but found ${currUrl}`)
|
|
2357
2419
|
}
|
|
@@ -2438,21 +2500,25 @@ class WebDriver extends Helper {
|
|
|
2438
2500
|
async waitNumberOfVisibleElements(locator, num, sec = null) {
|
|
2439
2501
|
const aSec = sec || this.options.waitForTimeoutInSeconds
|
|
2440
2502
|
|
|
2441
|
-
return this.browser
|
|
2442
|
-
|
|
2443
|
-
|
|
2444
|
-
|
|
2445
|
-
|
|
2446
|
-
|
|
2447
|
-
|
|
2448
|
-
|
|
2449
|
-
|
|
2450
|
-
|
|
2451
|
-
|
|
2452
|
-
|
|
2453
|
-
|
|
2454
|
-
|
|
2455
|
-
|
|
2503
|
+
return this.browser
|
|
2504
|
+
.waitUntil(
|
|
2505
|
+
async () => {
|
|
2506
|
+
const res = await this._res(locator)
|
|
2507
|
+
if (!res || res.length === 0) return false
|
|
2508
|
+
let selected = await forEachAsync(res, async el => el.isDisplayed())
|
|
2509
|
+
|
|
2510
|
+
if (!Array.isArray(selected)) selected = [selected]
|
|
2511
|
+
selected = selected.filter(val => val === true)
|
|
2512
|
+
return selected.length === num
|
|
2513
|
+
},
|
|
2514
|
+
{
|
|
2515
|
+
timeout: aSec * 1000,
|
|
2516
|
+
timeoutMsg: `The number of elements (${new Locator(locator)}) is not ${num} after ${aSec} sec`,
|
|
2517
|
+
},
|
|
2518
|
+
)
|
|
2519
|
+
.catch(e => {
|
|
2520
|
+
throw wrapError(e)
|
|
2521
|
+
})
|
|
2456
2522
|
}
|
|
2457
2523
|
|
|
2458
2524
|
/**
|
|
@@ -2609,7 +2675,6 @@ class WebDriver extends Helper {
|
|
|
2609
2675
|
*/
|
|
2610
2676
|
async openNewTab(url = 'about:blank', windowName = null) {
|
|
2611
2677
|
const client = this.browser
|
|
2612
|
-
const crypto = require('crypto')
|
|
2613
2678
|
if (windowName == null) {
|
|
2614
2679
|
windowName = crypto.randomBytes(32).toString('hex')
|
|
2615
2680
|
}
|
|
@@ -2806,6 +2871,7 @@ async function findClickable(locator, locateFn) {
|
|
|
2806
2871
|
}
|
|
2807
2872
|
|
|
2808
2873
|
if (locator.isAccessibilityId() && !this.isWeb) return locateFn(locator, true)
|
|
2874
|
+
if (locator.isRole()) return locateFn(locator, true)
|
|
2809
2875
|
if (!locator.isFuzzy()) return locateFn(locator, true)
|
|
2810
2876
|
|
|
2811
2877
|
let els
|
|
@@ -2820,7 +2886,15 @@ async function findClickable(locator, locateFn) {
|
|
|
2820
2886
|
els = await locateFn(Locator.clickable.self(literal))
|
|
2821
2887
|
if (els.length) return els
|
|
2822
2888
|
|
|
2823
|
-
|
|
2889
|
+
// Try ARIA selector for accessible name
|
|
2890
|
+
try {
|
|
2891
|
+
els = await this.browser.$$(`aria/${locator.value}`)
|
|
2892
|
+
if (els.length) return els
|
|
2893
|
+
} catch (e) {
|
|
2894
|
+
// ARIA selector not supported or failed
|
|
2895
|
+
}
|
|
2896
|
+
|
|
2897
|
+
return await locateFn(locator.value) // by css or xpath
|
|
2824
2898
|
}
|
|
2825
2899
|
|
|
2826
2900
|
async function findFields(locator) {
|
|
@@ -2831,6 +2905,7 @@ async function findFields(locator) {
|
|
|
2831
2905
|
}
|
|
2832
2906
|
|
|
2833
2907
|
if (locator.isAccessibilityId() && !this.isWeb) return this._locate(locator, true)
|
|
2908
|
+
if (locator.isRole()) return this._locate(locator, true)
|
|
2834
2909
|
if (!locator.isFuzzy()) return this._locate(locator, true)
|
|
2835
2910
|
|
|
2836
2911
|
const literal = xpathLocator.literal(locator.value)
|
|
@@ -2842,7 +2917,16 @@ async function findFields(locator) {
|
|
|
2842
2917
|
|
|
2843
2918
|
els = await this._locate(Locator.field.byName(literal))
|
|
2844
2919
|
if (els.length) return els
|
|
2845
|
-
|
|
2920
|
+
|
|
2921
|
+
// Try ARIA selector for accessible name
|
|
2922
|
+
try {
|
|
2923
|
+
els = await this.browser.$$(`aria/${locator.value}`)
|
|
2924
|
+
if (els.length) return els
|
|
2925
|
+
} catch (e) {
|
|
2926
|
+
// ARIA selector not supported or failed
|
|
2927
|
+
}
|
|
2928
|
+
|
|
2929
|
+
return await this._locate(locator.value) // by css or xpath
|
|
2846
2930
|
}
|
|
2847
2931
|
|
|
2848
2932
|
async function proceedSeeField(assertType, field, value) {
|
|
@@ -2868,13 +2952,19 @@ async function proceedSeeField(assertType, field, value) {
|
|
|
2868
2952
|
}
|
|
2869
2953
|
}
|
|
2870
2954
|
|
|
2871
|
-
const proceedSingle = el =>
|
|
2872
|
-
el.getValue()
|
|
2873
|
-
|
|
2874
|
-
|
|
2875
|
-
|
|
2876
|
-
|
|
2877
|
-
|
|
2955
|
+
const proceedSingle = async el => {
|
|
2956
|
+
let res = await el.getValue()
|
|
2957
|
+
|
|
2958
|
+
if (res === null) {
|
|
2959
|
+
res = await el.getText()
|
|
2960
|
+
}
|
|
2961
|
+
|
|
2962
|
+
if (res === null || res === undefined) {
|
|
2963
|
+
throw new Error(`Element ${el.selector} has no value attribute`)
|
|
2964
|
+
}
|
|
2965
|
+
|
|
2966
|
+
stringIncludes(`fields by ${field}`)[assertType](value, res)
|
|
2967
|
+
}
|
|
2878
2968
|
|
|
2879
2969
|
const filterBySelected = async elements => filterAsync(elements, async el => this.browser.isElementSelected(getElementId(el)))
|
|
2880
2970
|
|
|
@@ -2936,10 +3026,31 @@ async function proceedSeeCheckbox(assertType, field) {
|
|
|
2936
3026
|
const res = await findFields.call(this, field)
|
|
2937
3027
|
assertElementExists(res, field, 'Field')
|
|
2938
3028
|
|
|
2939
|
-
const selected = await forEachAsync(res, async el =>
|
|
3029
|
+
const selected = await forEachAsync(res, async el => {
|
|
3030
|
+
const elementId = getElementId(el)
|
|
3031
|
+
return isElementChecked(this.browser, elementId)
|
|
3032
|
+
})
|
|
3033
|
+
|
|
2940
3034
|
return truth(`checkable field "${field}"`, 'to be checked')[assertType](selected)
|
|
2941
3035
|
}
|
|
2942
3036
|
|
|
3037
|
+
async function getElementTextAttributes(element) {
|
|
3038
|
+
const elementId = getElementId(element)
|
|
3039
|
+
const ariaLabel = await this.browser.getElementAttribute(elementId, 'aria-label').catch(() => '')
|
|
3040
|
+
const placeholder = await this.browser.getElementAttribute(elementId, 'placeholder').catch(() => '')
|
|
3041
|
+
const innerText = await this.browser.getElementText(elementId).catch(() => '')
|
|
3042
|
+
return [ariaLabel, placeholder, innerText]
|
|
3043
|
+
}
|
|
3044
|
+
|
|
3045
|
+
async function isElementChecked(browser, elementId) {
|
|
3046
|
+
let isChecked = await browser.isElementSelected(elementId)
|
|
3047
|
+
if (!isChecked) {
|
|
3048
|
+
const ariaChecked = await browser.getElementAttribute(elementId, 'aria-checked')
|
|
3049
|
+
isChecked = ariaChecked === 'true'
|
|
3050
|
+
}
|
|
3051
|
+
return isChecked
|
|
3052
|
+
}
|
|
3053
|
+
|
|
2943
3054
|
async function findCheckable(locator, locateFn) {
|
|
2944
3055
|
let els
|
|
2945
3056
|
locator = new Locator(locator)
|
|
@@ -2949,6 +3060,7 @@ async function findCheckable(locator, locateFn) {
|
|
|
2949
3060
|
}
|
|
2950
3061
|
|
|
2951
3062
|
if (locator.isAccessibilityId() && !this.isWeb) return locateFn(locator, true)
|
|
3063
|
+
if (locator.isRole()) return locateFn(locator, true)
|
|
2952
3064
|
if (!locator.isFuzzy()) return locateFn(locator, true)
|
|
2953
3065
|
|
|
2954
3066
|
const literal = xpathLocator.literal(locator.value)
|
|
@@ -2957,7 +3069,15 @@ async function findCheckable(locator, locateFn) {
|
|
|
2957
3069
|
els = await locateFn(Locator.checkable.byName(literal))
|
|
2958
3070
|
if (els.length) return els
|
|
2959
3071
|
|
|
2960
|
-
|
|
3072
|
+
// Try ARIA selector for accessible name
|
|
3073
|
+
try {
|
|
3074
|
+
els = await this.browser.$$(`aria/${locator.value}`)
|
|
3075
|
+
if (els.length) return els
|
|
3076
|
+
} catch (e) {
|
|
3077
|
+
// ARIA selector not supported or failed
|
|
3078
|
+
}
|
|
3079
|
+
|
|
3080
|
+
return await locateFn(locator.value) // by css or xpath
|
|
2961
3081
|
}
|
|
2962
3082
|
|
|
2963
3083
|
function withStrictLocator(locator) {
|
|
@@ -3166,4 +3286,4 @@ function logEvents(event) {
|
|
|
3166
3286
|
browserLogs.push(event.text) // add log message to the array
|
|
3167
3287
|
}
|
|
3168
3288
|
|
|
3169
|
-
|
|
3289
|
+
export { WebDriver as default }
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
function ConnectionRefused(err) {
|
|
2
|
-
this.message = "Can't connect to WebDriver.\n"
|
|
3
|
-
this.message += `${err}\n\n
|
|
4
|
-
this.message += 'Please make sure Selenium Server is running and accessible'
|
|
5
|
-
this.stack = err.stack
|
|
2
|
+
this.message = "Can't connect to WebDriver.\n"
|
|
3
|
+
this.message += `${err}\n\n`
|
|
4
|
+
this.message += 'Please make sure Selenium Server is running and accessible'
|
|
5
|
+
this.stack = err.stack
|
|
6
6
|
}
|
|
7
7
|
|
|
8
|
-
ConnectionRefused.prototype = Object.create(Error.prototype)
|
|
8
|
+
ConnectionRefused.prototype = Object.create(Error.prototype)
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
export default ConnectionRefused
|
|
@@ -1,38 +1,33 @@
|
|
|
1
|
-
|
|
1
|
+
import Locator from '../../locator.js'
|
|
2
2
|
|
|
3
|
-
const prefixMessage = 'Element'
|
|
3
|
+
const prefixMessage = 'Element'
|
|
4
4
|
|
|
5
5
|
function seeElementError(locator) {
|
|
6
6
|
if (typeof locator === 'object') {
|
|
7
|
-
locator = JSON.stringify(locator)
|
|
7
|
+
locator = JSON.stringify(locator)
|
|
8
8
|
}
|
|
9
|
-
throw new Error(`${prefixMessage} "${
|
|
9
|
+
throw new Error(`${prefixMessage} "${new Locator(locator)}" is still visible on page.`)
|
|
10
10
|
}
|
|
11
11
|
|
|
12
12
|
function seeElementInDOMError(locator) {
|
|
13
13
|
if (typeof locator === 'object') {
|
|
14
|
-
locator = JSON.stringify(locator)
|
|
14
|
+
locator = JSON.stringify(locator)
|
|
15
15
|
}
|
|
16
|
-
throw new Error(`${prefixMessage} "${
|
|
16
|
+
throw new Error(`${prefixMessage} "${new Locator(locator)}" is still seen in DOM.`)
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
function dontSeeElementError(locator) {
|
|
20
20
|
if (typeof locator === 'object') {
|
|
21
|
-
locator = JSON.stringify(locator)
|
|
21
|
+
locator = JSON.stringify(locator)
|
|
22
22
|
}
|
|
23
|
-
throw new Error(`${prefixMessage} "${
|
|
23
|
+
throw new Error(`${prefixMessage} "${new Locator(locator)}" is not visible on page.`)
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
function dontSeeElementInDOMError(locator) {
|
|
27
27
|
if (typeof locator === 'object') {
|
|
28
|
-
locator = JSON.stringify(locator)
|
|
28
|
+
locator = JSON.stringify(locator)
|
|
29
29
|
}
|
|
30
|
-
throw new Error(`${prefixMessage} "${
|
|
30
|
+
throw new Error(`${prefixMessage} "${new Locator(locator)}" is not seen in DOM.`)
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
-
|
|
34
|
-
seeElementError,
|
|
35
|
-
dontSeeElementError,
|
|
36
|
-
seeElementInDOMError,
|
|
37
|
-
dontSeeElementInDOMError,
|
|
38
|
-
};
|
|
33
|
+
export { seeElementError, dontSeeElementError, seeElementInDOMError, dontSeeElementInDOMError }
|
|
@@ -1,19 +1,15 @@
|
|
|
1
|
-
|
|
1
|
+
import Locator from '../../locator.js'
|
|
2
2
|
/**
|
|
3
3
|
* Uses to throw readable element not found error
|
|
4
4
|
* Stringify object's locators
|
|
5
5
|
*/
|
|
6
6
|
class ElementNotFound {
|
|
7
|
-
constructor(
|
|
8
|
-
locator,
|
|
9
|
-
prefixMessage = 'Element',
|
|
10
|
-
postfixMessage = 'was not found by text|CSS|XPath',
|
|
11
|
-
) {
|
|
7
|
+
constructor(locator, prefixMessage = 'Element', postfixMessage = 'was not found by text|CSS|XPath') {
|
|
12
8
|
if (typeof locator === 'object') {
|
|
13
|
-
locator = JSON.stringify(locator)
|
|
9
|
+
locator = JSON.stringify(locator)
|
|
14
10
|
}
|
|
15
|
-
throw new Error(`${prefixMessage} "${
|
|
11
|
+
throw new Error(`${prefixMessage} "${new Locator(locator)}" ${postfixMessage}`)
|
|
16
12
|
}
|
|
17
13
|
}
|
|
18
14
|
|
|
19
|
-
|
|
15
|
+
export default ElementNotFound
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
function RemoteBrowserConnectionRefused(err) {
|
|
2
|
-
this.message = 'Cannot connect to websocket endpoint.\n\n'
|
|
3
|
-
this.message += 'Please make sure remote browser is running and accessible.'
|
|
4
|
-
this.stack = err.error || err
|
|
2
|
+
this.message = 'Cannot connect to websocket endpoint.\n\n'
|
|
3
|
+
this.message += 'Please make sure remote browser is running and accessible.'
|
|
4
|
+
this.stack = err.error || err
|
|
5
5
|
}
|
|
6
6
|
|
|
7
|
-
RemoteBrowserConnectionRefused.prototype = Object.create(Error.prototype)
|
|
7
|
+
RemoteBrowserConnectionRefused.prototype = Object.create(Error.prototype)
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
export default RemoteBrowserConnectionRefused
|
|
@@ -3,31 +3,31 @@
|
|
|
3
3
|
*/
|
|
4
4
|
class Console {
|
|
5
5
|
constructor() {
|
|
6
|
-
this._logEntries = []
|
|
6
|
+
this._logEntries = []
|
|
7
7
|
}
|
|
8
8
|
|
|
9
9
|
get entries() {
|
|
10
|
-
return this._logEntries
|
|
10
|
+
return this._logEntries
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
clear() {
|
|
14
|
-
this._logEntries = []
|
|
14
|
+
this._logEntries = []
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
includes(msg) {
|
|
18
|
-
const prev = this._logEntries[this._logEntries.length - 1]
|
|
19
|
-
if (!prev) return false
|
|
20
|
-
const text = msg.text && msg.text() || msg._text || ''
|
|
21
|
-
const prevText = prev.text && prev.text() || prev._text || ''
|
|
22
|
-
return text === prevText
|
|
18
|
+
const prev = this._logEntries[this._logEntries.length - 1]
|
|
19
|
+
if (!prev) return false
|
|
20
|
+
const text = (msg.text && msg.text()) || msg._text || ''
|
|
21
|
+
const prevText = (prev.text && prev.text()) || prev._text || ''
|
|
22
|
+
return text === prevText
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
add(entry) {
|
|
26
26
|
if (Array.isArray(entry)) {
|
|
27
|
-
this._logEntries = this._logEntries.concat(entry)
|
|
27
|
+
this._logEntries = this._logEntries.concat(entry)
|
|
28
28
|
}
|
|
29
|
-
this._logEntries.push(entry)
|
|
29
|
+
this._logEntries.push(entry)
|
|
30
30
|
}
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
-
|
|
33
|
+
export default Console
|