codeceptjs 3.4.1 → 3.5.1-2.beta.7
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 +31 -30
- package/bin/codecept.js +1 -1
- package/lib/actor.js +6 -3
- package/lib/ai.js +180 -0
- package/lib/cli.js +13 -3
- package/lib/codecept.js +8 -0
- package/lib/colorUtils.js +10 -0
- package/lib/command/definitions.js +2 -7
- package/lib/command/dryRun.js +11 -2
- package/lib/command/generate.js +46 -3
- package/lib/command/info.js +24 -0
- package/lib/command/init.js +64 -6
- package/lib/command/interactive.js +15 -1
- package/lib/command/run-multiple/collection.js +17 -5
- package/lib/command/run-multiple.js +4 -2
- package/lib/command/run-workers.js +68 -5
- package/lib/command/run.js +7 -0
- package/lib/command/workers/runTests.js +39 -0
- package/lib/container.js +13 -3
- package/lib/data/context.js +14 -6
- package/lib/event.js +4 -0
- package/lib/helper/ApiDataFactory.js +2 -1
- package/lib/helper/Appium.js +116 -29
- package/lib/helper/Expect.js +422 -0
- package/lib/helper/FileSystem.js +1 -1
- package/lib/helper/GraphQL.js +25 -0
- package/lib/helper/JSONResponse.js +4 -4
- package/lib/helper/Nightmare.js +10 -5
- package/lib/helper/OpenAI.js +126 -0
- package/lib/helper/Playwright.js +1298 -229
- package/lib/helper/Protractor.js +12 -7
- package/lib/helper/Puppeteer.js +204 -64
- package/lib/helper/REST.js +15 -5
- package/lib/helper/TestCafe.js +45 -10
- package/lib/helper/WebDriver.js +252 -83
- package/lib/helper/errors/ElementNotFound.js +2 -1
- package/lib/helper/extras/PlaywrightReactVueLocator.js +38 -0
- package/lib/helper/scripts/blurElement.js +17 -0
- package/lib/helper/scripts/focusElement.js +17 -0
- package/lib/helper/scripts/highlightElement.js +20 -0
- package/lib/html.js +258 -0
- package/lib/interfaces/bdd.js +1 -1
- package/lib/interfaces/gherkin.js +37 -3
- package/lib/interfaces/scenarioConfig.js +1 -0
- package/lib/listener/retry.js +2 -1
- package/lib/locator.js +17 -4
- package/lib/mochaFactory.js +2 -1
- package/lib/output.js +1 -1
- package/lib/pause.js +78 -19
- package/lib/plugin/autoLogin.js +45 -10
- package/lib/plugin/debugErrors.js +67 -0
- package/lib/plugin/fakerTransform.js +4 -6
- package/lib/plugin/heal.js +209 -0
- package/lib/plugin/retryFailedStep.js +10 -1
- package/lib/plugin/retryTo.js +2 -4
- package/lib/plugin/screenshotOnFail.js +11 -2
- package/lib/plugin/selenoid.js +6 -1
- package/lib/plugin/standardActingHelpers.js +0 -2
- package/lib/plugin/stepByStepReport.js +2 -2
- package/lib/plugin/tryTo.js +5 -7
- package/lib/plugin/wdio.js +0 -1
- package/lib/recorder.js +22 -11
- package/lib/secret.js +5 -4
- package/lib/session.js +1 -1
- package/lib/step.js +36 -12
- package/lib/ui.js +5 -3
- package/lib/utils.js +22 -1
- package/lib/workers.js +83 -10
- package/package.json +117 -95
- package/translations/de-DE.js +5 -0
- package/translations/fr-FR.js +14 -1
- package/translations/it-IT.js +1 -0
- package/translations/ja-JP.js +14 -9
- package/translations/pl-PL.js +5 -0
- package/translations/pt-BR.js +1 -0
- package/translations/ru-RU.js +1 -0
- package/translations/zh-CN.js +5 -0
- package/translations/zh-TW.js +5 -0
- package/typings/index.d.ts +51 -15
- package/typings/promiseBasedTypes.d.ts +864 -802
- package/typings/types.d.ts +1339 -744
- package/CHANGELOG.md +0 -2427
- package/docs/advanced.md +0 -351
- package/docs/api.md +0 -323
- package/docs/basics.md +0 -980
- package/docs/bdd.md +0 -535
- package/docs/best.md +0 -237
- package/docs/books.md +0 -37
- package/docs/bootstrap.md +0 -135
- package/docs/build/ApiDataFactory.js +0 -409
- package/docs/build/Appium.js +0 -1938
- package/docs/build/FileSystem.js +0 -228
- package/docs/build/GraphQL.js +0 -204
- package/docs/build/GraphQLDataFactory.js +0 -309
- package/docs/build/JSONResponse.js +0 -338
- package/docs/build/Mochawesome.js +0 -71
- package/docs/build/Nightmare.js +0 -2145
- package/docs/build/Playwright.js +0 -3986
- package/docs/build/Polly.js +0 -42
- package/docs/build/Protractor.js +0 -2699
- package/docs/build/Puppeteer.js +0 -3710
- package/docs/build/REST.js +0 -334
- package/docs/build/SeleniumWebdriver.js +0 -76
- package/docs/build/TestCafe.js +0 -2057
- package/docs/build/WebDriver.js +0 -4017
- package/docs/changelog.md +0 -2436
- package/docs/commands.md +0 -254
- package/docs/community-helpers.md +0 -58
- package/docs/configuration.md +0 -157
- package/docs/continuous-integration.md +0 -22
- package/docs/custom-helpers.md +0 -306
- package/docs/data.md +0 -375
- package/docs/detox.md +0 -235
- package/docs/docker.md +0 -137
- package/docs/email.md +0 -183
- package/docs/examples.md +0 -149
- package/docs/helpers/ApiDataFactory.md +0 -266
- package/docs/helpers/Appium.md +0 -1312
- package/docs/helpers/Detox.md +0 -586
- package/docs/helpers/FileSystem.md +0 -152
- package/docs/helpers/GraphQL.md +0 -130
- package/docs/helpers/GraphQLDataFactory.md +0 -226
- package/docs/helpers/JSONResponse.md +0 -254
- package/docs/helpers/Mochawesome.md +0 -8
- package/docs/helpers/MockRequest.md +0 -377
- package/docs/helpers/Nightmare.md +0 -1256
- package/docs/helpers/Playwright.md +0 -2208
- package/docs/helpers/Polly.md +0 -44
- package/docs/helpers/Puppeteer-firefox.md +0 -86
- package/docs/helpers/Puppeteer.md +0 -2141
- package/docs/helpers/REST.md +0 -217
- package/docs/helpers/TestCafe.md +0 -1222
- package/docs/helpers/WebDriver.md +0 -2319
- package/docs/hooks.md +0 -340
- package/docs/index.md +0 -111
- package/docs/installation.md +0 -75
- package/docs/internal-api.md +0 -265
- package/docs/locators.md +0 -331
- package/docs/mobile-react-native-locators.md +0 -67
- package/docs/mobile.md +0 -297
- package/docs/nightmare.md +0 -223
- package/docs/pageobjects.md +0 -291
- package/docs/parallel.md +0 -232
- package/docs/playwright.md +0 -609
- package/docs/plugins.md +0 -1171
- package/docs/puppeteer.md +0 -316
- package/docs/quickstart.md +0 -163
- package/docs/react.md +0 -69
- package/docs/reports.md +0 -392
- package/docs/secrets.md +0 -30
- package/docs/shadow.md +0 -68
- package/docs/shared/keys.mustache +0 -31
- package/docs/shared/react.mustache +0 -1
- package/docs/testcafe.md +0 -174
- package/docs/translation.md +0 -247
- package/docs/tutorial.md +0 -271
- package/docs/typescript.md +0 -180
- package/docs/ui.md +0 -59
- package/docs/videos.md +0 -28
- package/docs/visual.md +0 -202
- package/docs/vue.md +0 -121
- package/docs/webapi/amOnPage.mustache +0 -11
- package/docs/webapi/appendField.mustache +0 -9
- package/docs/webapi/attachFile.mustache +0 -12
- package/docs/webapi/checkOption.mustache +0 -13
- package/docs/webapi/clearCookie.mustache +0 -10
- package/docs/webapi/clearField.mustache +0 -9
- package/docs/webapi/click.mustache +0 -25
- 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/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/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/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/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/selectOption.mustache +0 -21
- package/docs/webapi/setCookie.mustache +0 -16
- package/docs/webapi/setGeoLocation.mustache +0 -12
- 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 -18
- package/docs/webapi/uncheckOption.mustache +0 -13
- package/docs/webapi/wait.mustache +0 -8
- package/docs/webapi/waitForClickable.mustache +0 -11
- package/docs/webapi/waitForDetached.mustache +0 -10
- 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/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/docs/webdriver.md +0 -657
- package/docs/wiki/Books-&-Posts.md +0 -27
- package/docs/wiki/Community-Helpers-&-Plugins.md +0 -49
- package/docs/wiki/Converting-Playwright-to-Istanbul-Coverage.md +0 -29
- package/docs/wiki/Examples.md +0 -139
- package/docs/wiki/Google-Summer-of-Code-(GSoC)-2020.md +0 -68
- package/docs/wiki/Home.md +0 -16
- package/docs/wiki/Release-Process.md +0 -24
- package/docs/wiki/Roadmap.md +0 -23
- package/docs/wiki/Tests.md +0 -1393
- package/docs/wiki/Upgrading-to-CodeceptJS-3.md +0 -153
- package/docs/wiki/Videos.md +0 -19
package/lib/helper/WebDriver.js
CHANGED
|
@@ -5,6 +5,7 @@ const path = require('path');
|
|
|
5
5
|
const fs = require('fs');
|
|
6
6
|
|
|
7
7
|
const Helper = require('@codeceptjs/helper');
|
|
8
|
+
const crypto = require('crypto');
|
|
8
9
|
const stringIncludes = require('../assert/include').includes;
|
|
9
10
|
const { urlEquals, equals } = require('../assert/equal');
|
|
10
11
|
const { debug } = require('../output');
|
|
@@ -27,6 +28,10 @@ const {
|
|
|
27
28
|
const ElementNotFound = require('./errors/ElementNotFound');
|
|
28
29
|
const ConnectionRefused = require('./errors/ConnectionRefused');
|
|
29
30
|
const Locator = require('../locator');
|
|
31
|
+
const { highlightElement } = require('./scripts/highlightElement');
|
|
32
|
+
const store = require('../store');
|
|
33
|
+
const { focusElement } = require('./scripts/focusElement');
|
|
34
|
+
const { blurElement } = require('./scripts/blurElement');
|
|
30
35
|
|
|
31
36
|
const SHADOW = 'shadow';
|
|
32
37
|
const webRoot = 'body';
|
|
@@ -39,7 +44,7 @@ const webRoot = 'body';
|
|
|
39
44
|
* @typedef WebDriverConfig
|
|
40
45
|
* @type {object}
|
|
41
46
|
* @prop {string} url - base url of website to be tested.
|
|
42
|
-
* @prop {string} browser
|
|
47
|
+
* @prop {string} browser - Browser in which to perform testing.
|
|
43
48
|
* @prop {string} [basicAuth] - (optional) the basic authentication to pass to base url. Example: {username: 'username', password: 'password'}
|
|
44
49
|
* @prop {string} [host=localhost] - WebDriver host to connect.
|
|
45
50
|
* @prop {number} [port=4444] - WebDriver port to connect.
|
|
@@ -57,6 +62,9 @@ const webRoot = 'body';
|
|
|
57
62
|
* @prop {object} [desiredCapabilities] Selenium's [desired capabilities](https://github.com/SeleniumHQ/selenium/wiki/DesiredCapabilities).
|
|
58
63
|
* @prop {boolean} [manualStart=false] - do not start browser before a test, start it manually inside a helper with `this.helpers["WebDriver"]._startBrowser()`.
|
|
59
64
|
* @prop {object} [timeouts] [WebDriver timeouts](http://webdriver.io/docs/timeouts.html) defined as hash.
|
|
65
|
+
* @prop {boolean} [highlightElement] - highlight the interacting elements. Default: false. Note: only activate under verbose mode (--verbose).
|
|
66
|
+
* @prop {string} [logLevel=silent] - level of logging verbosity. Default: silent. Options: trace | debug | info | warn | error | silent. More info: https://webdriver.io/docs/configuration/#loglevel
|
|
67
|
+
* @prop {boolean} [devtoolsProtocol=false] - enable devtools protocol. Default: false. More info: https://webdriver.io/docs/automationProtocols/#devtools-protocol.
|
|
60
68
|
*/
|
|
61
69
|
const config = {};
|
|
62
70
|
|
|
@@ -66,6 +74,13 @@ const config = {};
|
|
|
66
74
|
*
|
|
67
75
|
* WebDriver requires Selenium Server and ChromeDriver/GeckoDriver to be installed. Those tools can be easily installed via NPM. Please check [Testing with WebDriver](https://codecept.io/webdriver/#testing-with-webdriver) for more details.
|
|
68
76
|
*
|
|
77
|
+
* With the release of WebdriverIO version v8.14.0, and onwards, all driver management hassles are now a thing of the past 🙌. Read more [here](https://webdriver.io/blog/2023/07/31/driver-management/).
|
|
78
|
+
* One of the significant advantages of this update is that you can now get rid of any driver services you previously had to manage, such as
|
|
79
|
+
* `wdio-chromedriver-service`, `wdio-geckodriver-service`, `wdio-edgedriver-service`, `wdio-safaridriver-service`, and even `@wdio/selenium-standalone-service`.
|
|
80
|
+
*
|
|
81
|
+
* For those who require custom driver options, fear not; WebDriver Helper allows you to pass in driver options through custom WebDriver configuration.
|
|
82
|
+
* If you have a custom grid, use a cloud service, or prefer to run your own driver, there's no need to worry since WebDriver Helper will only start a driver when there are no other connection information settings like hostname or port specified.
|
|
83
|
+
*
|
|
69
84
|
* <!-- configuration -->
|
|
70
85
|
*
|
|
71
86
|
* Example:
|
|
@@ -87,6 +102,28 @@ const config = {};
|
|
|
87
102
|
* }
|
|
88
103
|
* ```
|
|
89
104
|
*
|
|
105
|
+
* Testing Chrome locally is now more convenient than ever. You can define a browser channel, and WebDriver Helper will take care of downloading the specified browser version for you.
|
|
106
|
+
* For example:
|
|
107
|
+
*
|
|
108
|
+
* ```js
|
|
109
|
+
* {
|
|
110
|
+
* helpers: {
|
|
111
|
+
* WebDriver : {
|
|
112
|
+
* smartWait: 5000,
|
|
113
|
+
* browser: "chrome",
|
|
114
|
+
* browserVersion: '116.0.5793.0', // or 'stable', 'beta', 'dev' or 'canary'
|
|
115
|
+
* restart: false,
|
|
116
|
+
* windowSize: "maximize",
|
|
117
|
+
* timeouts: {
|
|
118
|
+
* "script": 60000,
|
|
119
|
+
* "page load": 10000
|
|
120
|
+
* }
|
|
121
|
+
* }
|
|
122
|
+
* }
|
|
123
|
+
* }
|
|
124
|
+
* ```
|
|
125
|
+
*
|
|
126
|
+
*
|
|
90
127
|
* Example with basic authentication
|
|
91
128
|
* ```js
|
|
92
129
|
* {
|
|
@@ -127,6 +164,25 @@ const config = {};
|
|
|
127
164
|
* }
|
|
128
165
|
* ```
|
|
129
166
|
*
|
|
167
|
+
* ### Running with devtools protocol
|
|
168
|
+
*
|
|
169
|
+
* ```js
|
|
170
|
+
* {
|
|
171
|
+
* helpers: {
|
|
172
|
+
* WebDriver : {
|
|
173
|
+
* url: "http://localhost",
|
|
174
|
+
* browser: "chrome",
|
|
175
|
+
* devtoolsProtocol: true,
|
|
176
|
+
* desiredCapabilities: {
|
|
177
|
+
* chromeOptions: {
|
|
178
|
+
* args: [ "--headless", "--disable-gpu", "--no-sandbox" ]
|
|
179
|
+
* }
|
|
180
|
+
* }
|
|
181
|
+
* }
|
|
182
|
+
* }
|
|
183
|
+
* }
|
|
184
|
+
* ```
|
|
185
|
+
*
|
|
130
186
|
* ### Internet Explorer
|
|
131
187
|
*
|
|
132
188
|
* Additional configuration params can be used from [IE options](https://seleniumhq.github.io/selenium/docs/api/rb/Selenium/WebDriver/IE/Options.html)
|
|
@@ -409,7 +465,6 @@ class WebDriver extends Helper {
|
|
|
409
465
|
_validateConfig(config) {
|
|
410
466
|
const defaults = {
|
|
411
467
|
logLevel: 'silent',
|
|
412
|
-
path: '/wd/hub',
|
|
413
468
|
// codeceptjs
|
|
414
469
|
remoteFileUpload: true,
|
|
415
470
|
smartWait: 0,
|
|
@@ -423,17 +478,23 @@ class WebDriver extends Helper {
|
|
|
423
478
|
keepCookies: false,
|
|
424
479
|
keepBrowserState: false,
|
|
425
480
|
deprecationWarnings: false,
|
|
481
|
+
highlightElement: false,
|
|
426
482
|
};
|
|
427
483
|
|
|
428
484
|
// override defaults with config
|
|
429
485
|
config = Object.assign(defaults, config);
|
|
430
486
|
|
|
431
|
-
if (
|
|
487
|
+
if (config.host) {
|
|
488
|
+
// webdriverio spec
|
|
489
|
+
config.hostname = config.host;
|
|
490
|
+
config.path = '/wd/hub';
|
|
491
|
+
}
|
|
432
492
|
config.baseUrl = config.url || config.baseUrl;
|
|
433
493
|
if (config.desiredCapabilities && Object.keys(config.desiredCapabilities).length) {
|
|
434
494
|
config.capabilities = config.desiredCapabilities;
|
|
435
495
|
}
|
|
436
496
|
config.capabilities.browserName = config.browser || config.capabilities.browserName;
|
|
497
|
+
config.capabilities.browserVersion = config.browserVersion || config.capabilities.browserVersion;
|
|
437
498
|
if (config.capabilities.chromeOptions) {
|
|
438
499
|
config.capabilities['goog:chromeOptions'] = config.capabilities.chromeOptions;
|
|
439
500
|
delete config.capabilities.chromeOptions;
|
|
@@ -535,6 +596,10 @@ class WebDriver extends Helper {
|
|
|
535
596
|
delete this.options.capabilities.hostname;
|
|
536
597
|
delete this.options.capabilities.port;
|
|
537
598
|
delete this.options.capabilities.path;
|
|
599
|
+
if (this.options.devtoolsProtocol) {
|
|
600
|
+
if (!['chrome', 'chromium'].includes(this.options.browser.toLowerCase())) throw Error('The devtools protocol is only working with Chrome or Chromium');
|
|
601
|
+
this.options.automationProtocol = 'devtools';
|
|
602
|
+
}
|
|
538
603
|
this.browser = await webdriverio.remote(this.options);
|
|
539
604
|
}
|
|
540
605
|
} catch (err) {
|
|
@@ -822,7 +887,7 @@ class WebDriver extends Helper {
|
|
|
822
887
|
}
|
|
823
888
|
|
|
824
889
|
/**
|
|
825
|
-
* Find a checkbox by providing human
|
|
890
|
+
* Find a checkbox by providing human-readable text:
|
|
826
891
|
*
|
|
827
892
|
* ```js
|
|
828
893
|
* this.helpers['WebDriver']._locateCheckable('I agree with terms and conditions').then // ...
|
|
@@ -835,7 +900,7 @@ class WebDriver extends Helper {
|
|
|
835
900
|
}
|
|
836
901
|
|
|
837
902
|
/**
|
|
838
|
-
* Find a clickable element by providing human
|
|
903
|
+
* Find a clickable element by providing human-readable text:
|
|
839
904
|
*
|
|
840
905
|
* ```js
|
|
841
906
|
* const els = await this.helpers.WebDriver._locateClickable('Next page');
|
|
@@ -850,7 +915,7 @@ class WebDriver extends Helper {
|
|
|
850
915
|
}
|
|
851
916
|
|
|
852
917
|
/**
|
|
853
|
-
* Find field elements by providing human
|
|
918
|
+
* Find field elements by providing human-readable text:
|
|
854
919
|
*
|
|
855
920
|
* ```js
|
|
856
921
|
* this.helpers['WebDriver']._locateFields('Your email').then // ...
|
|
@@ -862,6 +927,14 @@ class WebDriver extends Helper {
|
|
|
862
927
|
return findFields.call(this, locator).then(res => res);
|
|
863
928
|
}
|
|
864
929
|
|
|
930
|
+
/**
|
|
931
|
+
* {{> grabWebElements }}
|
|
932
|
+
*
|
|
933
|
+
*/
|
|
934
|
+
async grabWebElements(locator) {
|
|
935
|
+
return this._locate(locator);
|
|
936
|
+
}
|
|
937
|
+
|
|
865
938
|
/**
|
|
866
939
|
* Set [WebDriver timeouts](https://webdriver.io/docs/timeouts.html) in realtime.
|
|
867
940
|
*
|
|
@@ -914,6 +987,7 @@ class WebDriver extends Helper {
|
|
|
914
987
|
assertElementExists(res, locator, 'Clickable element');
|
|
915
988
|
}
|
|
916
989
|
const elem = usingFirstElement(res);
|
|
990
|
+
highlightActiveElement.call(this, elem);
|
|
917
991
|
return this.browser[clickMethod](getElementId(elem));
|
|
918
992
|
}
|
|
919
993
|
|
|
@@ -932,6 +1006,7 @@ class WebDriver extends Helper {
|
|
|
932
1006
|
assertElementExists(res, locator, 'Clickable element');
|
|
933
1007
|
}
|
|
934
1008
|
const elem = usingFirstElement(res);
|
|
1009
|
+
highlightActiveElement.call(this, elem);
|
|
935
1010
|
|
|
936
1011
|
return this.executeScript((el) => {
|
|
937
1012
|
if (document.activeElement instanceof HTMLElement) {
|
|
@@ -959,6 +1034,7 @@ class WebDriver extends Helper {
|
|
|
959
1034
|
}
|
|
960
1035
|
|
|
961
1036
|
const elem = usingFirstElement(res);
|
|
1037
|
+
highlightActiveElement.call(this, elem);
|
|
962
1038
|
return elem.doubleClick();
|
|
963
1039
|
}
|
|
964
1040
|
|
|
@@ -1024,7 +1100,9 @@ class WebDriver extends Helper {
|
|
|
1024
1100
|
const res = await findFields.call(this, field);
|
|
1025
1101
|
assertElementExists(res, field, 'Field');
|
|
1026
1102
|
const elem = usingFirstElement(res);
|
|
1027
|
-
|
|
1103
|
+
highlightActiveElement.call(this, elem);
|
|
1104
|
+
await elem.clearValue();
|
|
1105
|
+
await elem.setValue(value.toString());
|
|
1028
1106
|
}
|
|
1029
1107
|
|
|
1030
1108
|
/**
|
|
@@ -1035,7 +1113,12 @@ class WebDriver extends Helper {
|
|
|
1035
1113
|
const res = await findFields.call(this, field);
|
|
1036
1114
|
assertElementExists(res, field, 'Field');
|
|
1037
1115
|
const elem = usingFirstElement(res);
|
|
1038
|
-
|
|
1116
|
+
highlightActiveElement.call(this, elem);
|
|
1117
|
+
if (this.options.automationProtocol) {
|
|
1118
|
+
const curentValue = await elem.getValue();
|
|
1119
|
+
return elem.setValue(curentValue + value.toString());
|
|
1120
|
+
}
|
|
1121
|
+
return elem.addValue(value.toString());
|
|
1039
1122
|
}
|
|
1040
1123
|
|
|
1041
1124
|
/**
|
|
@@ -1046,6 +1129,10 @@ class WebDriver extends Helper {
|
|
|
1046
1129
|
const res = await findFields.call(this, field);
|
|
1047
1130
|
assertElementExists(res, field, 'Field');
|
|
1048
1131
|
const elem = usingFirstElement(res);
|
|
1132
|
+
highlightActiveElement.call(this, elem);
|
|
1133
|
+
if (this.options.automationProtocol) {
|
|
1134
|
+
return elem.setValue('');
|
|
1135
|
+
}
|
|
1049
1136
|
return elem.clearValue(getElementId(elem));
|
|
1050
1137
|
}
|
|
1051
1138
|
|
|
@@ -1056,6 +1143,7 @@ class WebDriver extends Helper {
|
|
|
1056
1143
|
const res = await findFields.call(this, select);
|
|
1057
1144
|
assertElementExists(res, select, 'Selectable field');
|
|
1058
1145
|
const elem = usingFirstElement(res);
|
|
1146
|
+
highlightActiveElement.call(this, elem);
|
|
1059
1147
|
|
|
1060
1148
|
if (!Array.isArray(option)) {
|
|
1061
1149
|
option = [option];
|
|
@@ -1082,8 +1170,9 @@ class WebDriver extends Helper {
|
|
|
1082
1170
|
}
|
|
1083
1171
|
|
|
1084
1172
|
/**
|
|
1085
|
-
* {{> attachFile }}
|
|
1086
1173
|
* Appium: not tested
|
|
1174
|
+
*
|
|
1175
|
+
* {{> attachFile }}
|
|
1087
1176
|
*/
|
|
1088
1177
|
async attachFile(locator, pathToFile) {
|
|
1089
1178
|
let file = path.join(global.codecept_dir, pathToFile);
|
|
@@ -1097,7 +1186,7 @@ class WebDriver extends Helper {
|
|
|
1097
1186
|
const el = usingFirstElement(res);
|
|
1098
1187
|
|
|
1099
1188
|
// Remote Upload (when running Selenium Server)
|
|
1100
|
-
if (this.options.remoteFileUpload) {
|
|
1189
|
+
if (this.options.remoteFileUpload && !this.options.automationProtocol) {
|
|
1101
1190
|
try {
|
|
1102
1191
|
this.debugSection('File', 'Uploading file to remote server');
|
|
1103
1192
|
file = await this.browser.uploadFile(file);
|
|
@@ -1110,8 +1199,8 @@ class WebDriver extends Helper {
|
|
|
1110
1199
|
}
|
|
1111
1200
|
|
|
1112
1201
|
/**
|
|
1113
|
-
* {{> checkOption }}
|
|
1114
1202
|
* Appium: not tested
|
|
1203
|
+
* {{> checkOption }}
|
|
1115
1204
|
*/
|
|
1116
1205
|
async checkOption(field, context = null) {
|
|
1117
1206
|
const clickMethod = this.browser.isMobile && !this.browser.isW3C ? 'touchClick' : 'elementClick';
|
|
@@ -1122,6 +1211,7 @@ class WebDriver extends Helper {
|
|
|
1122
1211
|
assertElementExists(res, field, 'Checkable');
|
|
1123
1212
|
const elem = usingFirstElement(res);
|
|
1124
1213
|
const elementId = getElementId(elem);
|
|
1214
|
+
highlightActiveElement.call(this, elem);
|
|
1125
1215
|
|
|
1126
1216
|
const isSelected = await this.browser.isElementSelected(elementId);
|
|
1127
1217
|
if (isSelected) return Promise.resolve(true);
|
|
@@ -1129,8 +1219,8 @@ class WebDriver extends Helper {
|
|
|
1129
1219
|
}
|
|
1130
1220
|
|
|
1131
1221
|
/**
|
|
1132
|
-
* {{> uncheckOption }}
|
|
1133
1222
|
* Appium: not tested
|
|
1223
|
+
* {{> uncheckOption }}
|
|
1134
1224
|
*/
|
|
1135
1225
|
async uncheckOption(field, context = null) {
|
|
1136
1226
|
const clickMethod = this.browser.isMobile && !this.browser.isW3C ? 'touchClick' : 'elementClick';
|
|
@@ -1141,6 +1231,7 @@ class WebDriver extends Helper {
|
|
|
1141
1231
|
assertElementExists(res, field, 'Checkable');
|
|
1142
1232
|
const elem = usingFirstElement(res);
|
|
1143
1233
|
const elementId = getElementId(elem);
|
|
1234
|
+
highlightActiveElement.call(this, elem);
|
|
1144
1235
|
|
|
1145
1236
|
const isSelected = await this.browser.isElementSelected(elementId);
|
|
1146
1237
|
if (!isSelected) return Promise.resolve(true);
|
|
@@ -1332,7 +1423,8 @@ class WebDriver extends Helper {
|
|
|
1332
1423
|
*
|
|
1333
1424
|
*/
|
|
1334
1425
|
async seeInField(field, value) {
|
|
1335
|
-
|
|
1426
|
+
const _value = (typeof value === 'boolean') ? value : value.toString();
|
|
1427
|
+
return proceedSeeField.call(this, 'assert', field, _value);
|
|
1336
1428
|
}
|
|
1337
1429
|
|
|
1338
1430
|
/**
|
|
@@ -1340,20 +1432,21 @@ class WebDriver extends Helper {
|
|
|
1340
1432
|
*
|
|
1341
1433
|
*/
|
|
1342
1434
|
async dontSeeInField(field, value) {
|
|
1343
|
-
|
|
1435
|
+
const _value = (typeof value === 'boolean') ? value : value.toString();
|
|
1436
|
+
return proceedSeeField.call(this, 'negate', field, _value);
|
|
1344
1437
|
}
|
|
1345
1438
|
|
|
1346
1439
|
/**
|
|
1347
|
-
* {{> seeCheckboxIsChecked }}
|
|
1348
1440
|
* Appium: not tested
|
|
1441
|
+
* {{> seeCheckboxIsChecked }}
|
|
1349
1442
|
*/
|
|
1350
1443
|
async seeCheckboxIsChecked(field) {
|
|
1351
1444
|
return proceedSeeCheckbox.call(this, 'assert', field);
|
|
1352
1445
|
}
|
|
1353
1446
|
|
|
1354
1447
|
/**
|
|
1355
|
-
* {{> dontSeeCheckboxIsChecked }}
|
|
1356
1448
|
* Appium: not tested
|
|
1449
|
+
* {{> dontSeeCheckboxIsChecked }}
|
|
1357
1450
|
*/
|
|
1358
1451
|
async dontSeeCheckboxIsChecked(field) {
|
|
1359
1452
|
return proceedSeeCheckbox.call(this, 'negate', field);
|
|
@@ -1471,33 +1564,33 @@ class WebDriver extends Helper {
|
|
|
1471
1564
|
async seeCssPropertiesOnElements(locator, cssProperties) {
|
|
1472
1565
|
const res = await this._locate(locator);
|
|
1473
1566
|
assertElementExists(res, locator);
|
|
1567
|
+
|
|
1568
|
+
const cssPropertiesCamelCase = convertCssPropertiesToCamelCase(cssProperties);
|
|
1474
1569
|
const elemAmount = res.length;
|
|
1570
|
+
let props = [];
|
|
1475
1571
|
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
const
|
|
1479
|
-
if (isColorProperty(prop)
|
|
1480
|
-
|
|
1572
|
+
for (const element of res) {
|
|
1573
|
+
for (const prop of Object.keys(cssProperties)) {
|
|
1574
|
+
const cssProp = await this.grabCssPropertyFrom(locator, prop);
|
|
1575
|
+
if (isColorProperty(prop)) {
|
|
1576
|
+
props.push(convertColorToRGBA(cssProp));
|
|
1577
|
+
} else {
|
|
1578
|
+
props.push(cssProp);
|
|
1481
1579
|
}
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
});
|
|
1485
|
-
|
|
1486
|
-
const cssPropertiesCamelCase = convertCssPropertiesToCamelCase(cssProperties);
|
|
1580
|
+
}
|
|
1581
|
+
}
|
|
1487
1582
|
|
|
1488
1583
|
const values = Object.keys(cssPropertiesCamelCase).map(key => cssPropertiesCamelCase[key]);
|
|
1489
1584
|
if (!Array.isArray(props)) props = [props];
|
|
1490
1585
|
let chunked = chunkArray(props, values.length);
|
|
1491
1586
|
chunked = chunked.filter((val) => {
|
|
1492
1587
|
for (let i = 0; i < val.length; ++i) {
|
|
1493
|
-
|
|
1588
|
+
// eslint-disable-next-line eqeqeq
|
|
1589
|
+
if (val[i] != values[i]) return false;
|
|
1494
1590
|
}
|
|
1495
1591
|
return true;
|
|
1496
1592
|
});
|
|
1497
|
-
return assert.
|
|
1498
|
-
chunked.length === elemAmount,
|
|
1499
|
-
`expected all elements (${(new Locator(locator))}) to have CSS property ${JSON.stringify(cssProperties)}`,
|
|
1500
|
-
);
|
|
1593
|
+
return equals(`all elements (${(new Locator(locator))}) to have CSS property ${JSON.stringify(cssProperties)}`).assert(chunked.length, elemAmount);
|
|
1501
1594
|
}
|
|
1502
1595
|
|
|
1503
1596
|
/**
|
|
@@ -1517,7 +1610,9 @@ class WebDriver extends Helper {
|
|
|
1517
1610
|
let chunked = chunkArray(attrs, values.length);
|
|
1518
1611
|
chunked = chunked.filter((val) => {
|
|
1519
1612
|
for (let i = 0; i < val.length; ++i) {
|
|
1520
|
-
|
|
1613
|
+
const _actual = Number.isNaN(val[i]) || (typeof values[i]) === 'string' ? val[i] : Number.parseInt(val[i], 10);
|
|
1614
|
+
const _expected = Number.isNaN(values[i]) || (typeof values[i]) === 'string' ? values[i] : Number.parseInt(values[i], 10);
|
|
1615
|
+
if (_actual !== _expected) return false;
|
|
1521
1616
|
}
|
|
1522
1617
|
return true;
|
|
1523
1618
|
});
|
|
@@ -1576,10 +1671,9 @@ class WebDriver extends Helper {
|
|
|
1576
1671
|
}
|
|
1577
1672
|
|
|
1578
1673
|
/**
|
|
1579
|
-
* {{> executeScript }}
|
|
1580
|
-
*
|
|
1581
|
-
*
|
|
1582
1674
|
* Wraps [execute](http://webdriver.io/api/protocol/execute.html) command.
|
|
1675
|
+
*
|
|
1676
|
+
* {{> executeScript }}
|
|
1583
1677
|
*/
|
|
1584
1678
|
executeScript(...args) {
|
|
1585
1679
|
return this.browser.execute.apply(this.browser, args);
|
|
@@ -1642,7 +1736,11 @@ class WebDriver extends Helper {
|
|
|
1642
1736
|
const res = await this._locate(withStrictLocator(locator), true);
|
|
1643
1737
|
assertElementExists(res, locator);
|
|
1644
1738
|
const elem = usingFirstElement(res);
|
|
1645
|
-
|
|
1739
|
+
try {
|
|
1740
|
+
await elem.moveTo({ xOffset, yOffset });
|
|
1741
|
+
} catch (e) {
|
|
1742
|
+
debug(e.message);
|
|
1743
|
+
}
|
|
1646
1744
|
}
|
|
1647
1745
|
|
|
1648
1746
|
/**
|
|
@@ -1701,11 +1799,8 @@ class WebDriver extends Helper {
|
|
|
1701
1799
|
}
|
|
1702
1800
|
|
|
1703
1801
|
/**
|
|
1802
|
+
* Uses Selenium's JSON [cookie format](https://code.google.com/p/selenium/wiki/JsonWireProtocol#Cookie_JSON_Object).
|
|
1704
1803
|
* {{> setCookie }}
|
|
1705
|
-
*
|
|
1706
|
-
*
|
|
1707
|
-
* Uses Selenium's JSON [cookie
|
|
1708
|
-
* format](https://code.google.com/p/selenium/wiki/JsonWireProtocol#Cookie_JSON_Object).
|
|
1709
1804
|
*/
|
|
1710
1805
|
async setCookie(cookie) {
|
|
1711
1806
|
return this.browser.setCookies(cookie);
|
|
@@ -1758,7 +1853,7 @@ class WebDriver extends Helper {
|
|
|
1758
1853
|
}
|
|
1759
1854
|
|
|
1760
1855
|
/**
|
|
1761
|
-
* Dismisses the active JavaScript popup, as created by window.alert|window.confirm|window.prompt
|
|
1856
|
+
* Dismisses the active JavaScript popup, as created by `window.alert|window.confirm|window.prompt`.
|
|
1762
1857
|
*
|
|
1763
1858
|
*/
|
|
1764
1859
|
async cancelPopup() {
|
|
@@ -1832,9 +1927,9 @@ class WebDriver extends Helper {
|
|
|
1832
1927
|
}
|
|
1833
1928
|
|
|
1834
1929
|
/**
|
|
1835
|
-
* {{> pressKeyWithKeyNormalization }}
|
|
1836
|
-
*
|
|
1837
1930
|
* _Note:_ In case a text field or textarea is focused be aware that some browsers do not respect active modifier when combining modifier keys with other keys.
|
|
1931
|
+
*
|
|
1932
|
+
* {{> pressKeyWithKeyNormalization }}
|
|
1838
1933
|
*/
|
|
1839
1934
|
async pressKey(key) {
|
|
1840
1935
|
const modifiers = [];
|
|
@@ -1879,6 +1974,7 @@ class WebDriver extends Helper {
|
|
|
1879
1974
|
*/
|
|
1880
1975
|
async type(keys, delay = null) {
|
|
1881
1976
|
if (!Array.isArray(keys)) {
|
|
1977
|
+
keys = keys.toString();
|
|
1882
1978
|
keys = keys.split('');
|
|
1883
1979
|
}
|
|
1884
1980
|
if (delay) {
|
|
@@ -1892,11 +1988,12 @@ class WebDriver extends Helper {
|
|
|
1892
1988
|
}
|
|
1893
1989
|
|
|
1894
1990
|
/**
|
|
1895
|
-
* {{> resizeWindow }}
|
|
1896
1991
|
* Appium: not tested in web, in apps doesn't work
|
|
1992
|
+
*
|
|
1993
|
+
* {{> resizeWindow }}
|
|
1897
1994
|
*/
|
|
1898
1995
|
async resizeWindow(width, height) {
|
|
1899
|
-
return this.
|
|
1996
|
+
return this.browser.setWindowSize(width, height);
|
|
1900
1997
|
}
|
|
1901
1998
|
|
|
1902
1999
|
async _resizeBrowserWindow(browser, width, height) {
|
|
@@ -1921,8 +2018,32 @@ class WebDriver extends Helper {
|
|
|
1921
2018
|
}
|
|
1922
2019
|
|
|
1923
2020
|
/**
|
|
1924
|
-
* {{>
|
|
2021
|
+
* {{> focus }}
|
|
2022
|
+
*
|
|
2023
|
+
*/
|
|
2024
|
+
async focus(locator) {
|
|
2025
|
+
const els = await this._locate(locator);
|
|
2026
|
+
assertElementExists(els, locator, 'Element to focus');
|
|
2027
|
+
const el = usingFirstElement(els);
|
|
2028
|
+
|
|
2029
|
+
await focusElement(el, this.browser);
|
|
2030
|
+
}
|
|
2031
|
+
|
|
2032
|
+
/**
|
|
2033
|
+
* {{> blur }}
|
|
2034
|
+
*
|
|
2035
|
+
*/
|
|
2036
|
+
async blur(locator) {
|
|
2037
|
+
const els = await this._locate(locator);
|
|
2038
|
+
assertElementExists(els, locator, 'Element to blur');
|
|
2039
|
+
const el = usingFirstElement(els);
|
|
2040
|
+
|
|
2041
|
+
await blurElement(el, this.browser);
|
|
2042
|
+
}
|
|
2043
|
+
|
|
2044
|
+
/**
|
|
1925
2045
|
* Appium: not tested
|
|
2046
|
+
* {{> dragAndDrop }}
|
|
1926
2047
|
*/
|
|
1927
2048
|
async dragAndDrop(srcElement, destElement) {
|
|
1928
2049
|
let sourceEl = await this._locate(srcElement);
|
|
@@ -2022,7 +2143,9 @@ class WebDriver extends Helper {
|
|
|
2022
2143
|
* {{> wait }}
|
|
2023
2144
|
*/
|
|
2024
2145
|
async wait(sec) {
|
|
2025
|
-
return new Promise(resolve =>
|
|
2146
|
+
return new Promise(resolve => {
|
|
2147
|
+
setTimeout(resolve, sec * 1000);
|
|
2148
|
+
});
|
|
2026
2149
|
}
|
|
2027
2150
|
|
|
2028
2151
|
/**
|
|
@@ -2127,20 +2250,18 @@ class WebDriver extends Helper {
|
|
|
2127
2250
|
const aSec = sec || this.options.waitForTimeoutInSeconds;
|
|
2128
2251
|
const _context = context || this.root;
|
|
2129
2252
|
|
|
2130
|
-
return this.browser.waitUntil(
|
|
2131
|
-
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
|
|
2140
|
-
|
|
2141
|
-
|
|
2142
|
-
},
|
|
2143
|
-
);
|
|
2253
|
+
return this.browser.waitUntil(async () => {
|
|
2254
|
+
const res = await this.$$(withStrictLocator.call(this, _context));
|
|
2255
|
+
if (!res || res.length === 0) return false;
|
|
2256
|
+
const selected = await forEachAsync(res, async el => this.browser.getElementText(getElementId(el)));
|
|
2257
|
+
if (Array.isArray(selected)) {
|
|
2258
|
+
return selected.filter(part => part.indexOf(text) >= 0).length > 0;
|
|
2259
|
+
}
|
|
2260
|
+
return selected.indexOf(text) >= 0;
|
|
2261
|
+
}, {
|
|
2262
|
+
timeout: aSec * 1000,
|
|
2263
|
+
timeoutMsg: `element (${_context}) is not in DOM or there is no element(${_context}) with text "${text}" after ${aSec} sec`,
|
|
2264
|
+
});
|
|
2144
2265
|
}
|
|
2145
2266
|
|
|
2146
2267
|
/**
|
|
@@ -2150,20 +2271,18 @@ class WebDriver extends Helper {
|
|
|
2150
2271
|
const client = this.browser;
|
|
2151
2272
|
const aSec = sec || this.options.waitForTimeoutInSeconds;
|
|
2152
2273
|
|
|
2153
|
-
return client.waitUntil(
|
|
2154
|
-
|
|
2155
|
-
|
|
2156
|
-
|
|
2157
|
-
|
|
2158
|
-
|
|
2159
|
-
|
|
2160
|
-
|
|
2161
|
-
|
|
2162
|
-
|
|
2163
|
-
|
|
2164
|
-
|
|
2165
|
-
},
|
|
2166
|
-
);
|
|
2274
|
+
return client.waitUntil(async () => {
|
|
2275
|
+
const res = await findFields.call(this, field);
|
|
2276
|
+
if (!res || res.length === 0) return false;
|
|
2277
|
+
const selected = await forEachAsync(res, async el => el.getValue());
|
|
2278
|
+
if (Array.isArray(selected)) {
|
|
2279
|
+
return selected.filter(part => part.indexOf(value) >= 0).length > 0;
|
|
2280
|
+
}
|
|
2281
|
+
return selected.indexOf(value) >= 0;
|
|
2282
|
+
}, {
|
|
2283
|
+
timeout: aSec * 1000,
|
|
2284
|
+
timeoutMsg: `element (${field}) is not in DOM or there is no element(${field}) with value "${value}" after ${aSec} sec`,
|
|
2285
|
+
});
|
|
2167
2286
|
}
|
|
2168
2287
|
|
|
2169
2288
|
/**
|
|
@@ -2255,12 +2374,33 @@ class WebDriver extends Helper {
|
|
|
2255
2374
|
return this.browser.waitUntil(async () => this.browser.execute(fn, ...args), { timeout: aSec * 1000, timeoutMsg: '' });
|
|
2256
2375
|
}
|
|
2257
2376
|
|
|
2377
|
+
/**
|
|
2378
|
+
* {{> waitForNumberOfTabs }}
|
|
2379
|
+
*/
|
|
2380
|
+
async waitForNumberOfTabs(expectedTabs, sec) {
|
|
2381
|
+
const waitTimeout = sec ? sec * 1000 : this.options.waitForTimeoutInSeconds;
|
|
2382
|
+
let currentTabs;
|
|
2383
|
+
let count = 0;
|
|
2384
|
+
|
|
2385
|
+
do {
|
|
2386
|
+
currentTabs = await this.grabNumberOfOpenTabs();
|
|
2387
|
+
await this.wait(1);
|
|
2388
|
+
count += 1000;
|
|
2389
|
+
if (currentTabs >= expectedTabs) return;
|
|
2390
|
+
} while (count <= waitTimeout);
|
|
2391
|
+
|
|
2392
|
+
throw new Error(`Expected ${expectedTabs} tabs are not met after ${waitTimeout / 1000} sec.`);
|
|
2393
|
+
}
|
|
2394
|
+
|
|
2258
2395
|
/**
|
|
2259
2396
|
* {{> switchTo }}
|
|
2260
2397
|
*/
|
|
2261
2398
|
async switchTo(locator) {
|
|
2262
2399
|
this.browser.isInsideFrame = true;
|
|
2263
2400
|
if (Number.isInteger(locator)) {
|
|
2401
|
+
if (this.options.automationProtocol) {
|
|
2402
|
+
return this.browser.switchToFrame(locator + 1);
|
|
2403
|
+
}
|
|
2264
2404
|
return this.browser.switchToFrame(locator);
|
|
2265
2405
|
}
|
|
2266
2406
|
if (!locator) {
|
|
@@ -2371,8 +2511,11 @@ class WebDriver extends Helper {
|
|
|
2371
2511
|
const body = document.body;
|
|
2372
2512
|
const html = document.documentElement;
|
|
2373
2513
|
window.scrollTo(0, Math.max(
|
|
2374
|
-
body.scrollHeight,
|
|
2375
|
-
|
|
2514
|
+
body.scrollHeight,
|
|
2515
|
+
body.offsetHeight,
|
|
2516
|
+
html.clientHeight,
|
|
2517
|
+
html.scrollHeight,
|
|
2518
|
+
html.offsetHeight
|
|
2376
2519
|
));
|
|
2377
2520
|
});
|
|
2378
2521
|
/* eslint-enable */
|
|
@@ -2394,21 +2537,41 @@ class WebDriver extends Helper {
|
|
|
2394
2537
|
}
|
|
2395
2538
|
|
|
2396
2539
|
/**
|
|
2540
|
+
* This method is **deprecated**.
|
|
2541
|
+
*
|
|
2542
|
+
*
|
|
2397
2543
|
* {{> setGeoLocation }}
|
|
2398
2544
|
*/
|
|
2399
|
-
async setGeoLocation(latitude, longitude
|
|
2400
|
-
if (
|
|
2401
|
-
|
|
2545
|
+
async setGeoLocation(latitude, longitude) {
|
|
2546
|
+
if (!this.options.automationProtocol) {
|
|
2547
|
+
console.log(`setGeoLocation deprecated:
|
|
2548
|
+
* This command is deprecated due to using deprecated JSON Wire Protocol command. More info: https://webdriver.io/docs/api/jsonwp/#setgeolocation
|
|
2549
|
+
* Switch to devtools protocol to use this command by setting devtoolsProtocol: true in the configuration`);
|
|
2550
|
+
return;
|
|
2402
2551
|
}
|
|
2403
|
-
|
|
2552
|
+
this.geoLocation = { latitude, longitude };
|
|
2553
|
+
const puppeteerBrowser = await this.browser.getPuppeteer();
|
|
2554
|
+
await this.browser.call(async () => {
|
|
2555
|
+
const pages = await puppeteerBrowser.pages();
|
|
2556
|
+
await pages[0].setGeolocation({ latitude, longitude });
|
|
2557
|
+
});
|
|
2404
2558
|
}
|
|
2405
2559
|
|
|
2406
2560
|
/**
|
|
2561
|
+
* This method is **deprecated**.
|
|
2562
|
+
*
|
|
2407
2563
|
* {{> grabGeoLocation }}
|
|
2408
2564
|
*
|
|
2409
2565
|
*/
|
|
2410
2566
|
async grabGeoLocation() {
|
|
2411
|
-
|
|
2567
|
+
if (!this.options.automationProtocol) {
|
|
2568
|
+
console.log(`grabGeoLocation deprecated:
|
|
2569
|
+
* This command is deprecated due to using deprecated JSON Wire Protocol command. More info: https://webdriver.io/docs/api/jsonwp/#getgeolocation
|
|
2570
|
+
* Switch to devtools protocol to use this command by setting devtoolsProtocol: true in the configuration`);
|
|
2571
|
+
return;
|
|
2572
|
+
}
|
|
2573
|
+
if (!this.geoLocation) return 'No GeoLocation is set!';
|
|
2574
|
+
return this.geoLocation;
|
|
2412
2575
|
}
|
|
2413
2576
|
|
|
2414
2577
|
/**
|
|
@@ -2604,7 +2767,7 @@ async function proceedSeeField(assertType, field, value) {
|
|
|
2604
2767
|
}
|
|
2605
2768
|
};
|
|
2606
2769
|
|
|
2607
|
-
const proceedSingle = el =>
|
|
2770
|
+
const proceedSingle = el => el.getValue().then((res) => {
|
|
2608
2771
|
if (res === null) {
|
|
2609
2772
|
throw new Error(`Element ${el.selector} has no value attribute`);
|
|
2610
2773
|
}
|
|
@@ -2873,6 +3036,12 @@ function isModifierKey(key) {
|
|
|
2873
3036
|
return unicodeModifierKeys.includes(key);
|
|
2874
3037
|
}
|
|
2875
3038
|
|
|
3039
|
+
function highlightActiveElement(element) {
|
|
3040
|
+
if (this.options.highlightElement && global.debugMode) {
|
|
3041
|
+
highlightElement(element, this.browser);
|
|
3042
|
+
}
|
|
3043
|
+
}
|
|
3044
|
+
|
|
2876
3045
|
function prepareLocateFn(context) {
|
|
2877
3046
|
if (!context) return this._locate.bind(this);
|
|
2878
3047
|
return (l) => {
|