codeceptjs 3.5.10 → 3.5.12-beta.1
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 +3 -3
- package/lib/command/run-multiple.js +3 -1
- package/lib/command/run-workers.js +32 -1
- package/lib/command/workers/runTests.js +18 -2
- package/lib/helper/Expect.js +33 -33
- package/lib/helper/Playwright.js +49 -26
- package/lib/helper/Puppeteer.js +41 -19
- package/lib/helper/WebDriver.js +155 -48
- package/lib/helper/extras/PlaywrightReactVueLocator.js +38 -0
- package/lib/locator.js +17 -4
- package/lib/plugin/retryFailedStep.js +6 -2
- package/lib/plugin/retryTo.js +2 -2
- package/lib/plugin/tryTo.js +5 -4
- package/package.json +25 -22
- package/typings/index.d.ts +8 -5
- package/typings/promiseBasedTypes.d.ts +203 -75
- package/typings/types.d.ts +213 -145
- package/docs/advanced.md +0 -351
- package/docs/ai.md +0 -248
- package/docs/api.md +0 -323
- package/docs/basics.md +0 -979
- package/docs/bdd.md +0 -539
- package/docs/best.md +0 -237
- package/docs/books.md +0 -37
- package/docs/bootstrap.md +0 -135
- package/docs/build/ApiDataFactory.js +0 -410
- package/docs/build/Appium.js +0 -2027
- package/docs/build/Expect.js +0 -422
- package/docs/build/FileSystem.js +0 -228
- package/docs/build/GraphQL.js +0 -229
- 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 -2152
- package/docs/build/OpenAI.js +0 -126
- package/docs/build/Playwright.js +0 -5078
- package/docs/build/Protractor.js +0 -2706
- package/docs/build/Puppeteer.js +0 -3874
- package/docs/build/REST.js +0 -344
- package/docs/build/TestCafe.js +0 -2125
- package/docs/build/WebDriver.js +0 -4124
- package/docs/changelog.md +0 -2572
- package/docs/commands.md +0 -266
- 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 -379
- package/docs/detox.md +0 -235
- package/docs/docker.md +0 -136
- 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 -1374
- package/docs/helpers/Detox.md +0 -586
- package/docs/helpers/Expect.md +0 -275
- package/docs/helpers/FileSystem.md +0 -152
- package/docs/helpers/GraphQL.md +0 -151
- 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 -1305
- package/docs/helpers/OpenAI.md +0 -70
- package/docs/helpers/Playwright.md +0 -2706
- package/docs/helpers/Polly.md +0 -44
- package/docs/helpers/Protractor.md +0 -1769
- package/docs/helpers/Puppeteer-firefox.md +0 -86
- package/docs/helpers/Puppeteer.md +0 -2291
- package/docs/helpers/REST.md +0 -218
- package/docs/helpers/TestCafe.md +0 -1321
- package/docs/helpers/WebDriver.md +0 -2460
- package/docs/hooks.md +0 -340
- package/docs/index.md +0 -111
- package/docs/installation.md +0 -75
- package/docs/internal-api.md +0 -266
- package/docs/locators.md +0 -331
- package/docs/mobile-react-native-locators.md +0 -67
- package/docs/mobile.md +0 -338
- package/docs/pageobjects.md +0 -291
- package/docs/parallel.md +0 -400
- package/docs/playwright.md +0 -632
- package/docs/plugins.md +0 -1259
- package/docs/puppeteer.md +0 -316
- package/docs/quickstart.md +0 -162
- package/docs/react.md +0 -69
- package/docs/reports.md +0 -392
- package/docs/secrets.md +0 -36
- 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 -11
- package/docs/webapi/attachFile.mustache +0 -12
- package/docs/webapi/blur.mustache +0 -18
- package/docs/webapi/checkOption.mustache +0 -13
- package/docs/webapi/clearCookie.mustache +0 -9
- package/docs/webapi/clearField.mustache +0 -9
- package/docs/webapi/click.mustache +0 -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/focus.mustache +0 -13
- package/docs/webapi/forceClick.mustache +0 -28
- package/docs/webapi/forceRightClick.mustache +0 -18
- package/docs/webapi/grabAllWindowHandles.mustache +0 -7
- package/docs/webapi/grabAttributeFrom.mustache +0 -10
- package/docs/webapi/grabAttributeFromAll.mustache +0 -9
- package/docs/webapi/grabBrowserLogs.mustache +0 -9
- package/docs/webapi/grabCookie.mustache +0 -11
- package/docs/webapi/grabCssPropertyFrom.mustache +0 -11
- package/docs/webapi/grabCssPropertyFromAll.mustache +0 -10
- package/docs/webapi/grabCurrentUrl.mustache +0 -9
- package/docs/webapi/grabCurrentWindowHandle.mustache +0 -6
- package/docs/webapi/grabDataFromPerformanceTiming.mustache +0 -20
- package/docs/webapi/grabElementBoundingRect.mustache +0 -20
- package/docs/webapi/grabGeoLocation.mustache +0 -8
- package/docs/webapi/grabHTMLFrom.mustache +0 -10
- package/docs/webapi/grabHTMLFromAll.mustache +0 -9
- package/docs/webapi/grabNumberOfOpenTabs.mustache +0 -8
- package/docs/webapi/grabNumberOfVisibleElements.mustache +0 -9
- package/docs/webapi/grabPageScrollPosition.mustache +0 -8
- package/docs/webapi/grabPopupText.mustache +0 -5
- package/docs/webapi/grabSource.mustache +0 -8
- package/docs/webapi/grabTextFrom.mustache +0 -10
- package/docs/webapi/grabTextFromAll.mustache +0 -9
- package/docs/webapi/grabTitle.mustache +0 -8
- package/docs/webapi/grabValueFrom.mustache +0 -9
- package/docs/webapi/grabValueFromAll.mustache +0 -8
- package/docs/webapi/grabWebElement.mustache +0 -9
- package/docs/webapi/grabWebElements.mustache +0 -9
- package/docs/webapi/moveCursorTo.mustache +0 -12
- package/docs/webapi/openNewTab.mustache +0 -7
- package/docs/webapi/pressKey.mustache +0 -12
- package/docs/webapi/pressKeyDown.mustache +0 -12
- package/docs/webapi/pressKeyUp.mustache +0 -12
- package/docs/webapi/pressKeyWithKeyNormalization.mustache +0 -60
- package/docs/webapi/refreshPage.mustache +0 -6
- package/docs/webapi/resizeWindow.mustache +0 -6
- package/docs/webapi/rightClick.mustache +0 -14
- package/docs/webapi/saveElementScreenshot.mustache +0 -10
- package/docs/webapi/saveScreenshot.mustache +0 -12
- package/docs/webapi/say.mustache +0 -10
- package/docs/webapi/scrollIntoView.mustache +0 -11
- package/docs/webapi/scrollPageToBottom.mustache +0 -6
- package/docs/webapi/scrollPageToTop.mustache +0 -6
- package/docs/webapi/scrollTo.mustache +0 -12
- package/docs/webapi/see.mustache +0 -11
- package/docs/webapi/seeAttributesOnElements.mustache +0 -9
- package/docs/webapi/seeCheckboxIsChecked.mustache +0 -10
- package/docs/webapi/seeCookie.mustache +0 -8
- package/docs/webapi/seeCssPropertiesOnElements.mustache +0 -9
- package/docs/webapi/seeCurrentUrlEquals.mustache +0 -11
- package/docs/webapi/seeElement.mustache +0 -8
- package/docs/webapi/seeElementInDOM.mustache +0 -8
- package/docs/webapi/seeInCurrentUrl.mustache +0 -8
- package/docs/webapi/seeInField.mustache +0 -12
- package/docs/webapi/seeInPopup.mustache +0 -8
- package/docs/webapi/seeInSource.mustache +0 -7
- package/docs/webapi/seeInTitle.mustache +0 -8
- package/docs/webapi/seeNumberOfElements.mustache +0 -11
- package/docs/webapi/seeNumberOfVisibleElements.mustache +0 -10
- package/docs/webapi/seeTextEquals.mustache +0 -9
- package/docs/webapi/seeTitleEquals.mustache +0 -8
- package/docs/webapi/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 -21
- package/docs/webapi/uncheckOption.mustache +0 -13
- package/docs/webapi/wait.mustache +0 -8
- package/docs/webapi/waitForClickable.mustache +0 -11
- package/docs/webapi/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 -655
- package/docs/wiki/Books-&-Posts.md +0 -27
- package/docs/wiki/Community-Helpers-&-Plugins.md +0 -53
- package/docs/wiki/Converting-Playwright-to-Istanbul-Coverage.md +0 -61
- package/docs/wiki/Examples.md +0 -145
- package/docs/wiki/Google-Summer-of-Code-(GSoC)-2020.md +0 -68
- package/docs/wiki/Home.md +0 -16
- package/docs/wiki/Migration-to-Appium-v2---CodeceptJS.md +0 -83
- 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/extras/PlaywrightReact.js +0 -9
package/lib/helper/Puppeteer.js
CHANGED
|
@@ -259,6 +259,7 @@ class Puppeteer extends Helper {
|
|
|
259
259
|
headless: !this.options.show,
|
|
260
260
|
...this._getOptions(config),
|
|
261
261
|
};
|
|
262
|
+
if (this.puppeteerOptions.headless) this.puppeteerOptions.headless = 'new';
|
|
262
263
|
this.isRemoteBrowser = !!this.puppeteerOptions.browserWSEndpoint;
|
|
263
264
|
popupStore.defaultAction = this.options.defaultPopupAction;
|
|
264
265
|
}
|
|
@@ -684,11 +685,13 @@ class Puppeteer extends Helper {
|
|
|
684
685
|
}
|
|
685
686
|
|
|
686
687
|
/**
|
|
687
|
-
* {{> resizeWindow }}
|
|
688
688
|
*
|
|
689
689
|
* Unlike other drivers Puppeteer changes the size of a viewport, not the window!
|
|
690
|
-
* Puppeteer does not control the window of a browser so it can't adjust its real size.
|
|
690
|
+
* Puppeteer does not control the window of a browser, so it can't adjust its real size.
|
|
691
691
|
* It also can't maximize a window.
|
|
692
|
+
*
|
|
693
|
+
* {{> resizeWindow }}
|
|
694
|
+
*
|
|
692
695
|
*/
|
|
693
696
|
async resizeWindow(width, height) {
|
|
694
697
|
if (width === 'maximize') {
|
|
@@ -740,7 +743,8 @@ class Puppeteer extends Helper {
|
|
|
740
743
|
assertElementExists(els, locator, 'Element to focus');
|
|
741
744
|
const el = els[0];
|
|
742
745
|
|
|
743
|
-
await
|
|
746
|
+
await el.click();
|
|
747
|
+
await el.focus();
|
|
744
748
|
return this._waitForAction();
|
|
745
749
|
}
|
|
746
750
|
|
|
@@ -880,7 +884,7 @@ class Puppeteer extends Helper {
|
|
|
880
884
|
}
|
|
881
885
|
|
|
882
886
|
/**
|
|
883
|
-
* Find a checkbox by providing human
|
|
887
|
+
* Find a checkbox by providing human-readable text:
|
|
884
888
|
* NOTE: Assumes the checkable element exists
|
|
885
889
|
*
|
|
886
890
|
* ```js
|
|
@@ -895,7 +899,7 @@ class Puppeteer extends Helper {
|
|
|
895
899
|
}
|
|
896
900
|
|
|
897
901
|
/**
|
|
898
|
-
* Find a clickable element by providing human
|
|
902
|
+
* Find a clickable element by providing human-readable text:
|
|
899
903
|
*
|
|
900
904
|
* ```js
|
|
901
905
|
* this.helpers['Puppeteer']._locateClickable('Next page').then // ...
|
|
@@ -907,7 +911,7 @@ class Puppeteer extends Helper {
|
|
|
907
911
|
}
|
|
908
912
|
|
|
909
913
|
/**
|
|
910
|
-
* Find field elements by providing human
|
|
914
|
+
* Find field elements by providing human-readable text:
|
|
911
915
|
*
|
|
912
916
|
* ```js
|
|
913
917
|
* this.helpers['Puppeteer']._locateFields('Your email').then // ...
|
|
@@ -1119,7 +1123,7 @@ class Puppeteer extends Helper {
|
|
|
1119
1123
|
* Sets a directory to where save files. Allows to test file downloads.
|
|
1120
1124
|
* Should be used with [FileSystem helper](https://codecept.io/helpers/FileSystem) to check that file were downloaded correctly.
|
|
1121
1125
|
*
|
|
1122
|
-
* By default files are saved to `output/downloads`.
|
|
1126
|
+
* By default, files are saved to `output/downloads`.
|
|
1123
1127
|
* This directory is cleaned on every `handleDownloads` call, to ensure no old files are kept.
|
|
1124
1128
|
*
|
|
1125
1129
|
* ```js
|
|
@@ -1291,9 +1295,9 @@ class Puppeteer extends Helper {
|
|
|
1291
1295
|
}
|
|
1292
1296
|
|
|
1293
1297
|
/**
|
|
1294
|
-
* {{> pressKeyWithKeyNormalization }}
|
|
1295
|
-
*
|
|
1296
1298
|
* _Note:_ Shortcuts like `'Meta'` + `'A'` do not work on macOS ([GoogleChrome/puppeteer#1313](https://github.com/GoogleChrome/puppeteer/issues/1313)).
|
|
1299
|
+
*
|
|
1300
|
+
* {{> pressKeyWithKeyNormalization }}
|
|
1297
1301
|
*/
|
|
1298
1302
|
async pressKey(key) {
|
|
1299
1303
|
const modifiers = [];
|
|
@@ -1395,9 +1399,9 @@ class Puppeteer extends Helper {
|
|
|
1395
1399
|
}
|
|
1396
1400
|
|
|
1397
1401
|
/**
|
|
1398
|
-
* {{> attachFile }}
|
|
1399
|
-
*
|
|
1400
1402
|
* > ⚠ There is an [issue with file upload in Puppeteer 2.1.0 & 2.1.1](https://github.com/puppeteer/puppeteer/issues/5420), downgrade to 2.0.0 if you face it.
|
|
1403
|
+
*
|
|
1404
|
+
* {{> attachFile }}
|
|
1401
1405
|
*/
|
|
1402
1406
|
async attachFile(locator, pathToFile) {
|
|
1403
1407
|
const file = path.join(global.codecept_dir, pathToFile);
|
|
@@ -1628,9 +1632,9 @@ class Puppeteer extends Helper {
|
|
|
1628
1632
|
}
|
|
1629
1633
|
|
|
1630
1634
|
/**
|
|
1631
|
-
*
|
|
1635
|
+
* If a function returns a Promise, tt will wait for its resolution.
|
|
1632
1636
|
*
|
|
1633
|
-
*
|
|
1637
|
+
* {{> executeScript }}
|
|
1634
1638
|
*/
|
|
1635
1639
|
async executeScript(...args) {
|
|
1636
1640
|
let context = this.page;
|
|
@@ -1641,9 +1645,8 @@ class Puppeteer extends Helper {
|
|
|
1641
1645
|
}
|
|
1642
1646
|
|
|
1643
1647
|
/**
|
|
1644
|
-
* {{> executeAsyncScript }}
|
|
1645
|
-
*
|
|
1646
1648
|
* Asynchronous scripts can also be executed with `executeScript` if a function returns a Promise.
|
|
1649
|
+
* {{> executeAsyncScript }}
|
|
1647
1650
|
*/
|
|
1648
1651
|
async executeAsyncScript(...args) {
|
|
1649
1652
|
const asyncFn = function () {
|
|
@@ -1822,7 +1825,8 @@ class Puppeteer extends Helper {
|
|
|
1822
1825
|
for (let i = 0; i < val.length; ++i) {
|
|
1823
1826
|
const _actual = Number.isNaN(val[i]) || (typeof values[i]) === 'string' ? val[i] : Number.parseInt(values[i], 10);
|
|
1824
1827
|
const _expected = Number.isNaN(values[i]) || (typeof values[i]) === 'string' ? values[i] : Number.parseInt(values[i], 10);
|
|
1825
|
-
if
|
|
1828
|
+
// if the attribute doesn't exist, returns false as well
|
|
1829
|
+
if (!_actual || !_actual.includes(_expected)) return false;
|
|
1826
1830
|
}
|
|
1827
1831
|
return true;
|
|
1828
1832
|
});
|
|
@@ -2092,7 +2096,7 @@ class Puppeteer extends Helper {
|
|
|
2092
2096
|
/**
|
|
2093
2097
|
* {{> waitForVisible }}
|
|
2094
2098
|
*
|
|
2095
|
-
*
|
|
2099
|
+
* {{ react }}
|
|
2096
2100
|
*/
|
|
2097
2101
|
async waitForVisible(locator, sec) {
|
|
2098
2102
|
const waitTimeout = sec ? sec * 1000 : this.options.waitForTimeout;
|
|
@@ -2147,6 +2151,24 @@ class Puppeteer extends Helper {
|
|
|
2147
2151
|
});
|
|
2148
2152
|
}
|
|
2149
2153
|
|
|
2154
|
+
/**
|
|
2155
|
+
* {{> waitForNumberOfTabs }}
|
|
2156
|
+
*/
|
|
2157
|
+
async waitForNumberOfTabs(expectedTabs, sec) {
|
|
2158
|
+
const waitTimeout = sec ? sec * 1000 : this.options.waitForTimeout;
|
|
2159
|
+
let currentTabs;
|
|
2160
|
+
let count = 0;
|
|
2161
|
+
|
|
2162
|
+
do {
|
|
2163
|
+
currentTabs = await this.grabNumberOfOpenTabs();
|
|
2164
|
+
await this.wait(1);
|
|
2165
|
+
count += 1000;
|
|
2166
|
+
if (currentTabs >= expectedTabs) return;
|
|
2167
|
+
} while (count <= waitTimeout);
|
|
2168
|
+
|
|
2169
|
+
throw new Error(`Expected ${expectedTabs} tabs are not met after ${waitTimeout / 1000} sec.`);
|
|
2170
|
+
}
|
|
2171
|
+
|
|
2150
2172
|
async _getContext() {
|
|
2151
2173
|
if (this.context && this.context.constructor.name === 'Frame') {
|
|
2152
2174
|
return this.context;
|
|
@@ -2321,9 +2343,9 @@ class Puppeteer extends Helper {
|
|
|
2321
2343
|
}
|
|
2322
2344
|
|
|
2323
2345
|
/**
|
|
2324
|
-
* Waits for navigation to finish. By default takes configured `waitForNavigation` option.
|
|
2346
|
+
* Waits for navigation to finish. By default, takes configured `waitForNavigation` option.
|
|
2325
2347
|
*
|
|
2326
|
-
* See [
|
|
2348
|
+
* See [Puppeteer's reference](https://github.com/GoogleChrome/puppeteer/blob/master/docs/api.md#pagewaitfornavigationoptions)
|
|
2327
2349
|
*
|
|
2328
2350
|
* @param {*} opts
|
|
2329
2351
|
*/
|
package/lib/helper/WebDriver.js
CHANGED
|
@@ -63,6 +63,8 @@ const webRoot = 'body';
|
|
|
63
63
|
* @prop {boolean} [manualStart=false] - do not start browser before a test, start it manually inside a helper with `this.helpers["WebDriver"]._startBrowser()`.
|
|
64
64
|
* @prop {object} [timeouts] [WebDriver timeouts](http://webdriver.io/docs/timeouts.html) defined as hash.
|
|
65
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.
|
|
66
68
|
*/
|
|
67
69
|
const config = {};
|
|
68
70
|
|
|
@@ -72,6 +74,13 @@ const config = {};
|
|
|
72
74
|
*
|
|
73
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.
|
|
74
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
|
+
*
|
|
75
84
|
* <!-- configuration -->
|
|
76
85
|
*
|
|
77
86
|
* Example:
|
|
@@ -93,6 +102,28 @@ const config = {};
|
|
|
93
102
|
* }
|
|
94
103
|
* ```
|
|
95
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
|
+
*
|
|
96
127
|
* Example with basic authentication
|
|
97
128
|
* ```js
|
|
98
129
|
* {
|
|
@@ -133,6 +164,25 @@ const config = {};
|
|
|
133
164
|
* }
|
|
134
165
|
* ```
|
|
135
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
|
+
*
|
|
136
186
|
* ### Internet Explorer
|
|
137
187
|
*
|
|
138
188
|
* Additional configuration params can be used from [IE options](https://seleniumhq.github.io/selenium/docs/api/rb/Selenium/WebDriver/IE/Options.html)
|
|
@@ -415,7 +465,6 @@ class WebDriver extends Helper {
|
|
|
415
465
|
_validateConfig(config) {
|
|
416
466
|
const defaults = {
|
|
417
467
|
logLevel: 'silent',
|
|
418
|
-
path: '/wd/hub',
|
|
419
468
|
// codeceptjs
|
|
420
469
|
remoteFileUpload: true,
|
|
421
470
|
smartWait: 0,
|
|
@@ -435,12 +484,17 @@ class WebDriver extends Helper {
|
|
|
435
484
|
// override defaults with config
|
|
436
485
|
config = Object.assign(defaults, config);
|
|
437
486
|
|
|
438
|
-
if (
|
|
487
|
+
if (config.host) {
|
|
488
|
+
// webdriverio spec
|
|
489
|
+
config.hostname = config.host;
|
|
490
|
+
config.path = '/wd/hub';
|
|
491
|
+
}
|
|
439
492
|
config.baseUrl = config.url || config.baseUrl;
|
|
440
493
|
if (config.desiredCapabilities && Object.keys(config.desiredCapabilities).length) {
|
|
441
494
|
config.capabilities = config.desiredCapabilities;
|
|
442
495
|
}
|
|
443
496
|
config.capabilities.browserName = config.browser || config.capabilities.browserName;
|
|
497
|
+
config.capabilities.browserVersion = config.browserVersion || config.capabilities.browserVersion;
|
|
444
498
|
if (config.capabilities.chromeOptions) {
|
|
445
499
|
config.capabilities['goog:chromeOptions'] = config.capabilities.chromeOptions;
|
|
446
500
|
delete config.capabilities.chromeOptions;
|
|
@@ -542,6 +596,10 @@ class WebDriver extends Helper {
|
|
|
542
596
|
delete this.options.capabilities.hostname;
|
|
543
597
|
delete this.options.capabilities.port;
|
|
544
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
|
+
}
|
|
545
603
|
this.browser = await webdriverio.remote(this.options);
|
|
546
604
|
}
|
|
547
605
|
} catch (err) {
|
|
@@ -1043,7 +1101,8 @@ class WebDriver extends Helper {
|
|
|
1043
1101
|
assertElementExists(res, field, 'Field');
|
|
1044
1102
|
const elem = usingFirstElement(res);
|
|
1045
1103
|
highlightActiveElement.call(this, elem);
|
|
1046
|
-
|
|
1104
|
+
await elem.clearValue();
|
|
1105
|
+
await elem.setValue(value.toString());
|
|
1047
1106
|
}
|
|
1048
1107
|
|
|
1049
1108
|
/**
|
|
@@ -1055,6 +1114,10 @@ class WebDriver extends Helper {
|
|
|
1055
1114
|
assertElementExists(res, field, 'Field');
|
|
1056
1115
|
const elem = usingFirstElement(res);
|
|
1057
1116
|
highlightActiveElement.call(this, elem);
|
|
1117
|
+
if (this.options.automationProtocol) {
|
|
1118
|
+
const curentValue = await elem.getValue();
|
|
1119
|
+
return elem.setValue(curentValue + value.toString());
|
|
1120
|
+
}
|
|
1058
1121
|
return elem.addValue(value.toString());
|
|
1059
1122
|
}
|
|
1060
1123
|
|
|
@@ -1067,6 +1130,9 @@ class WebDriver extends Helper {
|
|
|
1067
1130
|
assertElementExists(res, field, 'Field');
|
|
1068
1131
|
const elem = usingFirstElement(res);
|
|
1069
1132
|
highlightActiveElement.call(this, elem);
|
|
1133
|
+
if (this.options.automationProtocol) {
|
|
1134
|
+
return elem.setValue('');
|
|
1135
|
+
}
|
|
1070
1136
|
return elem.clearValue(getElementId(elem));
|
|
1071
1137
|
}
|
|
1072
1138
|
|
|
@@ -1104,8 +1170,9 @@ class WebDriver extends Helper {
|
|
|
1104
1170
|
}
|
|
1105
1171
|
|
|
1106
1172
|
/**
|
|
1107
|
-
* {{> attachFile }}
|
|
1108
1173
|
* Appium: not tested
|
|
1174
|
+
*
|
|
1175
|
+
* {{> attachFile }}
|
|
1109
1176
|
*/
|
|
1110
1177
|
async attachFile(locator, pathToFile) {
|
|
1111
1178
|
let file = path.join(global.codecept_dir, pathToFile);
|
|
@@ -1119,7 +1186,7 @@ class WebDriver extends Helper {
|
|
|
1119
1186
|
const el = usingFirstElement(res);
|
|
1120
1187
|
|
|
1121
1188
|
// Remote Upload (when running Selenium Server)
|
|
1122
|
-
if (this.options.remoteFileUpload) {
|
|
1189
|
+
if (this.options.remoteFileUpload && !this.options.automationProtocol) {
|
|
1123
1190
|
try {
|
|
1124
1191
|
this.debugSection('File', 'Uploading file to remote server');
|
|
1125
1192
|
file = await this.browser.uploadFile(file);
|
|
@@ -1132,8 +1199,8 @@ class WebDriver extends Helper {
|
|
|
1132
1199
|
}
|
|
1133
1200
|
|
|
1134
1201
|
/**
|
|
1135
|
-
* {{> checkOption }}
|
|
1136
1202
|
* Appium: not tested
|
|
1203
|
+
* {{> checkOption }}
|
|
1137
1204
|
*/
|
|
1138
1205
|
async checkOption(field, context = null) {
|
|
1139
1206
|
const clickMethod = this.browser.isMobile && !this.browser.isW3C ? 'touchClick' : 'elementClick';
|
|
@@ -1152,8 +1219,8 @@ class WebDriver extends Helper {
|
|
|
1152
1219
|
}
|
|
1153
1220
|
|
|
1154
1221
|
/**
|
|
1155
|
-
* {{> uncheckOption }}
|
|
1156
1222
|
* Appium: not tested
|
|
1223
|
+
* {{> uncheckOption }}
|
|
1157
1224
|
*/
|
|
1158
1225
|
async uncheckOption(field, context = null) {
|
|
1159
1226
|
const clickMethod = this.browser.isMobile && !this.browser.isW3C ? 'touchClick' : 'elementClick';
|
|
@@ -1370,16 +1437,16 @@ class WebDriver extends Helper {
|
|
|
1370
1437
|
}
|
|
1371
1438
|
|
|
1372
1439
|
/**
|
|
1373
|
-
* {{> seeCheckboxIsChecked }}
|
|
1374
1440
|
* Appium: not tested
|
|
1441
|
+
* {{> seeCheckboxIsChecked }}
|
|
1375
1442
|
*/
|
|
1376
1443
|
async seeCheckboxIsChecked(field) {
|
|
1377
1444
|
return proceedSeeCheckbox.call(this, 'assert', field);
|
|
1378
1445
|
}
|
|
1379
1446
|
|
|
1380
1447
|
/**
|
|
1381
|
-
* {{> dontSeeCheckboxIsChecked }}
|
|
1382
1448
|
* Appium: not tested
|
|
1449
|
+
* {{> dontSeeCheckboxIsChecked }}
|
|
1383
1450
|
*/
|
|
1384
1451
|
async dontSeeCheckboxIsChecked(field) {
|
|
1385
1452
|
return proceedSeeCheckbox.call(this, 'negate', field);
|
|
@@ -1497,35 +1564,33 @@ class WebDriver extends Helper {
|
|
|
1497
1564
|
async seeCssPropertiesOnElements(locator, cssProperties) {
|
|
1498
1565
|
const res = await this._locate(locator);
|
|
1499
1566
|
assertElementExists(res, locator);
|
|
1567
|
+
|
|
1568
|
+
const cssPropertiesCamelCase = convertCssPropertiesToCamelCase(cssProperties);
|
|
1500
1569
|
const elemAmount = res.length;
|
|
1570
|
+
let props = [];
|
|
1501
1571
|
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
const
|
|
1505
|
-
if (isColorProperty(prop)
|
|
1506
|
-
|
|
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);
|
|
1507
1579
|
}
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
});
|
|
1511
|
-
|
|
1512
|
-
const cssPropertiesCamelCase = convertCssPropertiesToCamelCase(cssProperties);
|
|
1580
|
+
}
|
|
1581
|
+
}
|
|
1513
1582
|
|
|
1514
1583
|
const values = Object.keys(cssPropertiesCamelCase).map(key => cssPropertiesCamelCase[key]);
|
|
1515
1584
|
if (!Array.isArray(props)) props = [props];
|
|
1516
1585
|
let chunked = chunkArray(props, values.length);
|
|
1517
1586
|
chunked = chunked.filter((val) => {
|
|
1518
1587
|
for (let i = 0; i < val.length; ++i) {
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
if (_acutal !== _expected) return false;
|
|
1588
|
+
// eslint-disable-next-line eqeqeq
|
|
1589
|
+
if (val[i] != values[i]) return false;
|
|
1522
1590
|
}
|
|
1523
1591
|
return true;
|
|
1524
1592
|
});
|
|
1525
|
-
return assert.
|
|
1526
|
-
chunked.length === elemAmount,
|
|
1527
|
-
`expected all elements (${(new Locator(locator))}) to have CSS property ${JSON.stringify(cssProperties)}`,
|
|
1528
|
-
);
|
|
1593
|
+
return equals(`all elements (${(new Locator(locator))}) to have CSS property ${JSON.stringify(cssProperties)}`).assert(chunked.length, elemAmount);
|
|
1529
1594
|
}
|
|
1530
1595
|
|
|
1531
1596
|
/**
|
|
@@ -1545,9 +1610,9 @@ class WebDriver extends Helper {
|
|
|
1545
1610
|
let chunked = chunkArray(attrs, values.length);
|
|
1546
1611
|
chunked = chunked.filter((val) => {
|
|
1547
1612
|
for (let i = 0; i < val.length; ++i) {
|
|
1548
|
-
const
|
|
1613
|
+
const _actual = Number.isNaN(val[i]) || (typeof values[i]) === 'string' ? val[i] : Number.parseInt(val[i], 10);
|
|
1549
1614
|
const _expected = Number.isNaN(values[i]) || (typeof values[i]) === 'string' ? values[i] : Number.parseInt(values[i], 10);
|
|
1550
|
-
if (
|
|
1615
|
+
if (_actual !== _expected) return false;
|
|
1551
1616
|
}
|
|
1552
1617
|
return true;
|
|
1553
1618
|
});
|
|
@@ -1606,10 +1671,9 @@ class WebDriver extends Helper {
|
|
|
1606
1671
|
}
|
|
1607
1672
|
|
|
1608
1673
|
/**
|
|
1609
|
-
* {{> executeScript }}
|
|
1610
|
-
*
|
|
1611
|
-
*
|
|
1612
1674
|
* Wraps [execute](http://webdriver.io/api/protocol/execute.html) command.
|
|
1675
|
+
*
|
|
1676
|
+
* {{> executeScript }}
|
|
1613
1677
|
*/
|
|
1614
1678
|
executeScript(...args) {
|
|
1615
1679
|
return this.browser.execute.apply(this.browser, args);
|
|
@@ -1672,7 +1736,11 @@ class WebDriver extends Helper {
|
|
|
1672
1736
|
const res = await this._locate(withStrictLocator(locator), true);
|
|
1673
1737
|
assertElementExists(res, locator);
|
|
1674
1738
|
const elem = usingFirstElement(res);
|
|
1675
|
-
|
|
1739
|
+
try {
|
|
1740
|
+
await elem.moveTo({ xOffset, yOffset });
|
|
1741
|
+
} catch (e) {
|
|
1742
|
+
debug(e.message);
|
|
1743
|
+
}
|
|
1676
1744
|
}
|
|
1677
1745
|
|
|
1678
1746
|
/**
|
|
@@ -1731,11 +1799,8 @@ class WebDriver extends Helper {
|
|
|
1731
1799
|
}
|
|
1732
1800
|
|
|
1733
1801
|
/**
|
|
1802
|
+
* Uses Selenium's JSON [cookie format](https://code.google.com/p/selenium/wiki/JsonWireProtocol#Cookie_JSON_Object).
|
|
1734
1803
|
* {{> setCookie }}
|
|
1735
|
-
*
|
|
1736
|
-
*
|
|
1737
|
-
* Uses Selenium's JSON [cookie
|
|
1738
|
-
* format](https://code.google.com/p/selenium/wiki/JsonWireProtocol#Cookie_JSON_Object).
|
|
1739
1804
|
*/
|
|
1740
1805
|
async setCookie(cookie) {
|
|
1741
1806
|
return this.browser.setCookies(cookie);
|
|
@@ -1788,7 +1853,7 @@ class WebDriver extends Helper {
|
|
|
1788
1853
|
}
|
|
1789
1854
|
|
|
1790
1855
|
/**
|
|
1791
|
-
* 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`.
|
|
1792
1857
|
*
|
|
1793
1858
|
*/
|
|
1794
1859
|
async cancelPopup() {
|
|
@@ -1862,9 +1927,9 @@ class WebDriver extends Helper {
|
|
|
1862
1927
|
}
|
|
1863
1928
|
|
|
1864
1929
|
/**
|
|
1865
|
-
* {{> pressKeyWithKeyNormalization }}
|
|
1866
|
-
*
|
|
1867
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 }}
|
|
1868
1933
|
*/
|
|
1869
1934
|
async pressKey(key) {
|
|
1870
1935
|
const modifiers = [];
|
|
@@ -1923,11 +1988,12 @@ class WebDriver extends Helper {
|
|
|
1923
1988
|
}
|
|
1924
1989
|
|
|
1925
1990
|
/**
|
|
1926
|
-
* {{> resizeWindow }}
|
|
1927
1991
|
* Appium: not tested in web, in apps doesn't work
|
|
1992
|
+
*
|
|
1993
|
+
* {{> resizeWindow }}
|
|
1928
1994
|
*/
|
|
1929
1995
|
async resizeWindow(width, height) {
|
|
1930
|
-
return this.
|
|
1996
|
+
return this.browser.setWindowSize(width, height);
|
|
1931
1997
|
}
|
|
1932
1998
|
|
|
1933
1999
|
async _resizeBrowserWindow(browser, width, height) {
|
|
@@ -1976,8 +2042,8 @@ class WebDriver extends Helper {
|
|
|
1976
2042
|
}
|
|
1977
2043
|
|
|
1978
2044
|
/**
|
|
1979
|
-
* {{> dragAndDrop }}
|
|
1980
2045
|
* Appium: not tested
|
|
2046
|
+
* {{> dragAndDrop }}
|
|
1981
2047
|
*/
|
|
1982
2048
|
async dragAndDrop(srcElement, destElement) {
|
|
1983
2049
|
let sourceEl = await this._locate(srcElement);
|
|
@@ -2308,12 +2374,33 @@ class WebDriver extends Helper {
|
|
|
2308
2374
|
return this.browser.waitUntil(async () => this.browser.execute(fn, ...args), { timeout: aSec * 1000, timeoutMsg: '' });
|
|
2309
2375
|
}
|
|
2310
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
|
+
|
|
2311
2395
|
/**
|
|
2312
2396
|
* {{> switchTo }}
|
|
2313
2397
|
*/
|
|
2314
2398
|
async switchTo(locator) {
|
|
2315
2399
|
this.browser.isInsideFrame = true;
|
|
2316
2400
|
if (Number.isInteger(locator)) {
|
|
2401
|
+
if (this.options.automationProtocol) {
|
|
2402
|
+
return this.browser.switchToFrame(locator + 1);
|
|
2403
|
+
}
|
|
2317
2404
|
return this.browser.switchToFrame(locator);
|
|
2318
2405
|
}
|
|
2319
2406
|
if (!locator) {
|
|
@@ -2450,21 +2537,41 @@ class WebDriver extends Helper {
|
|
|
2450
2537
|
}
|
|
2451
2538
|
|
|
2452
2539
|
/**
|
|
2540
|
+
* This method is **deprecated**.
|
|
2541
|
+
*
|
|
2542
|
+
*
|
|
2453
2543
|
* {{> setGeoLocation }}
|
|
2454
2544
|
*/
|
|
2455
|
-
async setGeoLocation(latitude, longitude
|
|
2456
|
-
if (
|
|
2457
|
-
|
|
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;
|
|
2458
2551
|
}
|
|
2459
|
-
|
|
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
|
+
});
|
|
2460
2558
|
}
|
|
2461
2559
|
|
|
2462
2560
|
/**
|
|
2561
|
+
* This method is **deprecated**.
|
|
2562
|
+
*
|
|
2463
2563
|
* {{> grabGeoLocation }}
|
|
2464
2564
|
*
|
|
2465
2565
|
*/
|
|
2466
2566
|
async grabGeoLocation() {
|
|
2467
|
-
|
|
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;
|
|
2468
2575
|
}
|
|
2469
2576
|
|
|
2470
2577
|
/**
|
|
@@ -2660,7 +2767,7 @@ async function proceedSeeField(assertType, field, value) {
|
|
|
2660
2767
|
}
|
|
2661
2768
|
};
|
|
2662
2769
|
|
|
2663
|
-
const proceedSingle = el =>
|
|
2770
|
+
const proceedSingle = el => el.getValue().then((res) => {
|
|
2664
2771
|
if (res === null) {
|
|
2665
2772
|
throw new Error(`Element ${el.selector} has no value attribute`);
|
|
2666
2773
|
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
async function findReact(matcher, locator) {
|
|
2
|
+
let _locator = `_react=${locator.react}`;
|
|
3
|
+
let props = '';
|
|
4
|
+
|
|
5
|
+
if (locator.props) {
|
|
6
|
+
props += propBuilder(locator.props);
|
|
7
|
+
_locator += props;
|
|
8
|
+
}
|
|
9
|
+
return matcher.locator(_locator).all();
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
async function findVue(matcher, locator) {
|
|
13
|
+
let _locator = `_vue=${locator.vue}`;
|
|
14
|
+
let props = '';
|
|
15
|
+
|
|
16
|
+
if (locator.props) {
|
|
17
|
+
props += propBuilder(locator.props);
|
|
18
|
+
_locator += props;
|
|
19
|
+
}
|
|
20
|
+
return matcher.locator(_locator).all();
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function propBuilder(props) {
|
|
24
|
+
let _props = '';
|
|
25
|
+
|
|
26
|
+
for (const [key, value] of Object.entries(props)) {
|
|
27
|
+
if (typeof value === 'object') {
|
|
28
|
+
for (const [k, v] of Object.entries(value)) {
|
|
29
|
+
_props += `[${key}.${k} = "${v}"]`;
|
|
30
|
+
}
|
|
31
|
+
} else {
|
|
32
|
+
_props += `[${key} = "${value}"]`;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return _props;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
module.exports = { findReact, findVue };
|
package/lib/locator.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const cssToXPath = require('
|
|
1
|
+
const cssToXPath = require('csstoxpath');
|
|
2
2
|
const { sprintf } = require('sprintf-js');
|
|
3
3
|
|
|
4
4
|
const { xpathLocator } = require('./utils');
|
|
@@ -158,11 +158,12 @@ class Locator {
|
|
|
158
158
|
}
|
|
159
159
|
|
|
160
160
|
/**
|
|
161
|
+
* @param {string} [pseudoSelector] CSS to XPath extension pseudo: https://www.npmjs.com/package/csstoxpath?activeTab=explore#extension-pseudos
|
|
161
162
|
* @returns {string}
|
|
162
163
|
*/
|
|
163
|
-
toXPath() {
|
|
164
|
+
toXPath(pseudoSelector = '') {
|
|
164
165
|
if (this.isXPath()) return this.value;
|
|
165
|
-
if (this.isCSS()) return cssToXPath
|
|
166
|
+
if (this.isCSS()) return cssToXPath(`${this.value}${pseudoSelector}`);
|
|
166
167
|
|
|
167
168
|
throw new Error('Can\'t be converted to XPath');
|
|
168
169
|
}
|
|
@@ -243,12 +244,24 @@ class Locator {
|
|
|
243
244
|
}
|
|
244
245
|
|
|
245
246
|
/**
|
|
247
|
+
* Find an element containing a text
|
|
246
248
|
* @param {string} text
|
|
247
249
|
* @returns {Locator}
|
|
248
250
|
*/
|
|
249
251
|
withText(text) {
|
|
250
252
|
text = xpathLocator.literal(text);
|
|
251
|
-
const xpath =
|
|
253
|
+
const xpath = this.toXPath(`:text-contains-case(${text})`);
|
|
254
|
+
return new Locator({ xpath });
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* Find an element with exact text
|
|
259
|
+
* @param {string} text
|
|
260
|
+
* @returns {Locator}
|
|
261
|
+
*/
|
|
262
|
+
withTextEquals(text) {
|
|
263
|
+
text = xpathLocator.literal(text);
|
|
264
|
+
const xpath = this.toXPath(`:text-case(${text})`);
|
|
252
265
|
return new Locator({ xpath });
|
|
253
266
|
}
|
|
254
267
|
|