codeceptjs 4.0.0-beta.4 → 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 +89 -119
- package/bin/codecept.js +53 -54
- package/docs/webapi/clearCookie.mustache +1 -1
- package/lib/actor.js +70 -102
- package/lib/ai.js +131 -121
- package/lib/assert/empty.js +11 -12
- package/lib/assert/equal.js +16 -21
- package/lib/assert/error.js +2 -2
- package/lib/assert/include.js +11 -15
- package/lib/assert/throws.js +3 -5
- package/lib/assert/truth.js +10 -7
- package/lib/assert.js +18 -18
- package/lib/codecept.js +112 -101
- package/lib/colorUtils.js +48 -50
- package/lib/command/check.js +206 -0
- package/lib/command/configMigrate.js +13 -14
- package/lib/command/definitions.js +24 -36
- package/lib/command/dryRun.js +16 -16
- package/lib/command/generate.js +38 -39
- package/lib/command/gherkin/init.js +36 -38
- package/lib/command/gherkin/snippets.js +76 -74
- package/lib/command/gherkin/steps.js +21 -18
- package/lib/command/info.js +49 -15
- package/lib/command/init.js +41 -37
- package/lib/command/interactive.js +22 -13
- package/lib/command/list.js +11 -10
- package/lib/command/run-multiple/chunk.js +50 -47
- package/lib/command/run-multiple/collection.js +5 -5
- package/lib/command/run-multiple/run.js +3 -3
- package/lib/command/run-multiple.js +27 -47
- package/lib/command/run-rerun.js +6 -7
- package/lib/command/run-workers.js +15 -66
- package/lib/command/run.js +8 -8
- package/lib/command/utils.js +22 -21
- package/lib/command/workers/runTests.js +131 -241
- package/lib/config.js +111 -49
- package/lib/container.js +589 -244
- package/lib/data/context.js +16 -18
- package/lib/data/dataScenarioConfig.js +9 -9
- package/lib/data/dataTableArgument.js +7 -7
- package/lib/data/table.js +6 -12
- package/lib/effects.js +307 -0
- package/lib/els.js +160 -0
- package/lib/event.js +24 -19
- package/lib/globals.js +141 -0
- package/lib/heal.js +89 -81
- package/lib/helper/AI.js +3 -2
- package/lib/helper/ApiDataFactory.js +19 -19
- package/lib/helper/Appium.js +47 -51
- package/lib/helper/FileSystem.js +35 -15
- package/lib/helper/GraphQL.js +1 -1
- package/lib/helper/GraphQLDataFactory.js +4 -4
- package/lib/helper/JSONResponse.js +72 -45
- package/lib/helper/Mochawesome.js +14 -11
- package/lib/helper/Playwright.js +832 -434
- package/lib/helper/Puppeteer.js +393 -292
- package/lib/helper/REST.js +32 -27
- package/lib/helper/WebDriver.js +320 -219
- 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 +22 -22
- 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 +54 -0
- package/lib/listener/exit.js +10 -12
- package/lib/listener/{retry.js → globalRetry.js} +10 -10
- package/lib/listener/globalTimeout.js +166 -0
- package/lib/listener/helpers.js +43 -24
- package/lib/listener/mocha.js +4 -5
- package/lib/listener/result.js +11 -0
- package/lib/listener/steps.js +26 -23
- package/lib/listener/store.js +20 -0
- package/lib/locator.js +213 -192
- package/lib/mocha/asyncWrapper.js +264 -0
- package/lib/mocha/bdd.js +167 -0
- package/lib/mocha/cli.js +341 -0
- package/lib/mocha/factory.js +160 -0
- package/lib/{interfaces → mocha}/featureConfig.js +33 -13
- package/lib/{interfaces → mocha}/gherkin.js +75 -45
- package/lib/mocha/hooks.js +121 -0
- package/lib/mocha/index.js +21 -0
- package/lib/mocha/inject.js +46 -0
- package/lib/{interfaces → mocha}/scenarioConfig.js +32 -8
- package/lib/mocha/suite.js +89 -0
- package/lib/mocha/test.js +178 -0
- package/lib/mocha/types.d.ts +42 -0
- package/lib/mocha/ui.js +229 -0
- package/lib/output.js +86 -64
- package/lib/parser.js +44 -44
- package/lib/pause.js +160 -139
- package/lib/plugin/analyze.js +403 -0
- package/lib/plugin/{autoLogin.js → auth.js} +137 -43
- package/lib/plugin/autoDelay.js +19 -15
- package/lib/plugin/coverage.js +22 -27
- package/lib/plugin/customLocator.js +5 -5
- package/lib/plugin/customReporter.js +53 -0
- package/lib/plugin/heal.js +49 -17
- package/lib/plugin/pageInfo.js +140 -0
- package/lib/plugin/pauseOnFail.js +4 -3
- package/lib/plugin/retryFailedStep.js +60 -19
- package/lib/plugin/screenshotOnFail.js +80 -83
- package/lib/plugin/stepByStepReport.js +70 -31
- package/lib/plugin/stepTimeout.js +7 -13
- package/lib/plugin/subtitles.js +10 -9
- package/lib/recorder.js +167 -126
- package/lib/rerun.js +94 -50
- package/lib/result.js +161 -0
- package/lib/secret.js +18 -17
- package/lib/session.js +95 -89
- package/lib/step/base.js +239 -0
- package/lib/step/comment.js +10 -0
- package/lib/step/config.js +50 -0
- package/lib/step/func.js +46 -0
- package/lib/step/helper.js +50 -0
- package/lib/step/meta.js +99 -0
- package/lib/step/record.js +74 -0
- package/lib/step/retry.js +11 -0
- package/lib/step/section.js +55 -0
- package/lib/step.js +18 -332
- package/lib/steps.js +54 -0
- package/lib/store.js +37 -5
- package/lib/template/heal.js +2 -11
- package/lib/timeout.js +60 -0
- package/lib/transform.js +8 -8
- package/lib/translation.js +32 -18
- package/lib/utils.js +354 -250
- package/lib/workerStorage.js +16 -16
- package/lib/workers.js +366 -282
- package/package.json +107 -95
- package/translations/de-DE.js +5 -4
- package/translations/fr-FR.js +5 -4
- package/translations/index.js +23 -9
- package/translations/it-IT.js +5 -4
- package/translations/ja-JP.js +5 -4
- package/translations/nl-NL.js +76 -0
- package/translations/pl-PL.js +5 -4
- package/translations/pt-BR.js +5 -4
- package/translations/ru-RU.js +5 -4
- package/translations/utils.js +18 -0
- package/translations/zh-CN.js +5 -4
- package/translations/zh-TW.js +5 -4
- package/typings/index.d.ts +177 -186
- package/typings/promiseBasedTypes.d.ts +3573 -5941
- package/typings/types.d.ts +4042 -6370
- package/lib/cli.js +0 -256
- package/lib/helper/ExpectHelper.js +0 -391
- package/lib/helper/Nightmare.js +0 -1504
- package/lib/helper/Protractor.js +0 -1863
- package/lib/helper/SoftExpectHelper.js +0 -381
- package/lib/helper/TestCafe.js +0 -1414
- 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 -62
- package/lib/interfaces/bdd.js +0 -81
- package/lib/listener/artifacts.js +0 -19
- package/lib/listener/timeout.js +0 -109
- package/lib/mochaFactory.js +0 -113
- package/lib/plugin/allure.js +0 -15
- package/lib/plugin/commentStep.js +0 -136
- package/lib/plugin/debugErrors.js +0 -67
- package/lib/plugin/eachElement.js +0 -127
- package/lib/plugin/fakerTransform.js +0 -49
- package/lib/plugin/retryTo.js +0 -127
- package/lib/plugin/selenoid.js +0 -384
- package/lib/plugin/standardActingHelpers.js +0 -3
- package/lib/plugin/tryTo.js +0 -115
- package/lib/plugin/wdio.js +0 -249
- package/lib/scenario.js +0 -224
- package/lib/ui.js +0 -236
- package/lib/within.js +0 -70
package/lib/helper/WebDriver.js
CHANGED
|
@@ -1,48 +1,44 @@
|
|
|
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
|
-
|
|
25
|
-
const ConnectionRefused = require('./errors/ConnectionRefused')
|
|
26
|
-
const Locator = require('../locator')
|
|
27
|
-
const { highlightElement } = require('./scripts/highlightElement')
|
|
28
|
-
const { focusElement } = require('./scripts/focusElement')
|
|
29
|
-
const { blurElement } = require('./scripts/blurElement')
|
|
30
|
-
const {
|
|
31
|
-
dontSeeElementError,
|
|
32
|
-
seeElementError,
|
|
33
|
-
seeElementInDOMError,
|
|
34
|
-
dontSeeElementInDOMError,
|
|
35
|
-
} = require('./errors/ElementAssertion')
|
|
36
|
-
const {
|
|
37
|
-
dontSeeTraffic,
|
|
38
|
-
seeTraffic,
|
|
39
|
-
grabRecordedNetworkTraffics,
|
|
40
|
-
stopRecordingTraffic,
|
|
41
|
-
flushNetworkTraffics,
|
|
42
|
-
} = require('./network/actions')
|
|
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'
|
|
43
25
|
|
|
44
26
|
const SHADOW = 'shadow'
|
|
45
27
|
const webRoot = 'body'
|
|
28
|
+
let browserLogs = []
|
|
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
|
+
}
|
|
46
42
|
|
|
47
43
|
/**
|
|
48
44
|
* ## Configuration
|
|
@@ -53,6 +49,7 @@ const webRoot = 'body'
|
|
|
53
49
|
* @type {object}
|
|
54
50
|
* @prop {string} url - base url of website to be tested.
|
|
55
51
|
* @prop {string} browser - Browser in which to perform testing.
|
|
52
|
+
* @prop {boolean} [bidiProtocol=false] - WebDriver Bidi Protocol. Default: false. More info: https://webdriver.io/docs/api/webdriverBidi/
|
|
56
53
|
* @prop {string} [basicAuth] - (optional) the basic authentication to pass to base url. Example: {username: 'username', password: 'password'}
|
|
57
54
|
* @prop {string} [host=localhost] - WebDriver host to connect.
|
|
58
55
|
* @prop {number} [port=4444] - WebDriver port to connect.
|
|
@@ -444,7 +441,7 @@ const config = {}
|
|
|
444
441
|
class WebDriver extends Helper {
|
|
445
442
|
constructor(config) {
|
|
446
443
|
super(config)
|
|
447
|
-
webdriverio
|
|
444
|
+
// webdriverio will be loaded dynamically in _init method
|
|
448
445
|
|
|
449
446
|
// set defaults
|
|
450
447
|
this.root = webRoot
|
|
@@ -506,6 +503,10 @@ class WebDriver extends Helper {
|
|
|
506
503
|
config.capabilities = config.desiredCapabilities
|
|
507
504
|
}
|
|
508
505
|
config.capabilities.browserName = config.browser || config.capabilities.browserName
|
|
506
|
+
|
|
507
|
+
// WebDriver Bidi Protocol. Default: false
|
|
508
|
+
config.capabilities.webSocketUrl = config.bidiProtocol ?? config.capabilities.webSocketUrl ?? true
|
|
509
|
+
|
|
509
510
|
config.capabilities.browserVersion = config.browserVersion || config.capabilities.browserVersion
|
|
510
511
|
if (config.capabilities.chromeOptions) {
|
|
511
512
|
config.capabilities['goog:chromeOptions'] = config.capabilities.chromeOptions
|
|
@@ -546,12 +547,26 @@ class WebDriver extends Helper {
|
|
|
546
547
|
|
|
547
548
|
static _checkRequirements() {
|
|
548
549
|
try {
|
|
549
|
-
|
|
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
|
|
550
553
|
} catch (e) {
|
|
551
554
|
return ['webdriverio@^6.12.1']
|
|
552
555
|
}
|
|
553
556
|
}
|
|
554
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
|
+
|
|
555
570
|
static _config() {
|
|
556
571
|
return [
|
|
557
572
|
{
|
|
@@ -595,10 +610,7 @@ class WebDriver extends Helper {
|
|
|
595
610
|
}
|
|
596
611
|
|
|
597
612
|
async _res(locator) {
|
|
598
|
-
const res =
|
|
599
|
-
this._isShadowLocator(locator) || this._isCustomLocator(locator)
|
|
600
|
-
? await this._locate(locator)
|
|
601
|
-
: await this.$$(withStrictLocator(locator))
|
|
613
|
+
const res = this._isShadowLocator(locator) || this._isCustomLocator(locator) ? await this._locate(locator) : await this.$$(withStrictLocator(locator))
|
|
602
614
|
return res
|
|
603
615
|
}
|
|
604
616
|
|
|
@@ -631,7 +643,7 @@ class WebDriver extends Helper {
|
|
|
631
643
|
this.$$ = this.browser.$$.bind(this.browser)
|
|
632
644
|
|
|
633
645
|
if (this._isCustomLocatorStrategyDefined()) {
|
|
634
|
-
Object.keys(this.customLocatorStrategies).forEach(async
|
|
646
|
+
Object.keys(this.customLocatorStrategies).forEach(async customLocator => {
|
|
635
647
|
this.debugSection('Weddriver', `adding custom locator strategy: ${customLocator}`)
|
|
636
648
|
const locatorFunction = this._lookupCustomLocator(customLocator)
|
|
637
649
|
this.browser.addLocatorStrategy(customLocator, locatorFunction)
|
|
@@ -642,6 +654,11 @@ class WebDriver extends Helper {
|
|
|
642
654
|
this.browser.capabilities.platformName = this.browser.capabilities.platformName.toLowerCase()
|
|
643
655
|
}
|
|
644
656
|
|
|
657
|
+
this.browser.on('dialog', () => {})
|
|
658
|
+
|
|
659
|
+
await this.browser.sessionSubscribe({ events: ['log.entryAdded'] })
|
|
660
|
+
this.browser.on('log.entryAdded', logEvents)
|
|
661
|
+
|
|
645
662
|
return this.browser
|
|
646
663
|
}
|
|
647
664
|
|
|
@@ -654,6 +671,7 @@ class WebDriver extends Helper {
|
|
|
654
671
|
}
|
|
655
672
|
|
|
656
673
|
async _before() {
|
|
674
|
+
if (!webdriverio) await this._init()
|
|
657
675
|
this.context = this.root
|
|
658
676
|
if (this.options.restart && !this.options.manualStart) return this._startBrowser()
|
|
659
677
|
if (!this.isRunning && !this.options.manualStart) return this._startBrowser()
|
|
@@ -667,7 +685,7 @@ class WebDriver extends Helper {
|
|
|
667
685
|
this.isRunning = false
|
|
668
686
|
return this.browser.deleteSession()
|
|
669
687
|
}
|
|
670
|
-
if (this.browser.isInsideFrame) await this.browser.
|
|
688
|
+
if (this.browser.isInsideFrame) await this.browser.switchFrame(null)
|
|
671
689
|
|
|
672
690
|
if (this.options.keepBrowserState) return
|
|
673
691
|
|
|
@@ -675,10 +693,11 @@ class WebDriver extends Helper {
|
|
|
675
693
|
this.debugSection('Session', 'cleaning cookies and localStorage')
|
|
676
694
|
await this.browser.deleteCookies()
|
|
677
695
|
}
|
|
678
|
-
await this.browser.execute('localStorage.clear();').catch(
|
|
696
|
+
await this.browser.execute('localStorage.clear();').catch(err => {
|
|
679
697
|
if (!(err.message.indexOf("Storage is disabled inside 'data:' URLs.") > -1)) throw err
|
|
680
698
|
})
|
|
681
699
|
await this.closeOtherTabs()
|
|
700
|
+
browserLogs = []
|
|
682
701
|
return this.browser
|
|
683
702
|
}
|
|
684
703
|
|
|
@@ -705,17 +724,17 @@ class WebDriver extends Helper {
|
|
|
705
724
|
|
|
706
725
|
return browser
|
|
707
726
|
},
|
|
708
|
-
stop: async
|
|
727
|
+
stop: async browser => {
|
|
709
728
|
if (!browser) return
|
|
710
729
|
return browser.deleteSession()
|
|
711
730
|
},
|
|
712
|
-
loadVars: async
|
|
731
|
+
loadVars: async browser => {
|
|
713
732
|
if (this.context !== this.root) throw new Error("Can't start session inside within block")
|
|
714
733
|
this.browser = browser
|
|
715
734
|
this.$$ = this.browser.$$.bind(this.browser)
|
|
716
735
|
this.sessionWindows[this.activeSessionName] = browser
|
|
717
736
|
},
|
|
718
|
-
restoreVars: async
|
|
737
|
+
restoreVars: async session => {
|
|
719
738
|
if (!session) {
|
|
720
739
|
this.activeSessionName = ''
|
|
721
740
|
}
|
|
@@ -757,7 +776,7 @@ class WebDriver extends Helper {
|
|
|
757
776
|
this.browser.isInsideFrame = true
|
|
758
777
|
if (Array.isArray(frame)) {
|
|
759
778
|
// this.switchTo(null);
|
|
760
|
-
await forEachAsync(frame, async
|
|
779
|
+
await forEachAsync(frame, async f => this.switchTo(f))
|
|
761
780
|
return
|
|
762
781
|
}
|
|
763
782
|
await this.switchTo(frame)
|
|
@@ -835,10 +854,7 @@ class WebDriver extends Helper {
|
|
|
835
854
|
* @param {object} locator
|
|
836
855
|
*/
|
|
837
856
|
async _smartWait(locator) {
|
|
838
|
-
this.debugSection(
|
|
839
|
-
`SmartWait (${this.options.smartWait}ms)`,
|
|
840
|
-
`Locating ${JSON.stringify(locator)} in ${this.options.smartWait}`,
|
|
841
|
-
)
|
|
857
|
+
this.debugSection(`SmartWait (${this.options.smartWait}ms)`, `Locating ${JSON.stringify(locator)} in ${this.options.smartWait}`)
|
|
842
858
|
await this.defineTimeout({ implicit: this.options.smartWait })
|
|
843
859
|
}
|
|
844
860
|
|
|
@@ -854,7 +870,7 @@ class WebDriver extends Helper {
|
|
|
854
870
|
* @param {CodeceptJS.LocatorOrString} locator element located by CSS|XPath|strict locator.
|
|
855
871
|
*/
|
|
856
872
|
async _locate(locator, smartWait = false) {
|
|
857
|
-
if (
|
|
873
|
+
if (store.debugMode) smartWait = false
|
|
858
874
|
|
|
859
875
|
// special locator type for Shadow DOM
|
|
860
876
|
if (this._isShadowLocator(locator)) {
|
|
@@ -874,6 +890,17 @@ class WebDriver extends Helper {
|
|
|
874
890
|
return els
|
|
875
891
|
}
|
|
876
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
|
+
|
|
877
904
|
if (!this.options.smartWait || !smartWait) {
|
|
878
905
|
if (this._isCustomLocator(locator)) {
|
|
879
906
|
const locatorObj = new Locator(locator)
|
|
@@ -913,7 +940,7 @@ class WebDriver extends Helper {
|
|
|
913
940
|
* @param {CodeceptJS.LocatorOrString} locator element located by CSS|XPath|strict locator.
|
|
914
941
|
*/
|
|
915
942
|
async _locateCheckable(locator) {
|
|
916
|
-
return findCheckable.call(this, locator, this.$$.bind(this)).then(
|
|
943
|
+
return findCheckable.call(this, locator, this.$$.bind(this)).then(res => res)
|
|
917
944
|
}
|
|
918
945
|
|
|
919
946
|
/**
|
|
@@ -941,7 +968,35 @@ class WebDriver extends Helper {
|
|
|
941
968
|
* @param {CodeceptJS.LocatorOrString} locator element located by CSS|XPath|strict locator.
|
|
942
969
|
*/
|
|
943
970
|
async _locateFields(locator) {
|
|
944
|
-
return findFields.call(this, locator).then(
|
|
971
|
+
return findFields.call(this, locator).then(res => res)
|
|
972
|
+
}
|
|
973
|
+
|
|
974
|
+
/**
|
|
975
|
+
* Locate elements by ARIA role using WebdriverIO accessibility selectors
|
|
976
|
+
*
|
|
977
|
+
* @param {object} locator - role locator object { role: string, text?: string, exact?: boolean }
|
|
978
|
+
*/
|
|
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
|
|
945
1000
|
}
|
|
946
1001
|
|
|
947
1002
|
/**
|
|
@@ -1025,7 +1080,7 @@ class WebDriver extends Helper {
|
|
|
1025
1080
|
const elem = usingFirstElement(res)
|
|
1026
1081
|
highlightActiveElement.call(this, elem)
|
|
1027
1082
|
|
|
1028
|
-
return this.executeScript(
|
|
1083
|
+
return this.executeScript(el => {
|
|
1029
1084
|
if (document.activeElement instanceof HTMLElement) {
|
|
1030
1085
|
document.activeElement.blur()
|
|
1031
1086
|
}
|
|
@@ -1097,7 +1152,7 @@ class WebDriver extends Helper {
|
|
|
1097
1152
|
}
|
|
1098
1153
|
const elem = usingFirstElement(res)
|
|
1099
1154
|
|
|
1100
|
-
return this.executeScript(
|
|
1155
|
+
return this.executeScript(el => {
|
|
1101
1156
|
if (document.activeElement instanceof HTMLElement) {
|
|
1102
1157
|
document.activeElement.blur()
|
|
1103
1158
|
}
|
|
@@ -1160,15 +1215,9 @@ class WebDriver extends Helper {
|
|
|
1160
1215
|
}
|
|
1161
1216
|
|
|
1162
1217
|
// select options by visible text
|
|
1163
|
-
let els = await forEachAsync(option, async (opt)
|
|
1164
|
-
this.browser.findElementsFromElement(
|
|
1165
|
-
getElementId(elem),
|
|
1166
|
-
'xpath',
|
|
1167
|
-
Locator.select.byVisibleText(xpathLocator.literal(opt)),
|
|
1168
|
-
),
|
|
1169
|
-
)
|
|
1218
|
+
let els = await forEachAsync(option, async opt => this.browser.findElementsFromElement(getElementId(elem), 'xpath', Locator.select.byVisibleText(xpathLocator.literal(opt))))
|
|
1170
1219
|
|
|
1171
|
-
const clickOptionFn = async
|
|
1220
|
+
const clickOptionFn = async el => {
|
|
1172
1221
|
if (el[0]) el = el[0]
|
|
1173
1222
|
const elementId = getElementId(el)
|
|
1174
1223
|
if (elementId) return this.browser.elementClick(elementId)
|
|
@@ -1178,19 +1227,9 @@ class WebDriver extends Helper {
|
|
|
1178
1227
|
return forEachAsync(els, clickOptionFn)
|
|
1179
1228
|
}
|
|
1180
1229
|
// select options by value
|
|
1181
|
-
els = await forEachAsync(option, async (opt)
|
|
1182
|
-
this.browser.findElementsFromElement(
|
|
1183
|
-
getElementId(elem),
|
|
1184
|
-
'xpath',
|
|
1185
|
-
Locator.select.byValue(xpathLocator.literal(opt)),
|
|
1186
|
-
),
|
|
1187
|
-
)
|
|
1230
|
+
els = await forEachAsync(option, async opt => this.browser.findElementsFromElement(getElementId(elem), 'xpath', Locator.select.byValue(xpathLocator.literal(opt))))
|
|
1188
1231
|
if (els.length === 0) {
|
|
1189
|
-
throw new ElementNotFound(
|
|
1190
|
-
select,
|
|
1191
|
-
`Option "${option}" in`,
|
|
1192
|
-
'was not found neither by a visible text nor by a value',
|
|
1193
|
-
)
|
|
1232
|
+
throw new ElementNotFound(select, `Option "${option}" in`, 'was not found neither by a visible text nor by a value')
|
|
1194
1233
|
}
|
|
1195
1234
|
return forEachAsync(els, clickOptionFn)
|
|
1196
1235
|
}
|
|
@@ -1217,9 +1256,7 @@ class WebDriver extends Helper {
|
|
|
1217
1256
|
this.debugSection('File', 'Uploading file to remote server')
|
|
1218
1257
|
file = await this.browser.uploadFile(file)
|
|
1219
1258
|
} catch (err) {
|
|
1220
|
-
throw new Error(
|
|
1221
|
-
`File can't be transferred to remote server. Set \`remoteFileUpload: false\` in config to upload file locally.\n${err.message}`,
|
|
1222
|
-
)
|
|
1259
|
+
throw new Error(`File can't be transferred to remote server. Set \`remoteFileUpload: false\` in config to upload file locally.\n${err.message}`)
|
|
1223
1260
|
}
|
|
1224
1261
|
}
|
|
1225
1262
|
|
|
@@ -1241,7 +1278,8 @@ class WebDriver extends Helper {
|
|
|
1241
1278
|
const elementId = getElementId(elem)
|
|
1242
1279
|
highlightActiveElement.call(this, elem)
|
|
1243
1280
|
|
|
1244
|
-
const isSelected = await this.browser
|
|
1281
|
+
const isSelected = await isElementChecked(this.browser, elementId)
|
|
1282
|
+
|
|
1245
1283
|
if (isSelected) return Promise.resolve(true)
|
|
1246
1284
|
return this.browser[clickMethod](elementId)
|
|
1247
1285
|
}
|
|
@@ -1261,7 +1299,8 @@ class WebDriver extends Helper {
|
|
|
1261
1299
|
const elementId = getElementId(elem)
|
|
1262
1300
|
highlightActiveElement.call(this, elem)
|
|
1263
1301
|
|
|
1264
|
-
const isSelected = await this.browser
|
|
1302
|
+
const isSelected = await isElementChecked(this.browser, elementId)
|
|
1303
|
+
|
|
1265
1304
|
if (!isSelected) return Promise.resolve(true)
|
|
1266
1305
|
return this.browser[clickMethod](elementId)
|
|
1267
1306
|
}
|
|
@@ -1272,7 +1311,11 @@ class WebDriver extends Helper {
|
|
|
1272
1311
|
*/
|
|
1273
1312
|
async grabTextFromAll(locator) {
|
|
1274
1313
|
const res = await this._locate(locator, true)
|
|
1275
|
-
|
|
1314
|
+
let val = []
|
|
1315
|
+
await forEachAsync(res, async el => {
|
|
1316
|
+
const text = await this.browser.getElementText(getElementId(el))
|
|
1317
|
+
val.push(text)
|
|
1318
|
+
})
|
|
1276
1319
|
this.debugSection('GrabText', String(val))
|
|
1277
1320
|
return val
|
|
1278
1321
|
}
|
|
@@ -1297,7 +1340,7 @@ class WebDriver extends Helper {
|
|
|
1297
1340
|
*/
|
|
1298
1341
|
async grabHTMLFromAll(locator) {
|
|
1299
1342
|
const elems = await this._locate(locator, true)
|
|
1300
|
-
const html = await forEachAsync(elems,
|
|
1343
|
+
const html = await forEachAsync(elems, elem => elem.getHTML(false))
|
|
1301
1344
|
this.debugSection('GrabHTML', String(html))
|
|
1302
1345
|
return html
|
|
1303
1346
|
}
|
|
@@ -1322,7 +1365,7 @@ class WebDriver extends Helper {
|
|
|
1322
1365
|
*/
|
|
1323
1366
|
async grabValueFromAll(locator) {
|
|
1324
1367
|
const res = await this._locate(locator, true)
|
|
1325
|
-
const val = await forEachAsync(res,
|
|
1368
|
+
const val = await forEachAsync(res, el => el.getValue())
|
|
1326
1369
|
this.debugSection('GrabValue', String(val))
|
|
1327
1370
|
|
|
1328
1371
|
return val
|
|
@@ -1347,7 +1390,7 @@ class WebDriver extends Helper {
|
|
|
1347
1390
|
*/
|
|
1348
1391
|
async grabCssPropertyFromAll(locator, cssProperty) {
|
|
1349
1392
|
const res = await this._locate(locator, true)
|
|
1350
|
-
const val = await forEachAsync(res, async
|
|
1393
|
+
const val = await forEachAsync(res, async el => this.browser.getElementCSSValue(getElementId(el), cssProperty))
|
|
1351
1394
|
this.debugSection('Grab', String(val))
|
|
1352
1395
|
return val
|
|
1353
1396
|
}
|
|
@@ -1371,7 +1414,7 @@ class WebDriver extends Helper {
|
|
|
1371
1414
|
*/
|
|
1372
1415
|
async grabAttributeFromAll(locator, attr) {
|
|
1373
1416
|
const res = await this._locate(locator, true)
|
|
1374
|
-
const val = await forEachAsync(res, async
|
|
1417
|
+
const val = await forEachAsync(res, async el => el.getAttribute(attr))
|
|
1375
1418
|
this.debugSection('GrabAttribute', String(val))
|
|
1376
1419
|
return val
|
|
1377
1420
|
}
|
|
@@ -1488,7 +1531,7 @@ class WebDriver extends Helper {
|
|
|
1488
1531
|
async seeElement(locator) {
|
|
1489
1532
|
const res = await this._locate(locator, true)
|
|
1490
1533
|
assertElementExists(res, locator)
|
|
1491
|
-
const selected = await forEachAsync(res, async
|
|
1534
|
+
const selected = await forEachAsync(res, async el => el.isDisplayed())
|
|
1492
1535
|
try {
|
|
1493
1536
|
return truth(`elements of ${new Locator(locator)}`, 'to be seen').assert(selected)
|
|
1494
1537
|
} catch (e) {
|
|
@@ -1505,7 +1548,7 @@ class WebDriver extends Helper {
|
|
|
1505
1548
|
if (!res || res.length === 0) {
|
|
1506
1549
|
return truth(`elements of ${new Locator(locator)}`, 'to be seen').negate(false)
|
|
1507
1550
|
}
|
|
1508
|
-
const selected = await forEachAsync(res, async
|
|
1551
|
+
const selected = await forEachAsync(res, async el => el.isDisplayed())
|
|
1509
1552
|
try {
|
|
1510
1553
|
return truth(`elements of ${new Locator(locator)}`, 'to be seen').negate(selected)
|
|
1511
1554
|
} catch (e) {
|
|
@@ -1560,11 +1603,7 @@ class WebDriver extends Helper {
|
|
|
1560
1603
|
* {{> grabBrowserLogs }}
|
|
1561
1604
|
*/
|
|
1562
1605
|
async grabBrowserLogs() {
|
|
1563
|
-
|
|
1564
|
-
this.debug('Logs not available in W3C specification')
|
|
1565
|
-
return
|
|
1566
|
-
}
|
|
1567
|
-
return this.browser.getLogs('browser')
|
|
1606
|
+
return browserLogs
|
|
1568
1607
|
}
|
|
1569
1608
|
|
|
1570
1609
|
/**
|
|
@@ -1590,11 +1629,7 @@ class WebDriver extends Helper {
|
|
|
1590
1629
|
*/
|
|
1591
1630
|
async seeNumberOfElements(locator, num) {
|
|
1592
1631
|
const res = await this._locate(locator)
|
|
1593
|
-
return assert.equal(
|
|
1594
|
-
res.length,
|
|
1595
|
-
num,
|
|
1596
|
-
`expected number of elements (${new Locator(locator)}) is ${num}, but found ${res.length}`,
|
|
1597
|
-
)
|
|
1632
|
+
return assert.equal(res.length, num, `expected number of elements (${new Locator(locator)}) is ${num}, but found ${res.length}`)
|
|
1598
1633
|
}
|
|
1599
1634
|
|
|
1600
1635
|
/**
|
|
@@ -1603,11 +1638,7 @@ class WebDriver extends Helper {
|
|
|
1603
1638
|
*/
|
|
1604
1639
|
async seeNumberOfVisibleElements(locator, num) {
|
|
1605
1640
|
const res = await this.grabNumberOfVisibleElements(locator)
|
|
1606
|
-
return assert.equal(
|
|
1607
|
-
res,
|
|
1608
|
-
num,
|
|
1609
|
-
`expected number of visible elements (${new Locator(locator)}) is ${num}, but found ${res}`,
|
|
1610
|
-
)
|
|
1641
|
+
return assert.equal(res, num, `expected number of visible elements (${new Locator(locator)}) is ${num}, but found ${res}`)
|
|
1611
1642
|
}
|
|
1612
1643
|
|
|
1613
1644
|
/**
|
|
@@ -1632,19 +1663,16 @@ class WebDriver extends Helper {
|
|
|
1632
1663
|
}
|
|
1633
1664
|
}
|
|
1634
1665
|
|
|
1635
|
-
const values = Object.keys(cssPropertiesCamelCase).map(
|
|
1666
|
+
const values = Object.keys(cssPropertiesCamelCase).map(key => cssPropertiesCamelCase[key])
|
|
1636
1667
|
if (!Array.isArray(props)) props = [props]
|
|
1637
1668
|
let chunked = chunkArray(props, values.length)
|
|
1638
|
-
chunked = chunked.filter(
|
|
1669
|
+
chunked = chunked.filter(val => {
|
|
1639
1670
|
for (let i = 0; i < val.length; ++i) {
|
|
1640
|
-
// eslint-disable-next-line eqeqeq
|
|
1641
1671
|
if (val[i] != values[i]) return false
|
|
1642
1672
|
}
|
|
1643
1673
|
return true
|
|
1644
1674
|
})
|
|
1645
|
-
return equals(
|
|
1646
|
-
`all elements (${new Locator(locator)}) to have CSS property ${JSON.stringify(cssProperties)}`,
|
|
1647
|
-
).assert(chunked.length, elemAmount)
|
|
1675
|
+
return equals(`all elements (${new Locator(locator)}) to have CSS property ${JSON.stringify(cssProperties)}`).assert(chunked.length, elemAmount)
|
|
1648
1676
|
}
|
|
1649
1677
|
|
|
1650
1678
|
/**
|
|
@@ -1655,28 +1683,24 @@ class WebDriver extends Helper {
|
|
|
1655
1683
|
assertElementExists(res, locator)
|
|
1656
1684
|
const elemAmount = res.length
|
|
1657
1685
|
|
|
1658
|
-
let attrs = await forEachAsync(res, async
|
|
1659
|
-
return forEachAsync(Object.keys(attributes), async
|
|
1686
|
+
let attrs = await forEachAsync(res, async el => {
|
|
1687
|
+
return forEachAsync(Object.keys(attributes), async attr => el.getAttribute(attr))
|
|
1660
1688
|
})
|
|
1661
1689
|
|
|
1662
|
-
const values = Object.keys(attributes).map(
|
|
1690
|
+
const values = Object.keys(attributes).map(key => attributes[key])
|
|
1663
1691
|
if (!Array.isArray(attrs)) attrs = [attrs]
|
|
1664
1692
|
let chunked = chunkArray(attrs, values.length)
|
|
1665
|
-
chunked = chunked.filter(
|
|
1693
|
+
chunked = chunked.filter(val => {
|
|
1666
1694
|
for (let i = 0; i < val.length; ++i) {
|
|
1667
1695
|
const _actual = Number.isNaN(val[i]) || typeof values[i] === 'string' ? val[i] : Number.parseInt(val[i], 10)
|
|
1668
|
-
const _expected =
|
|
1669
|
-
Number.isNaN(values[i]) || typeof values[i] === 'string' ? values[i] : Number.parseInt(values[i], 10)
|
|
1696
|
+
const _expected = Number.isNaN(values[i]) || typeof values[i] === 'string' ? values[i] : Number.parseInt(values[i], 10)
|
|
1670
1697
|
// the attribute could be a boolean
|
|
1671
1698
|
if (typeof _actual === 'boolean') return _actual === _expected
|
|
1672
1699
|
if (_actual !== _expected) return false
|
|
1673
1700
|
}
|
|
1674
1701
|
return true
|
|
1675
1702
|
})
|
|
1676
|
-
return assert.ok(
|
|
1677
|
-
chunked.length === elemAmount,
|
|
1678
|
-
`expected all elements (${new Locator(locator)}) to have attributes ${JSON.stringify(attributes)}`,
|
|
1679
|
-
)
|
|
1703
|
+
return assert.ok(chunked.length === elemAmount, `expected all elements (${new Locator(locator)}) to have attributes ${JSON.stringify(attributes)}`)
|
|
1680
1704
|
}
|
|
1681
1705
|
|
|
1682
1706
|
/**
|
|
@@ -1685,9 +1709,9 @@ class WebDriver extends Helper {
|
|
|
1685
1709
|
async grabNumberOfVisibleElements(locator) {
|
|
1686
1710
|
const res = await this._locate(locator)
|
|
1687
1711
|
|
|
1688
|
-
let selected = await forEachAsync(res, async
|
|
1712
|
+
let selected = await forEachAsync(res, async el => el.isDisplayed())
|
|
1689
1713
|
if (!Array.isArray(selected)) selected = [selected]
|
|
1690
|
-
selected = selected.filter(
|
|
1714
|
+
selected = selected.filter(val => val === true)
|
|
1691
1715
|
return selected.length
|
|
1692
1716
|
}
|
|
1693
1717
|
|
|
@@ -1805,7 +1829,7 @@ class WebDriver extends Helper {
|
|
|
1805
1829
|
try {
|
|
1806
1830
|
await elem.moveTo({ xOffset, yOffset })
|
|
1807
1831
|
} catch (e) {
|
|
1808
|
-
debug(e.message)
|
|
1832
|
+
output.debug(e.message)
|
|
1809
1833
|
}
|
|
1810
1834
|
}
|
|
1811
1835
|
|
|
@@ -1841,18 +1865,25 @@ class WebDriver extends Helper {
|
|
|
1841
1865
|
|
|
1842
1866
|
if (browser) {
|
|
1843
1867
|
this.debug(`Screenshot of ${sessionName} session has been saved to ${outputFile}`)
|
|
1844
|
-
|
|
1868
|
+
await browser.saveScreenshot(outputFile)
|
|
1845
1869
|
}
|
|
1846
1870
|
}
|
|
1847
1871
|
}
|
|
1848
1872
|
|
|
1849
1873
|
if (!fullPage) {
|
|
1850
1874
|
this.debug(`Screenshot has been saved to ${outputFile}`)
|
|
1851
|
-
|
|
1875
|
+
await this.browser.saveScreenshot(outputFile)
|
|
1852
1876
|
}
|
|
1853
1877
|
|
|
1854
1878
|
const originalWindowSize = await this.browser.getWindowSize()
|
|
1855
1879
|
|
|
1880
|
+
// this case running on device, so we could not set the windowSize
|
|
1881
|
+
if (this.browser.isMobile) {
|
|
1882
|
+
this.debug(`Screenshot has been saved to ${outputFile}, size: ${originalWindowSize.width}x${originalWindowSize.height}`)
|
|
1883
|
+
const buffer = await this.browser.saveScreenshot(outputFile)
|
|
1884
|
+
return buffer
|
|
1885
|
+
}
|
|
1886
|
+
|
|
1856
1887
|
let { width, height } = await this.browser
|
|
1857
1888
|
.execute(function () {
|
|
1858
1889
|
return {
|
|
@@ -1860,7 +1891,7 @@ class WebDriver extends Helper {
|
|
|
1860
1891
|
width: document.body.scrollWidth,
|
|
1861
1892
|
}
|
|
1862
1893
|
})
|
|
1863
|
-
.then(
|
|
1894
|
+
.then(res => res)
|
|
1864
1895
|
|
|
1865
1896
|
if (height < 100) height = 500 // errors for very small height
|
|
1866
1897
|
|
|
@@ -1928,7 +1959,7 @@ class WebDriver extends Helper {
|
|
|
1928
1959
|
|
|
1929
1960
|
return promiseRetry(
|
|
1930
1961
|
async (retry, number) => {
|
|
1931
|
-
const _grabCookie = async
|
|
1962
|
+
const _grabCookie = async name => {
|
|
1932
1963
|
const cookie = await this.browser.getCookies([name])
|
|
1933
1964
|
if (cookie.length === 0) throw Error(`Cookie ${name} is not found after ${retries}s`)
|
|
1934
1965
|
}
|
|
@@ -1952,11 +1983,10 @@ class WebDriver extends Helper {
|
|
|
1952
1983
|
* libraries](http://jster.net/category/windows-modals-popups).
|
|
1953
1984
|
*/
|
|
1954
1985
|
async acceptPopup() {
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
})
|
|
1986
|
+
const text = await this.browser.getAlertText()
|
|
1987
|
+
if (text) {
|
|
1988
|
+
return await this.browser.acceptAlert()
|
|
1989
|
+
}
|
|
1960
1990
|
}
|
|
1961
1991
|
|
|
1962
1992
|
/**
|
|
@@ -1964,11 +1994,10 @@ class WebDriver extends Helper {
|
|
|
1964
1994
|
*
|
|
1965
1995
|
*/
|
|
1966
1996
|
async cancelPopup() {
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
})
|
|
1997
|
+
const text = await this.browser.getAlertText()
|
|
1998
|
+
if (text) {
|
|
1999
|
+
return await this.browser.dismissAlert()
|
|
2000
|
+
}
|
|
1972
2001
|
}
|
|
1973
2002
|
|
|
1974
2003
|
/**
|
|
@@ -1978,7 +2007,7 @@ class WebDriver extends Helper {
|
|
|
1978
2007
|
* @param {string} text value to check.
|
|
1979
2008
|
*/
|
|
1980
2009
|
async seeInPopup(text) {
|
|
1981
|
-
return this.browser.getAlertText().then(
|
|
2010
|
+
return await this.browser.getAlertText().then(res => {
|
|
1982
2011
|
if (res === null) {
|
|
1983
2012
|
throw new Error('Popup is not opened')
|
|
1984
2013
|
}
|
|
@@ -2259,9 +2288,9 @@ class WebDriver extends Helper {
|
|
|
2259
2288
|
async closeOtherTabs() {
|
|
2260
2289
|
const handles = await this.browser.getWindowHandles()
|
|
2261
2290
|
const currentHandle = await this.browser.getWindowHandle()
|
|
2262
|
-
const otherHandles = handles.filter(
|
|
2291
|
+
const otherHandles = handles.filter(handle => handle !== currentHandle)
|
|
2263
2292
|
|
|
2264
|
-
await forEachAsync(otherHandles, async
|
|
2293
|
+
await forEachAsync(otherHandles, async handle => {
|
|
2265
2294
|
await this.browser.switchToWindow(handle)
|
|
2266
2295
|
await this.browser.closeWindow()
|
|
2267
2296
|
})
|
|
@@ -2272,7 +2301,7 @@ class WebDriver extends Helper {
|
|
|
2272
2301
|
* {{> wait }}
|
|
2273
2302
|
*/
|
|
2274
2303
|
async wait(sec) {
|
|
2275
|
-
return new Promise(
|
|
2304
|
+
return new Promise(resolve => {
|
|
2276
2305
|
setTimeout(resolve, sec * 1000)
|
|
2277
2306
|
})
|
|
2278
2307
|
}
|
|
@@ -2289,9 +2318,9 @@ class WebDriver extends Helper {
|
|
|
2289
2318
|
if (!res || res.length === 0) {
|
|
2290
2319
|
return false
|
|
2291
2320
|
}
|
|
2292
|
-
const selected = await forEachAsync(res, async
|
|
2321
|
+
const selected = await forEachAsync(res, async el => this.browser.isElementEnabled(getElementId(el)))
|
|
2293
2322
|
if (Array.isArray(selected)) {
|
|
2294
|
-
return selected.filter(
|
|
2323
|
+
return selected.filter(val => val === true).length > 0
|
|
2295
2324
|
}
|
|
2296
2325
|
return selected
|
|
2297
2326
|
},
|
|
@@ -2329,10 +2358,14 @@ class WebDriver extends Helper {
|
|
|
2329
2358
|
res = usingFirstElement(res)
|
|
2330
2359
|
assertElementExists(res, locator)
|
|
2331
2360
|
|
|
2332
|
-
return res
|
|
2333
|
-
|
|
2334
|
-
|
|
2335
|
-
|
|
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
|
+
})
|
|
2336
2369
|
}
|
|
2337
2370
|
|
|
2338
2371
|
/**
|
|
@@ -2346,14 +2379,15 @@ class WebDriver extends Helper {
|
|
|
2346
2379
|
return client
|
|
2347
2380
|
.waitUntil(
|
|
2348
2381
|
function () {
|
|
2349
|
-
return this.getUrl().then(
|
|
2382
|
+
return this.getUrl().then(res => {
|
|
2350
2383
|
currUrl = decodeUrl(res)
|
|
2351
2384
|
return currUrl.indexOf(urlPart) > -1
|
|
2352
2385
|
})
|
|
2353
2386
|
},
|
|
2354
2387
|
{ timeout: aSec * 1000 },
|
|
2355
2388
|
)
|
|
2356
|
-
.catch(
|
|
2389
|
+
.catch(e => {
|
|
2390
|
+
e = wrapError(e)
|
|
2357
2391
|
if (e.message.indexOf('timeout')) {
|
|
2358
2392
|
throw new Error(`expected url to include ${urlPart}, but found ${currUrl}`)
|
|
2359
2393
|
}
|
|
@@ -2373,12 +2407,13 @@ class WebDriver extends Helper {
|
|
|
2373
2407
|
let currUrl = ''
|
|
2374
2408
|
return this.browser
|
|
2375
2409
|
.waitUntil(function () {
|
|
2376
|
-
return this.getUrl().then(
|
|
2410
|
+
return this.getUrl().then(res => {
|
|
2377
2411
|
currUrl = decodeUrl(res)
|
|
2378
2412
|
return currUrl === urlPart
|
|
2379
2413
|
})
|
|
2380
2414
|
}, aSec * 1000)
|
|
2381
|
-
.catch(
|
|
2415
|
+
.catch(e => {
|
|
2416
|
+
e = wrapError(e)
|
|
2382
2417
|
if (e.message.indexOf('timeout')) {
|
|
2383
2418
|
throw new Error(`expected url to be ${urlPart}, but found ${currUrl}`)
|
|
2384
2419
|
}
|
|
@@ -2398,9 +2433,9 @@ class WebDriver extends Helper {
|
|
|
2398
2433
|
async () => {
|
|
2399
2434
|
const res = await this.$$(withStrictLocator.call(this, _context))
|
|
2400
2435
|
if (!res || res.length === 0) return false
|
|
2401
|
-
const selected = await forEachAsync(res, async
|
|
2436
|
+
const selected = await forEachAsync(res, async el => this.browser.getElementText(getElementId(el)))
|
|
2402
2437
|
if (Array.isArray(selected)) {
|
|
2403
|
-
return selected.filter(
|
|
2438
|
+
return selected.filter(part => part.indexOf(text) >= 0).length > 0
|
|
2404
2439
|
}
|
|
2405
2440
|
return selected.indexOf(text) >= 0
|
|
2406
2441
|
},
|
|
@@ -2422,9 +2457,9 @@ class WebDriver extends Helper {
|
|
|
2422
2457
|
async () => {
|
|
2423
2458
|
const res = await findFields.call(this, field)
|
|
2424
2459
|
if (!res || res.length === 0) return false
|
|
2425
|
-
const selected = await forEachAsync(res, async
|
|
2460
|
+
const selected = await forEachAsync(res, async el => el.getValue())
|
|
2426
2461
|
if (Array.isArray(selected)) {
|
|
2427
|
-
return selected.filter(
|
|
2462
|
+
return selected.filter(part => part.indexOf(value) >= 0).length > 0
|
|
2428
2463
|
}
|
|
2429
2464
|
return selected.indexOf(value) >= 0
|
|
2430
2465
|
},
|
|
@@ -2446,9 +2481,9 @@ class WebDriver extends Helper {
|
|
|
2446
2481
|
async () => {
|
|
2447
2482
|
const res = await this._res(locator)
|
|
2448
2483
|
if (!res || res.length === 0) return false
|
|
2449
|
-
const selected = await forEachAsync(res, async
|
|
2484
|
+
const selected = await forEachAsync(res, async el => el.isDisplayed())
|
|
2450
2485
|
if (Array.isArray(selected)) {
|
|
2451
|
-
return selected.filter(
|
|
2486
|
+
return selected.filter(val => val === true).length > 0
|
|
2452
2487
|
}
|
|
2453
2488
|
return selected
|
|
2454
2489
|
},
|
|
@@ -2465,21 +2500,25 @@ class WebDriver extends Helper {
|
|
|
2465
2500
|
async waitNumberOfVisibleElements(locator, num, sec = null) {
|
|
2466
2501
|
const aSec = sec || this.options.waitForTimeoutInSeconds
|
|
2467
2502
|
|
|
2468
|
-
return this.browser
|
|
2469
|
-
|
|
2470
|
-
|
|
2471
|
-
|
|
2472
|
-
|
|
2473
|
-
|
|
2474
|
-
|
|
2475
|
-
|
|
2476
|
-
|
|
2477
|
-
|
|
2478
|
-
|
|
2479
|
-
|
|
2480
|
-
|
|
2481
|
-
|
|
2482
|
-
|
|
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
|
+
})
|
|
2483
2522
|
}
|
|
2484
2523
|
|
|
2485
2524
|
/**
|
|
@@ -2492,7 +2531,7 @@ class WebDriver extends Helper {
|
|
|
2492
2531
|
async () => {
|
|
2493
2532
|
const res = await this._res(locator)
|
|
2494
2533
|
if (!res || res.length === 0) return true
|
|
2495
|
-
const selected = await forEachAsync(res, async
|
|
2534
|
+
const selected = await forEachAsync(res, async el => el.isDisplayed())
|
|
2496
2535
|
return !selected.length
|
|
2497
2536
|
},
|
|
2498
2537
|
{ timeout: aSec * 1000, timeoutMsg: `element (${new Locator(locator)}) still visible after ${aSec} sec` },
|
|
@@ -2568,17 +2607,14 @@ class WebDriver extends Helper {
|
|
|
2568
2607
|
*/
|
|
2569
2608
|
async switchTo(locator) {
|
|
2570
2609
|
this.browser.isInsideFrame = true
|
|
2571
|
-
if (Number.isInteger(locator)) {
|
|
2572
|
-
return this.browser.switchToFrame(locator)
|
|
2573
|
-
}
|
|
2574
2610
|
if (!locator) {
|
|
2575
|
-
return this.browser.
|
|
2611
|
+
return this.browser.switchFrame(null)
|
|
2576
2612
|
}
|
|
2577
2613
|
|
|
2578
2614
|
let res = await this._locate(locator, true)
|
|
2579
2615
|
assertElementExists(res, locator)
|
|
2580
2616
|
res = usingFirstElement(res)
|
|
2581
|
-
return this.browser.
|
|
2617
|
+
return this.browser.switchFrame(res)
|
|
2582
2618
|
}
|
|
2583
2619
|
|
|
2584
2620
|
/**
|
|
@@ -2591,7 +2627,7 @@ class WebDriver extends Helper {
|
|
|
2591
2627
|
|
|
2592
2628
|
await this.browser.waitUntil(
|
|
2593
2629
|
async () => {
|
|
2594
|
-
await this.browser.getWindowHandles().then(
|
|
2630
|
+
await this.browser.getWindowHandles().then(handles => {
|
|
2595
2631
|
if (handles.indexOf(current) + num + 1 <= handles.length) {
|
|
2596
2632
|
target = handles[handles.indexOf(current) + num]
|
|
2597
2633
|
}
|
|
@@ -2613,7 +2649,7 @@ class WebDriver extends Helper {
|
|
|
2613
2649
|
|
|
2614
2650
|
await this.browser.waitUntil(
|
|
2615
2651
|
async () => {
|
|
2616
|
-
await this.browser.getWindowHandles().then(
|
|
2652
|
+
await this.browser.getWindowHandles().then(handles => {
|
|
2617
2653
|
if (handles.indexOf(current) - num > -1) {
|
|
2618
2654
|
target = handles[handles.indexOf(current) - num]
|
|
2619
2655
|
}
|
|
@@ -2639,7 +2675,6 @@ class WebDriver extends Helper {
|
|
|
2639
2675
|
*/
|
|
2640
2676
|
async openNewTab(url = 'about:blank', windowName = null) {
|
|
2641
2677
|
const client = this.browser
|
|
2642
|
-
const crypto = require('crypto')
|
|
2643
2678
|
if (windowName == null) {
|
|
2644
2679
|
windowName = crypto.randomBytes(32).toString('hex')
|
|
2645
2680
|
}
|
|
@@ -2683,10 +2718,7 @@ class WebDriver extends Helper {
|
|
|
2683
2718
|
return client.execute(function () {
|
|
2684
2719
|
const body = document.body
|
|
2685
2720
|
const html = document.documentElement
|
|
2686
|
-
window.scrollTo(
|
|
2687
|
-
0,
|
|
2688
|
-
Math.max(body.scrollHeight, body.offsetHeight, html.clientHeight, html.scrollHeight, html.offsetHeight),
|
|
2689
|
-
)
|
|
2721
|
+
window.scrollTo(0, Math.max(body.scrollHeight, body.offsetHeight, html.clientHeight, html.scrollHeight, html.offsetHeight))
|
|
2690
2722
|
})
|
|
2691
2723
|
}
|
|
2692
2724
|
|
|
@@ -2760,10 +2792,17 @@ async function proceedSee(assertType, text, context, strict = false) {
|
|
|
2760
2792
|
const smartWaitEnabled = assertType === 'assert'
|
|
2761
2793
|
const res = await this._locate(withStrictLocator(context), smartWaitEnabled)
|
|
2762
2794
|
assertElementExists(res, context)
|
|
2763
|
-
|
|
2795
|
+
let selected = await forEachAsync(res, async el => this.browser.getElementText(getElementId(el)))
|
|
2796
|
+
|
|
2797
|
+
// apply ignoreCase option
|
|
2798
|
+
if (store?.currentStep?.opts?.ignoreCase === true) {
|
|
2799
|
+
text = text.toLowerCase()
|
|
2800
|
+
selected = selected.map(elText => elText.toLowerCase())
|
|
2801
|
+
}
|
|
2802
|
+
|
|
2764
2803
|
if (strict) {
|
|
2765
2804
|
if (Array.isArray(selected) && selected.length !== 0) {
|
|
2766
|
-
return selected.map(
|
|
2805
|
+
return selected.map(elText => equals(description)[assertType](text, elText))
|
|
2767
2806
|
}
|
|
2768
2807
|
return equals(description)[assertType](text, selected)
|
|
2769
2808
|
}
|
|
@@ -2791,7 +2830,7 @@ async function forEachAsync(array, callback, options = { expandArrayResults: tru
|
|
|
2791
2830
|
const res = await callback(inputArray[index], index, inputArray)
|
|
2792
2831
|
|
|
2793
2832
|
if (Array.isArray(res) && expandArrayResults) {
|
|
2794
|
-
res.forEach(
|
|
2833
|
+
res.forEach(val => values.push(val))
|
|
2795
2834
|
} else if (res) {
|
|
2796
2835
|
values.push(res)
|
|
2797
2836
|
}
|
|
@@ -2832,6 +2871,7 @@ async function findClickable(locator, locateFn) {
|
|
|
2832
2871
|
}
|
|
2833
2872
|
|
|
2834
2873
|
if (locator.isAccessibilityId() && !this.isWeb) return locateFn(locator, true)
|
|
2874
|
+
if (locator.isRole()) return locateFn(locator, true)
|
|
2835
2875
|
if (!locator.isFuzzy()) return locateFn(locator, true)
|
|
2836
2876
|
|
|
2837
2877
|
let els
|
|
@@ -2846,7 +2886,15 @@ async function findClickable(locator, locateFn) {
|
|
|
2846
2886
|
els = await locateFn(Locator.clickable.self(literal))
|
|
2847
2887
|
if (els.length) return els
|
|
2848
2888
|
|
|
2849
|
-
|
|
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
|
|
2850
2898
|
}
|
|
2851
2899
|
|
|
2852
2900
|
async function findFields(locator) {
|
|
@@ -2857,6 +2905,7 @@ async function findFields(locator) {
|
|
|
2857
2905
|
}
|
|
2858
2906
|
|
|
2859
2907
|
if (locator.isAccessibilityId() && !this.isWeb) return this._locate(locator, true)
|
|
2908
|
+
if (locator.isRole()) return this._locate(locator, true)
|
|
2860
2909
|
if (!locator.isFuzzy()) return this._locate(locator, true)
|
|
2861
2910
|
|
|
2862
2911
|
const literal = xpathLocator.literal(locator.value)
|
|
@@ -2868,7 +2917,16 @@ async function findFields(locator) {
|
|
|
2868
2917
|
|
|
2869
2918
|
els = await this._locate(Locator.field.byName(literal))
|
|
2870
2919
|
if (els.length) return els
|
|
2871
|
-
|
|
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
|
|
2872
2930
|
}
|
|
2873
2931
|
|
|
2874
2932
|
async function proceedSeeField(assertType, field, value) {
|
|
@@ -2877,11 +2935,11 @@ async function proceedSeeField(assertType, field, value) {
|
|
|
2877
2935
|
const elem = usingFirstElement(res)
|
|
2878
2936
|
const elemId = getElementId(elem)
|
|
2879
2937
|
|
|
2880
|
-
const proceedMultiple = async
|
|
2938
|
+
const proceedMultiple = async fields => {
|
|
2881
2939
|
const fieldResults = toArray(
|
|
2882
|
-
await forEachAsync(fields, async
|
|
2940
|
+
await forEachAsync(fields, async el => {
|
|
2883
2941
|
const elementId = getElementId(el)
|
|
2884
|
-
return this.browser.
|
|
2942
|
+
return this.browser.getElementAttribute(elementId, 'value')
|
|
2885
2943
|
}),
|
|
2886
2944
|
)
|
|
2887
2945
|
|
|
@@ -2890,27 +2948,30 @@ async function proceedSeeField(assertType, field, value) {
|
|
|
2890
2948
|
} else {
|
|
2891
2949
|
// Assert that results were found so the forEach assert does not silently pass
|
|
2892
2950
|
equals(`no. of items matching > 0: ${field}`)[assertType](true, !!fieldResults.length)
|
|
2893
|
-
fieldResults.forEach(
|
|
2951
|
+
fieldResults.forEach(val => stringIncludes(`fields by ${field}`)[assertType](value, val))
|
|
2894
2952
|
}
|
|
2895
2953
|
}
|
|
2896
2954
|
|
|
2897
|
-
const proceedSingle =
|
|
2898
|
-
el.getValue()
|
|
2899
|
-
if (res === null) {
|
|
2900
|
-
throw new Error(`Element ${el.selector} has no value attribute`)
|
|
2901
|
-
}
|
|
2902
|
-
stringIncludes(`fields by ${field}`)[assertType](value, res)
|
|
2903
|
-
})
|
|
2955
|
+
const proceedSingle = async el => {
|
|
2956
|
+
let res = await el.getValue()
|
|
2904
2957
|
|
|
2905
|
-
|
|
2906
|
-
|
|
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
|
+
}
|
|
2968
|
+
|
|
2969
|
+
const filterBySelected = async elements => filterAsync(elements, async el => this.browser.isElementSelected(getElementId(el)))
|
|
2907
2970
|
|
|
2908
2971
|
const filterSelectedByValue = async (elements, value) => {
|
|
2909
|
-
return filterAsync(elements, async
|
|
2972
|
+
return filterAsync(elements, async el => {
|
|
2910
2973
|
const elementId = getElementId(el)
|
|
2911
|
-
const currentValue = this.browser.
|
|
2912
|
-
? await el.getValue()
|
|
2913
|
-
: await this.browser.getElementAttribute(elementId, 'value')
|
|
2974
|
+
const currentValue = await this.browser.getElementAttribute(elementId, 'value')
|
|
2914
2975
|
const isSelected = await this.browser.isElementSelected(elementId)
|
|
2915
2976
|
return currentValue === value && isSelected
|
|
2916
2977
|
})
|
|
@@ -2918,7 +2979,13 @@ async function proceedSeeField(assertType, field, value) {
|
|
|
2918
2979
|
|
|
2919
2980
|
const tag = await elem.getTagName()
|
|
2920
2981
|
if (tag === 'select') {
|
|
2921
|
-
|
|
2982
|
+
let subOptions
|
|
2983
|
+
|
|
2984
|
+
try {
|
|
2985
|
+
subOptions = await this.browser.findElementsFromElement(elemId, 'css', 'option')
|
|
2986
|
+
} catch (e) {
|
|
2987
|
+
subOptions = await this.browser.findElementsFromElement(elemId, 'xpath', 'option')
|
|
2988
|
+
}
|
|
2922
2989
|
|
|
2923
2990
|
if (value === '') {
|
|
2924
2991
|
// Don't filter by value
|
|
@@ -2959,10 +3026,31 @@ async function proceedSeeCheckbox(assertType, field) {
|
|
|
2959
3026
|
const res = await findFields.call(this, field)
|
|
2960
3027
|
assertElementExists(res, field, 'Field')
|
|
2961
3028
|
|
|
2962
|
-
const selected = await forEachAsync(res, async
|
|
3029
|
+
const selected = await forEachAsync(res, async el => {
|
|
3030
|
+
const elementId = getElementId(el)
|
|
3031
|
+
return isElementChecked(this.browser, elementId)
|
|
3032
|
+
})
|
|
3033
|
+
|
|
2963
3034
|
return truth(`checkable field "${field}"`, 'to be checked')[assertType](selected)
|
|
2964
3035
|
}
|
|
2965
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
|
+
|
|
2966
3054
|
async function findCheckable(locator, locateFn) {
|
|
2967
3055
|
let els
|
|
2968
3056
|
locator = new Locator(locator)
|
|
@@ -2972,6 +3060,7 @@ async function findCheckable(locator, locateFn) {
|
|
|
2972
3060
|
}
|
|
2973
3061
|
|
|
2974
3062
|
if (locator.isAccessibilityId() && !this.isWeb) return locateFn(locator, true)
|
|
3063
|
+
if (locator.isRole()) return locateFn(locator, true)
|
|
2975
3064
|
if (!locator.isFuzzy()) return locateFn(locator, true)
|
|
2976
3065
|
|
|
2977
3066
|
const literal = xpathLocator.literal(locator.value)
|
|
@@ -2980,7 +3069,15 @@ async function findCheckable(locator, locateFn) {
|
|
|
2980
3069
|
els = await locateFn(Locator.checkable.byName(literal))
|
|
2981
3070
|
if (els.length) return els
|
|
2982
3071
|
|
|
2983
|
-
|
|
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
|
|
2984
3081
|
}
|
|
2985
3082
|
|
|
2986
3083
|
function withStrictLocator(locator) {
|
|
@@ -3160,7 +3257,7 @@ function getNormalizedKey(key) {
|
|
|
3160
3257
|
return convertKeyToRawKey(normalizedKey)
|
|
3161
3258
|
}
|
|
3162
3259
|
|
|
3163
|
-
const unicodeModifierKeys = modifierKeys.map(
|
|
3260
|
+
const unicodeModifierKeys = modifierKeys.map(k => convertKeyToRawKey(k))
|
|
3164
3261
|
function isModifierKey(key) {
|
|
3165
3262
|
return unicodeModifierKeys.includes(key)
|
|
3166
3263
|
}
|
|
@@ -3173,9 +3270,9 @@ function highlightActiveElement(element) {
|
|
|
3173
3270
|
|
|
3174
3271
|
function prepareLocateFn(context) {
|
|
3175
3272
|
if (!context) return this._locate.bind(this)
|
|
3176
|
-
return
|
|
3273
|
+
return l => {
|
|
3177
3274
|
l = new Locator(l, 'css')
|
|
3178
|
-
return this._locate(context, true).then(async
|
|
3275
|
+
return this._locate(context, true).then(async res => {
|
|
3179
3276
|
assertElementExists(res, context, 'Context element')
|
|
3180
3277
|
if (l.react) {
|
|
3181
3278
|
return res[0].react$$(l.react, l.props || undefined)
|
|
@@ -3185,4 +3282,8 @@ function prepareLocateFn(context) {
|
|
|
3185
3282
|
}
|
|
3186
3283
|
}
|
|
3187
3284
|
|
|
3188
|
-
|
|
3285
|
+
function logEvents(event) {
|
|
3286
|
+
browserLogs.push(event.text) // add log message to the array
|
|
3287
|
+
}
|
|
3288
|
+
|
|
3289
|
+
export { WebDriver as default }
|