codeceptjs 2.4.3 → 2.6.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +117 -0
- package/README.md +32 -7
- package/bin/codecept.js +3 -0
- package/docs/basics.md +11 -5
- package/docs/bdd.md +4 -4
- package/docs/build/MockRequest.js +3 -0
- package/docs/build/Nightmare.js +10 -2
- package/docs/build/Playwright.js +3187 -0
- package/docs/build/Protractor.js +16 -2
- package/docs/build/Puppeteer.js +126 -19
- package/docs/build/REST.js +3 -1
- package/docs/build/TestCafe.js +11 -3
- package/docs/build/WebDriver.js +361 -28
- package/docs/changelog.md +116 -0
- package/docs/configuration.md +2 -2
- package/docs/custom-helpers.md +55 -10
- package/docs/helpers/Appium.md +81 -1
- package/docs/helpers/MockRequest.md +281 -38
- package/docs/helpers/Nightmare.md +10 -2
- package/docs/helpers/Playwright.md +1770 -0
- package/docs/helpers/Protractor.md +15 -3
- package/docs/helpers/Puppeteer-firefox.md +32 -1
- package/docs/helpers/Puppeteer.md +126 -76
- package/docs/helpers/TestCafe.md +10 -2
- package/docs/helpers/WebDriver.md +208 -118
- package/docs/locators.md +2 -0
- package/docs/playwright.md +306 -0
- package/docs/plugins.md +103 -0
- package/docs/reports.md +12 -0
- package/docs/shadow.md +68 -0
- package/docs/visual.md +0 -73
- package/docs/webapi/forceClick.mustache +27 -0
- package/docs/webapi/seeInPopup.mustache +7 -0
- package/docs/webapi/setCookie.mustache +10 -2
- package/docs/webapi/type.mustache +12 -0
- package/docs/webdriver.md +7 -3
- package/lib/codecept.js +1 -1
- package/lib/command/definitions.js +2 -2
- package/lib/command/generate.js +4 -4
- package/lib/command/gherkin/snippets.js +4 -4
- package/lib/command/init.js +1 -1
- package/lib/command/interactive.js +3 -0
- package/lib/command/run-multiple.js +2 -2
- package/lib/command/run-rerun.js +2 -0
- package/lib/command/run-workers.js +22 -8
- package/lib/command/run.js +2 -0
- package/lib/command/workers/runTests.js +1 -0
- package/lib/container.js +1 -1
- package/lib/event.js +2 -0
- package/lib/helper/MockRequest.js +3 -0
- package/lib/helper/Playwright.js +2422 -0
- package/lib/helper/Protractor.js +1 -2
- package/lib/helper/Puppeteer.js +84 -19
- package/lib/helper/REST.js +3 -1
- package/lib/helper/TestCafe.js +1 -1
- package/lib/helper/WebDriver.js +313 -26
- package/lib/helper/extras/PlaywrightPropEngine.js +53 -0
- package/lib/helper/scripts/isElementClickable.js +54 -14
- package/lib/interfaces/gherkin.js +1 -1
- package/lib/listener/helpers.js +3 -0
- package/lib/locator.js +5 -0
- package/lib/mochaFactory.js +12 -10
- package/lib/plugin/allure.js +8 -1
- package/lib/plugin/autoDelay.js +1 -8
- package/lib/plugin/commentStep.js +133 -0
- package/lib/plugin/screenshotOnFail.js +3 -10
- package/lib/plugin/selenoid.js +2 -2
- package/lib/plugin/standardActingHelpers.js +13 -0
- package/lib/plugin/stepByStepReport.js +1 -8
- package/lib/plugin/wdio.js +10 -1
- package/lib/reporter/cli.js +30 -1
- package/lib/session.js +7 -4
- package/package.json +13 -10
- package/typings/Mocha.d.ts +567 -16
- package/typings/index.d.ts +9 -5
- package/typings/types.d.ts +1634 -74
package/lib/helper/WebDriver.js
CHANGED
|
@@ -2,6 +2,7 @@ let webdriverio;
|
|
|
2
2
|
|
|
3
3
|
const assert = require('assert');
|
|
4
4
|
const path = require('path');
|
|
5
|
+
const fs = require('fs');
|
|
5
6
|
const requireg = require('requireg');
|
|
6
7
|
|
|
7
8
|
const Helper = require('../helper');
|
|
@@ -28,8 +29,10 @@ const ElementNotFound = require('./errors/ElementNotFound');
|
|
|
28
29
|
const ConnectionRefused = require('./errors/ConnectionRefused');
|
|
29
30
|
const Locator = require('../locator');
|
|
30
31
|
|
|
32
|
+
const SHADOW = 'shadow';
|
|
31
33
|
const webRoot = 'body';
|
|
32
34
|
|
|
35
|
+
let version;
|
|
33
36
|
/**
|
|
34
37
|
* WebDriver helper which wraps [webdriverio](http://webdriver.io/) library to
|
|
35
38
|
* manipulate browser using Selenium WebDriver or PhantomJS.
|
|
@@ -380,10 +383,22 @@ class WebDriver extends Helper {
|
|
|
380
383
|
if (webdriverio.VERSION && webdriverio.VERSION.indexOf('4') === 0) {
|
|
381
384
|
throw new Error(`This helper is compatible with "webdriverio@5". Current version: ${webdriverio.VERSION}. Please upgrade webdriverio to v5+ or use WebDriverIO helper instead`);
|
|
382
385
|
}
|
|
386
|
+
try {
|
|
387
|
+
version = JSON.parse(fs.readFileSync(path.join(requireg.resolve('webdriverio'), '/../../', 'package.json')).toString()).version;
|
|
388
|
+
} catch (err) {
|
|
389
|
+
this.debug('Can\'t detect webdriverio version, assuming webdriverio v6 is used');
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
if (isWebDriver5()) {
|
|
393
|
+
console.log('DEPRECATION NOTICE:');
|
|
394
|
+
console.log('You are using webdriverio v5. It is recommended to update to webdriverio@6.\nSupport of webdriverio v5 is deprecated and will be removed in CodeceptJS 3.0\n');
|
|
395
|
+
}
|
|
383
396
|
// set defaults
|
|
384
397
|
this.root = webRoot;
|
|
385
398
|
this.isWeb = true;
|
|
386
399
|
this.isRunning = false;
|
|
400
|
+
this.sessionWindows = {};
|
|
401
|
+
this.activeSessionName = '';
|
|
387
402
|
|
|
388
403
|
this._setConfig(config);
|
|
389
404
|
|
|
@@ -402,6 +417,7 @@ class WebDriver extends Helper {
|
|
|
402
417
|
_validateConfig(config) {
|
|
403
418
|
const defaults = {
|
|
404
419
|
logLevel: 'silent',
|
|
420
|
+
path: '/wd/hub',
|
|
405
421
|
// codeceptjs
|
|
406
422
|
remoteFileUpload: true,
|
|
407
423
|
smartWait: 0,
|
|
@@ -500,6 +516,11 @@ class WebDriver extends Helper {
|
|
|
500
516
|
if (this.options.multiremote) {
|
|
501
517
|
this.browser = await webdriverio.multiremote(this.options.multiremote);
|
|
502
518
|
} else {
|
|
519
|
+
// remove non w3c capabilities
|
|
520
|
+
delete this.options.capabilities.protocol;
|
|
521
|
+
delete this.options.capabilities.hostname;
|
|
522
|
+
delete this.options.capabilities.port;
|
|
523
|
+
delete this.options.capabilities.path;
|
|
503
524
|
this.browser = await webdriverio.remote(this.options);
|
|
504
525
|
}
|
|
505
526
|
} catch (err) {
|
|
@@ -563,12 +584,12 @@ class WebDriver extends Helper {
|
|
|
563
584
|
_session() {
|
|
564
585
|
const defaultSession = this.browser;
|
|
565
586
|
return {
|
|
566
|
-
start: async (opts) => {
|
|
587
|
+
start: async (sessionName, opts) => {
|
|
567
588
|
// opts.disableScreenshots = true; // screenshots cant be saved as session will be already closed
|
|
568
589
|
opts = this._validateConfig(Object.assign(this.options, opts));
|
|
569
590
|
this.debugSection('New Browser', JSON.stringify(opts));
|
|
570
591
|
const browser = await webdriverio.remote(opts);
|
|
571
|
-
|
|
592
|
+
this.activeSessionName = sessionName;
|
|
572
593
|
if (opts.timeouts && this.isWeb) {
|
|
573
594
|
await this._defineBrowserTimeout(browser, opts.timeouts);
|
|
574
595
|
}
|
|
@@ -578,14 +599,19 @@ class WebDriver extends Helper {
|
|
|
578
599
|
return browser;
|
|
579
600
|
},
|
|
580
601
|
stop: async (browser) => {
|
|
602
|
+
if (!browser) return;
|
|
581
603
|
return browser.deleteSession();
|
|
582
604
|
},
|
|
583
605
|
loadVars: async (browser) => {
|
|
584
606
|
if (this.context !== this.root) throw new Error('Can\'t start session inside within block');
|
|
585
607
|
this.browser = browser;
|
|
586
608
|
this.$$ = this.browser.$$.bind(this.browser);
|
|
609
|
+
this.sessionWindows[this.activeSessionName] = browser;
|
|
587
610
|
},
|
|
588
|
-
restoreVars: async () => {
|
|
611
|
+
restoreVars: async (session) => {
|
|
612
|
+
if (!session) {
|
|
613
|
+
this.activeSessionName = '';
|
|
614
|
+
}
|
|
589
615
|
this.browser = defaultSession;
|
|
590
616
|
this.$$ = this.browser.$$.bind(this.browser);
|
|
591
617
|
},
|
|
@@ -626,6 +652,62 @@ class WebDriver extends Helper {
|
|
|
626
652
|
this.$$ = this.browser.$$.bind(this.browser);
|
|
627
653
|
}
|
|
628
654
|
|
|
655
|
+
/**
|
|
656
|
+
* Check if locator is type of "Shadow"
|
|
657
|
+
*
|
|
658
|
+
* @param {object} locator
|
|
659
|
+
*/
|
|
660
|
+
_isShadowLocator(locator) {
|
|
661
|
+
return locator.type === SHADOW || locator[SHADOW];
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
/**
|
|
665
|
+
* Locate Element within the Shadow Dom
|
|
666
|
+
*
|
|
667
|
+
* @param {object} locator
|
|
668
|
+
*/
|
|
669
|
+
async _locateShadow(locator) {
|
|
670
|
+
const shadow = locator.value ? locator.value : locator[SHADOW];
|
|
671
|
+
const shadowSequence = [];
|
|
672
|
+
let elements;
|
|
673
|
+
|
|
674
|
+
if (!Array.isArray(shadow)) {
|
|
675
|
+
throw new Error(`Shadow '${shadow}' should be defined as an Array of elements.`);
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
// traverse through the Shadow locators in sequence
|
|
679
|
+
for (let index = 0; index < shadow.length; index++) {
|
|
680
|
+
const shadowElement = shadow[index];
|
|
681
|
+
shadowSequence.push(shadowElement);
|
|
682
|
+
|
|
683
|
+
if (!elements) {
|
|
684
|
+
elements = await (this.browser.$$(shadowElement));
|
|
685
|
+
} else if (Array.isArray(elements)) {
|
|
686
|
+
elements = await elements[0].shadow$$(shadowElement);
|
|
687
|
+
} else if (elements) {
|
|
688
|
+
elements = await elements.shadow$$(shadowElement);
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
if (!elements || !elements[0]) {
|
|
692
|
+
throw new Error(`Shadow Element '${shadowElement}' is not found. It is possible the element is incorrect or elements sequence is incorrect. Please verify the sequence '${shadowSequence.join('>')}' is correctly chained.`);
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
this.debugSection('Elements', `Found ${elements.length} '${SHADOW}' elements`);
|
|
697
|
+
|
|
698
|
+
return elements;
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
/**
|
|
702
|
+
* Smart Wait to locate an element
|
|
703
|
+
*
|
|
704
|
+
* @param {object} locator
|
|
705
|
+
*/
|
|
706
|
+
async _smartWait(locator) {
|
|
707
|
+
this.debugSection(`SmartWait (${this.options.smartWait}ms)`, `Locating ${locator} in ${this.options.smartWait}`);
|
|
708
|
+
await this.defineTimeout({ implicit: this.options.smartWait });
|
|
709
|
+
}
|
|
710
|
+
|
|
629
711
|
/**
|
|
630
712
|
* Get elements by different locator types, including strict locator.
|
|
631
713
|
* Should be used in custom helpers:
|
|
@@ -640,6 +722,18 @@ class WebDriver extends Helper {
|
|
|
640
722
|
async _locate(locator, smartWait = false) {
|
|
641
723
|
if (require('../store').debugMode) smartWait = false;
|
|
642
724
|
|
|
725
|
+
// special locator type for Shadow DOM
|
|
726
|
+
if (this._isShadowLocator(locator)) {
|
|
727
|
+
if (!this.options.smartWait || !smartWait) {
|
|
728
|
+
const els = await this._locateShadow(locator);
|
|
729
|
+
return els;
|
|
730
|
+
}
|
|
731
|
+
|
|
732
|
+
|
|
733
|
+
const els = await this._locateShadow(locator);
|
|
734
|
+
return els;
|
|
735
|
+
}
|
|
736
|
+
|
|
643
737
|
// special locator type for React
|
|
644
738
|
if (locator.react) {
|
|
645
739
|
const els = await this.browser.react$$(locator.react, locator.props || undefined, locator.state || undefined);
|
|
@@ -651,9 +745,9 @@ class WebDriver extends Helper {
|
|
|
651
745
|
const els = await this.$$(withStrictLocator(locator));
|
|
652
746
|
return els;
|
|
653
747
|
}
|
|
654
|
-
this.debugSection(`SmartWait (${this.options.smartWait}ms)`, `Locating ${locator} in ${this.options.smartWait}`);
|
|
655
748
|
|
|
656
|
-
await this.
|
|
749
|
+
await this._smartWait(locator);
|
|
750
|
+
|
|
657
751
|
const els = await this.$$(withStrictLocator(locator));
|
|
658
752
|
await this.defineTimeout({ implicit: 0 });
|
|
659
753
|
return els;
|
|
@@ -676,13 +770,15 @@ class WebDriver extends Helper {
|
|
|
676
770
|
* Find a clickable element by providing human readable text:
|
|
677
771
|
*
|
|
678
772
|
* ```js
|
|
679
|
-
* this.helpers
|
|
773
|
+
* const els = await this.helpers.WebDriver._locateClickable('Next page');
|
|
774
|
+
* const els = await this.helpers.WebDriver._locateClickable('Next page', '.pages');
|
|
680
775
|
* ```
|
|
681
776
|
*
|
|
682
777
|
* @param {CodeceptJS.LocatorOrString} locator element located by CSS|XPath|strict locator.
|
|
683
778
|
*/
|
|
684
|
-
async _locateClickable(locator) {
|
|
685
|
-
|
|
779
|
+
async _locateClickable(locator, context) {
|
|
780
|
+
const locateFn = prepareLocateFn.call(this, context);
|
|
781
|
+
return findClickable.call(this, locator, locateFn);
|
|
686
782
|
}
|
|
687
783
|
|
|
688
784
|
/**
|
|
@@ -753,6 +849,32 @@ class WebDriver extends Helper {
|
|
|
753
849
|
return this.browser[clickMethod](getElementId(elem));
|
|
754
850
|
}
|
|
755
851
|
|
|
852
|
+
/**
|
|
853
|
+
* {{> forceClick }}
|
|
854
|
+
*
|
|
855
|
+
* {{ react }}
|
|
856
|
+
*/
|
|
857
|
+
async forceClick(locator, context = null) {
|
|
858
|
+
const locateFn = prepareLocateFn.call(this, context);
|
|
859
|
+
|
|
860
|
+
const res = await findClickable.call(this, locator, locateFn);
|
|
861
|
+
if (context) {
|
|
862
|
+
assertElementExists(res, locator, 'Clickable element', `was not found inside element ${new Locator(context)}`);
|
|
863
|
+
} else {
|
|
864
|
+
assertElementExists(res, locator, 'Clickable element');
|
|
865
|
+
}
|
|
866
|
+
const elem = usingFirstElement(res);
|
|
867
|
+
|
|
868
|
+
return this.executeScript((el) => {
|
|
869
|
+
if (document.activeElement instanceof HTMLElement) {
|
|
870
|
+
document.activeElement.blur();
|
|
871
|
+
}
|
|
872
|
+
const event = document.createEvent('MouseEvent');
|
|
873
|
+
event.initEvent('click', true, true);
|
|
874
|
+
return el.dispatchEvent(event);
|
|
875
|
+
}, elem);
|
|
876
|
+
}
|
|
877
|
+
|
|
756
878
|
/**
|
|
757
879
|
* {{> doubleClick }}
|
|
758
880
|
*
|
|
@@ -1382,11 +1504,12 @@ class WebDriver extends Helper {
|
|
|
1382
1504
|
* {{> moveCursorTo }}
|
|
1383
1505
|
*
|
|
1384
1506
|
*/
|
|
1385
|
-
async moveCursorTo(locator,
|
|
1507
|
+
async moveCursorTo(locator, xOffset, yOffset) {
|
|
1386
1508
|
const res = await this._locate(withStrictLocator(locator), true);
|
|
1387
1509
|
assertElementExists(res, locator);
|
|
1388
1510
|
const elem = usingFirstElement(res);
|
|
1389
|
-
return elem.moveTo(
|
|
1511
|
+
if (isWebDriver5()) return elem.moveTo(xOffset, yOffset);
|
|
1512
|
+
return elem.moveTo({ xOffset, yOffset });
|
|
1390
1513
|
}
|
|
1391
1514
|
|
|
1392
1515
|
/**
|
|
@@ -1396,6 +1519,15 @@ class WebDriver extends Helper {
|
|
|
1396
1519
|
async saveScreenshot(fileName, fullPage = false) {
|
|
1397
1520
|
const outputFile = screenshotOutputFolder(fileName);
|
|
1398
1521
|
|
|
1522
|
+
if (this.activeSessionName) {
|
|
1523
|
+
const browser = this.sessionWindows[this.activeSessionName];
|
|
1524
|
+
|
|
1525
|
+
if (browser) {
|
|
1526
|
+
this.debug(`Screenshot of ${this.activeSessionName} session has been saved to ${outputFile}`);
|
|
1527
|
+
return browser.saveScreenshot(outputFile);
|
|
1528
|
+
}
|
|
1529
|
+
}
|
|
1530
|
+
|
|
1399
1531
|
if (!fullPage) {
|
|
1400
1532
|
this.debug(`Screenshot has been saved to ${outputFile}`);
|
|
1401
1533
|
return this.browser.saveScreenshot(outputFile);
|
|
@@ -1604,6 +1736,18 @@ class WebDriver extends Helper {
|
|
|
1604
1736
|
}
|
|
1605
1737
|
}
|
|
1606
1738
|
|
|
1739
|
+
/**
|
|
1740
|
+
* {{> type }}
|
|
1741
|
+
* Type out given array of keys or a string of text
|
|
1742
|
+
*/
|
|
1743
|
+
async type(keys) {
|
|
1744
|
+
if (Array.isArray(keys)) {
|
|
1745
|
+
await this.browser.keys(keys);
|
|
1746
|
+
return;
|
|
1747
|
+
}
|
|
1748
|
+
await this.browser.keys(keys.split(''));
|
|
1749
|
+
}
|
|
1750
|
+
|
|
1607
1751
|
/**
|
|
1608
1752
|
* {{> resizeWindow }}
|
|
1609
1753
|
* Appium: not tested in web, in apps doesn't work
|
|
@@ -1748,6 +1892,19 @@ class WebDriver extends Helper {
|
|
|
1748
1892
|
*/
|
|
1749
1893
|
async waitForEnabled(locator, sec = null) {
|
|
1750
1894
|
const aSec = sec || this.options.waitForTimeout;
|
|
1895
|
+
if (isWebDriver5()) {
|
|
1896
|
+
return this.browser.waitUntil(async () => {
|
|
1897
|
+
const res = await this.$$(withStrictLocator(locator));
|
|
1898
|
+
if (!res || res.length === 0) {
|
|
1899
|
+
return false;
|
|
1900
|
+
}
|
|
1901
|
+
const selected = await forEachAsync(res, async el => this.browser.isElementEnabled(getElementId(el)));
|
|
1902
|
+
if (Array.isArray(selected)) {
|
|
1903
|
+
return selected.filter(val => val === true).length > 0;
|
|
1904
|
+
}
|
|
1905
|
+
return selected;
|
|
1906
|
+
}, aSec * 1000, `element (${new Locator(locator)}) still not enabled after ${aSec} sec`);
|
|
1907
|
+
}
|
|
1751
1908
|
return this.browser.waitUntil(async () => {
|
|
1752
1909
|
const res = await this.$$(withStrictLocator(locator));
|
|
1753
1910
|
if (!res || res.length === 0) {
|
|
@@ -1758,7 +1915,10 @@ class WebDriver extends Helper {
|
|
|
1758
1915
|
return selected.filter(val => val === true).length > 0;
|
|
1759
1916
|
}
|
|
1760
1917
|
return selected;
|
|
1761
|
-
},
|
|
1918
|
+
}, {
|
|
1919
|
+
timeout: aSec * 1000,
|
|
1920
|
+
timeoutMsg: `element (${new Locator(locator)}) still not enabled after ${aSec} sec`,
|
|
1921
|
+
});
|
|
1762
1922
|
}
|
|
1763
1923
|
|
|
1764
1924
|
/**
|
|
@@ -1766,10 +1926,16 @@ class WebDriver extends Helper {
|
|
|
1766
1926
|
*/
|
|
1767
1927
|
async waitForElement(locator, sec = null) {
|
|
1768
1928
|
const aSec = sec || this.options.waitForTimeout;
|
|
1929
|
+
if (isWebDriver5()) {
|
|
1930
|
+
return this.browser.waitUntil(async () => {
|
|
1931
|
+
const res = await this.$$(withStrictLocator(locator));
|
|
1932
|
+
return res && res.length;
|
|
1933
|
+
}, aSec * 1000, `element (${locator}) still not present on page after ${aSec} sec`);
|
|
1934
|
+
}
|
|
1769
1935
|
return this.browser.waitUntil(async () => {
|
|
1770
1936
|
const res = await this.$$(withStrictLocator(locator));
|
|
1771
1937
|
return res && res.length;
|
|
1772
|
-
}, aSec * 1000, `element (${locator}) still not present on page after ${aSec} sec`);
|
|
1938
|
+
}, { timeout: aSec * 1000, timeoutMsg: `element (${locator}) still not present on page after ${aSec} sec` });
|
|
1773
1939
|
}
|
|
1774
1940
|
|
|
1775
1941
|
/**
|
|
@@ -1802,13 +1968,27 @@ class WebDriver extends Helper {
|
|
|
1802
1968
|
const client = this.browser;
|
|
1803
1969
|
const aSec = sec || this.options.waitForTimeout;
|
|
1804
1970
|
let currUrl = '';
|
|
1971
|
+
if (isWebDriver5()) {
|
|
1972
|
+
return client
|
|
1973
|
+
.waitUntil(function () {
|
|
1974
|
+
return this.getUrl().then((res) => {
|
|
1975
|
+
currUrl = decodeUrl(res);
|
|
1976
|
+
return currUrl.indexOf(urlPart) > -1;
|
|
1977
|
+
});
|
|
1978
|
+
}, aSec * 1000).catch((e) => {
|
|
1979
|
+
if (e.message.indexOf('timeout')) {
|
|
1980
|
+
throw new Error(`expected url to include ${urlPart}, but found ${currUrl}`);
|
|
1981
|
+
}
|
|
1982
|
+
throw e;
|
|
1983
|
+
});
|
|
1984
|
+
}
|
|
1805
1985
|
return client
|
|
1806
1986
|
.waitUntil(function () {
|
|
1807
1987
|
return this.getUrl().then((res) => {
|
|
1808
1988
|
currUrl = decodeUrl(res);
|
|
1809
1989
|
return currUrl.indexOf(urlPart) > -1;
|
|
1810
1990
|
});
|
|
1811
|
-
}, aSec * 1000).catch((e) => {
|
|
1991
|
+
}, { timeout: aSec * 1000 }).catch((e) => {
|
|
1812
1992
|
if (e.message.indexOf('timeout')) {
|
|
1813
1993
|
throw new Error(`expected url to include ${urlPart}, but found ${currUrl}`);
|
|
1814
1994
|
}
|
|
@@ -1846,6 +2026,21 @@ class WebDriver extends Helper {
|
|
|
1846
2026
|
async waitForText(text, sec = null, context = null) {
|
|
1847
2027
|
const aSec = sec || this.options.waitForTimeout;
|
|
1848
2028
|
const _context = context || this.root;
|
|
2029
|
+
if (isWebDriver5()) {
|
|
2030
|
+
return this.browser.waitUntil(
|
|
2031
|
+
async () => {
|
|
2032
|
+
const res = await this.$$(withStrictLocator.call(this, _context));
|
|
2033
|
+
if (!res || res.length === 0) return false;
|
|
2034
|
+
const selected = await forEachAsync(res, async el => this.browser.getElementText(getElementId(el)));
|
|
2035
|
+
if (Array.isArray(selected)) {
|
|
2036
|
+
return selected.filter(part => part.indexOf(text) >= 0).length > 0;
|
|
2037
|
+
}
|
|
2038
|
+
return selected.indexOf(text) >= 0;
|
|
2039
|
+
}, aSec * 1000,
|
|
2040
|
+
`element (${_context}) is not in DOM or there is no element(${_context}) with text "${text}" after ${aSec} sec`,
|
|
2041
|
+
);
|
|
2042
|
+
}
|
|
2043
|
+
|
|
1849
2044
|
return this.browser.waitUntil(
|
|
1850
2045
|
async () => {
|
|
1851
2046
|
const res = await this.$$(withStrictLocator.call(this, _context));
|
|
@@ -1855,8 +2050,10 @@ class WebDriver extends Helper {
|
|
|
1855
2050
|
return selected.filter(part => part.indexOf(text) >= 0).length > 0;
|
|
1856
2051
|
}
|
|
1857
2052
|
return selected.indexOf(text) >= 0;
|
|
1858
|
-
},
|
|
1859
|
-
|
|
2053
|
+
}, {
|
|
2054
|
+
timeout: aSec * 1000,
|
|
2055
|
+
timeoutMsg: `element (${_context}) is not in DOM or there is no element(${_context}) with text "${text}" after ${aSec} sec`,
|
|
2056
|
+
},
|
|
1860
2057
|
);
|
|
1861
2058
|
}
|
|
1862
2059
|
|
|
@@ -1866,6 +2063,20 @@ class WebDriver extends Helper {
|
|
|
1866
2063
|
async waitForValue(field, value, sec = null) {
|
|
1867
2064
|
const client = this.browser;
|
|
1868
2065
|
const aSec = sec || this.options.waitForTimeout;
|
|
2066
|
+
if (isWebDriver5()) {
|
|
2067
|
+
return client.waitUntil(
|
|
2068
|
+
async () => {
|
|
2069
|
+
const res = await findFields.call(this, field);
|
|
2070
|
+
if (!res || res.length === 0) return false;
|
|
2071
|
+
const selected = await forEachAsync(res, async el => el.getValue());
|
|
2072
|
+
if (Array.isArray(selected)) {
|
|
2073
|
+
return selected.filter(part => part.indexOf(value) >= 0).length > 0;
|
|
2074
|
+
}
|
|
2075
|
+
return selected.indexOf(value) >= 0;
|
|
2076
|
+
}, aSec * 1000,
|
|
2077
|
+
`element (${field}) is not in DOM or there is no element(${field}) with value "${value}" after ${aSec} sec`,
|
|
2078
|
+
);
|
|
2079
|
+
}
|
|
1869
2080
|
return client.waitUntil(
|
|
1870
2081
|
async () => {
|
|
1871
2082
|
const res = await findFields.call(this, field);
|
|
@@ -1875,8 +2086,10 @@ class WebDriver extends Helper {
|
|
|
1875
2086
|
return selected.filter(part => part.indexOf(value) >= 0).length > 0;
|
|
1876
2087
|
}
|
|
1877
2088
|
return selected.indexOf(value) >= 0;
|
|
1878
|
-
},
|
|
1879
|
-
|
|
2089
|
+
}, {
|
|
2090
|
+
timeout: aSec * 1000,
|
|
2091
|
+
timeoutMsg: `element (${field}) is not in DOM or there is no element(${field}) with value "${value}" after ${aSec} sec`,
|
|
2092
|
+
},
|
|
1880
2093
|
);
|
|
1881
2094
|
}
|
|
1882
2095
|
|
|
@@ -1886,6 +2099,17 @@ class WebDriver extends Helper {
|
|
|
1886
2099
|
*/
|
|
1887
2100
|
async waitForVisible(locator, sec = null) {
|
|
1888
2101
|
const aSec = sec || this.options.waitForTimeout;
|
|
2102
|
+
if (isWebDriver5()) {
|
|
2103
|
+
return this.browser.waitUntil(async () => {
|
|
2104
|
+
const res = await this.$$(withStrictLocator(locator));
|
|
2105
|
+
if (!res || res.length === 0) return false;
|
|
2106
|
+
const selected = await forEachAsync(res, async el => el.isDisplayed());
|
|
2107
|
+
if (Array.isArray(selected)) {
|
|
2108
|
+
return selected.filter(val => val === true).length > 0;
|
|
2109
|
+
}
|
|
2110
|
+
return selected;
|
|
2111
|
+
}, aSec * 1000, `element (${new Locator(locator)}) still not visible after ${aSec} sec`);
|
|
2112
|
+
}
|
|
1889
2113
|
return this.browser.waitUntil(async () => {
|
|
1890
2114
|
const res = await this.$$(withStrictLocator(locator));
|
|
1891
2115
|
if (!res || res.length === 0) return false;
|
|
@@ -1894,7 +2118,7 @@ class WebDriver extends Helper {
|
|
|
1894
2118
|
return selected.filter(val => val === true).length > 0;
|
|
1895
2119
|
}
|
|
1896
2120
|
return selected;
|
|
1897
|
-
}, aSec * 1000, `element (${new Locator(locator)}) still not visible after ${aSec} sec`);
|
|
2121
|
+
}, { timeout: aSec * 1000, timeoutMsg: `element (${new Locator(locator)}) still not visible after ${aSec} sec` });
|
|
1898
2122
|
}
|
|
1899
2123
|
|
|
1900
2124
|
/**
|
|
@@ -1902,6 +2126,16 @@ class WebDriver extends Helper {
|
|
|
1902
2126
|
*/
|
|
1903
2127
|
async waitNumberOfVisibleElements(locator, num, sec = null) {
|
|
1904
2128
|
const aSec = sec || this.options.waitForTimeout;
|
|
2129
|
+
if (isWebDriver5()) {
|
|
2130
|
+
return this.browser.waitUntil(async () => {
|
|
2131
|
+
const res = await this.$$(withStrictLocator(locator));
|
|
2132
|
+
if (!res || res.length === 0) return false;
|
|
2133
|
+
let selected = await forEachAsync(res, async el => el.isDisplayed());
|
|
2134
|
+
|
|
2135
|
+
if (!Array.isArray(selected)) selected = [selected];
|
|
2136
|
+
return selected.length === num;
|
|
2137
|
+
}, aSec * 1000, `The number of elements (${new Locator(locator)}) is not ${num} after ${aSec} sec`);
|
|
2138
|
+
}
|
|
1905
2139
|
return this.browser.waitUntil(async () => {
|
|
1906
2140
|
const res = await this.$$(withStrictLocator(locator));
|
|
1907
2141
|
if (!res || res.length === 0) return false;
|
|
@@ -1909,7 +2143,7 @@ class WebDriver extends Helper {
|
|
|
1909
2143
|
|
|
1910
2144
|
if (!Array.isArray(selected)) selected = [selected];
|
|
1911
2145
|
return selected.length === num;
|
|
1912
|
-
}, aSec * 1000, `The number of elements (${new Locator(locator)}) is not ${num} after ${aSec} sec`);
|
|
2146
|
+
}, { timeout: aSec * 1000, timeoutMsg: `The number of elements (${new Locator(locator)}) is not ${num} after ${aSec} sec` });
|
|
1913
2147
|
}
|
|
1914
2148
|
|
|
1915
2149
|
/**
|
|
@@ -1918,12 +2152,20 @@ class WebDriver extends Helper {
|
|
|
1918
2152
|
*/
|
|
1919
2153
|
async waitForInvisible(locator, sec = null) {
|
|
1920
2154
|
const aSec = sec || this.options.waitForTimeout;
|
|
2155
|
+
if (isWebDriver5()) {
|
|
2156
|
+
return this.browser.waitUntil(async () => {
|
|
2157
|
+
const res = await this.$$(withStrictLocator(locator));
|
|
2158
|
+
if (!res || res.length === 0) return true;
|
|
2159
|
+
const selected = await forEachAsync(res, async el => el.isDisplayed());
|
|
2160
|
+
return !selected.length;
|
|
2161
|
+
}, aSec * 1000, `element (${new Locator(locator)}) still visible after ${aSec} sec`);
|
|
2162
|
+
}
|
|
1921
2163
|
return this.browser.waitUntil(async () => {
|
|
1922
2164
|
const res = await this.$$(withStrictLocator(locator));
|
|
1923
2165
|
if (!res || res.length === 0) return true;
|
|
1924
2166
|
const selected = await forEachAsync(res, async el => el.isDisplayed());
|
|
1925
2167
|
return !selected.length;
|
|
1926
|
-
}, aSec * 1000, `element (${new Locator(locator)}) still visible after ${aSec} sec`);
|
|
2168
|
+
}, { timeout: aSec * 1000, timeoutMsg: `element (${new Locator(locator)}) still visible after ${aSec} sec` });
|
|
1927
2169
|
}
|
|
1928
2170
|
|
|
1929
2171
|
/**
|
|
@@ -1945,13 +2187,22 @@ class WebDriver extends Helper {
|
|
|
1945
2187
|
*/
|
|
1946
2188
|
async waitForDetached(locator, sec = null) {
|
|
1947
2189
|
const aSec = sec || this.options.waitForTimeout;
|
|
2190
|
+
if (isWebDriver5()) {
|
|
2191
|
+
return this.browser.waitUntil(async () => {
|
|
2192
|
+
const res = await this.$$(withStrictLocator(locator));
|
|
2193
|
+
if (!res || res.length === 0) {
|
|
2194
|
+
return true;
|
|
2195
|
+
}
|
|
2196
|
+
return false;
|
|
2197
|
+
}, aSec * 1000, `element (${new Locator(locator)}) still on page after ${aSec} sec`);
|
|
2198
|
+
}
|
|
1948
2199
|
return this.browser.waitUntil(async () => {
|
|
1949
2200
|
const res = await this.$$(withStrictLocator(locator));
|
|
1950
2201
|
if (!res || res.length === 0) {
|
|
1951
2202
|
return true;
|
|
1952
2203
|
}
|
|
1953
2204
|
return false;
|
|
1954
|
-
}, aSec * 1000, `element (${new Locator(locator)}) still on page after ${aSec} sec`);
|
|
2205
|
+
}, { timeout: aSec * 1000, timeoutMsg: `element (${new Locator(locator)}) still on page after ${aSec} sec` });
|
|
1955
2206
|
}
|
|
1956
2207
|
|
|
1957
2208
|
/**
|
|
@@ -1969,7 +2220,10 @@ class WebDriver extends Helper {
|
|
|
1969
2220
|
}
|
|
1970
2221
|
|
|
1971
2222
|
const aSec = sec || this.options.waitForTimeout;
|
|
1972
|
-
|
|
2223
|
+
if (isWebDriver5()) {
|
|
2224
|
+
return this.browser.waitUntil(async () => this.browser.execute(fn, ...args), aSec * 1000, '');
|
|
2225
|
+
}
|
|
2226
|
+
return this.browser.waitUntil(async () => this.browser.execute(fn, ...args), { timeout: aSec * 1000, timeoutMsg: '' });
|
|
1973
2227
|
}
|
|
1974
2228
|
|
|
1975
2229
|
/**
|
|
@@ -1979,7 +2233,10 @@ class WebDriver extends Helper {
|
|
|
1979
2233
|
async waitUntil(fn, sec = null, timeoutMsg = null, interval = null) {
|
|
1980
2234
|
const aSec = sec || this.options.waitForTimeout;
|
|
1981
2235
|
const _interval = typeof interval === 'number' ? interval * 1000 : null;
|
|
1982
|
-
|
|
2236
|
+
if (isWebDriver5()) {
|
|
2237
|
+
return this.browser.waitUntil(fn, aSec * 1000, timeoutMsg, _interval);
|
|
2238
|
+
}
|
|
2239
|
+
return this.browser.waitUntil(fn, { timeout: aSec * 1000, timeoutMsg, interval: _interval });
|
|
1983
2240
|
}
|
|
1984
2241
|
|
|
1985
2242
|
/**
|
|
@@ -2016,6 +2273,19 @@ class WebDriver extends Helper {
|
|
|
2016
2273
|
const aSec = sec || this.options.waitForTimeout;
|
|
2017
2274
|
let target;
|
|
2018
2275
|
const current = await this.browser.getWindowHandle();
|
|
2276
|
+
|
|
2277
|
+
if (isWebDriver5()) {
|
|
2278
|
+
await this.browser.waitUntil(async () => {
|
|
2279
|
+
await this.browser.getWindowHandles().then((handles) => {
|
|
2280
|
+
if (handles.indexOf(current) + num + 1 <= handles.length) {
|
|
2281
|
+
target = handles[handles.indexOf(current) + num];
|
|
2282
|
+
}
|
|
2283
|
+
});
|
|
2284
|
+
return target;
|
|
2285
|
+
}, aSec * 1000, `There is no ability to switch to next tab with offset ${num}`);
|
|
2286
|
+
return this.browser.switchToWindow(target);
|
|
2287
|
+
}
|
|
2288
|
+
|
|
2019
2289
|
await this.browser.waitUntil(async () => {
|
|
2020
2290
|
await this.browser.getWindowHandles().then((handles) => {
|
|
2021
2291
|
if (handles.indexOf(current) + num + 1 <= handles.length) {
|
|
@@ -2023,7 +2293,7 @@ class WebDriver extends Helper {
|
|
|
2023
2293
|
}
|
|
2024
2294
|
});
|
|
2025
2295
|
return target;
|
|
2026
|
-
}, aSec * 1000, `There is no ability to switch to next tab with offset ${num}`);
|
|
2296
|
+
}, { timeout: aSec * 1000, timeoutMsg: `There is no ability to switch to next tab with offset ${num}` });
|
|
2027
2297
|
return this.browser.switchToWindow(target);
|
|
2028
2298
|
}
|
|
2029
2299
|
|
|
@@ -2042,6 +2312,19 @@ class WebDriver extends Helper {
|
|
|
2042
2312
|
const aSec = sec || this.options.waitForTimeout;
|
|
2043
2313
|
const current = await this.browser.getWindowHandle();
|
|
2044
2314
|
let target;
|
|
2315
|
+
|
|
2316
|
+
if (isWebDriver5()) {
|
|
2317
|
+
await this.browser.waitUntil(async () => {
|
|
2318
|
+
await this.browser.getWindowHandles().then((handles) => {
|
|
2319
|
+
if (handles.indexOf(current) - num > -1) {
|
|
2320
|
+
target = handles[handles.indexOf(current) - num];
|
|
2321
|
+
}
|
|
2322
|
+
});
|
|
2323
|
+
return target;
|
|
2324
|
+
}, aSec * 1000, `There is no ability to switch to previous tab with offset ${num}`);
|
|
2325
|
+
return this.browser.switchToWindow(target);
|
|
2326
|
+
}
|
|
2327
|
+
|
|
2045
2328
|
await this.browser.waitUntil(async () => {
|
|
2046
2329
|
await this.browser.getWindowHandles().then((handles) => {
|
|
2047
2330
|
if (handles.indexOf(current) - num > -1) {
|
|
@@ -2049,7 +2332,7 @@ class WebDriver extends Helper {
|
|
|
2049
2332
|
}
|
|
2050
2333
|
});
|
|
2051
2334
|
return target;
|
|
2052
|
-
}, aSec * 1000, `There is no ability to switch to previous tab with offset ${num}`);
|
|
2335
|
+
}, { timeout: aSec * 1000, timeoutMsg: `There is no ability to switch to previous tab with offset ${num}` });
|
|
2053
2336
|
return this.browser.switchToWindow(target);
|
|
2054
2337
|
}
|
|
2055
2338
|
|
|
@@ -2284,7 +2567,6 @@ async function filterAsync(array, callback) {
|
|
|
2284
2567
|
return values;
|
|
2285
2568
|
}
|
|
2286
2569
|
|
|
2287
|
-
|
|
2288
2570
|
async function findClickable(locator, locateFn) {
|
|
2289
2571
|
locator = new Locator(locator);
|
|
2290
2572
|
if (locator.isAccessibilityId() && !this.isWeb) return locateFn(locator, true);
|
|
@@ -2308,6 +2590,7 @@ async function findClickable(locator, locateFn) {
|
|
|
2308
2590
|
|
|
2309
2591
|
async function findFields(locator) {
|
|
2310
2592
|
locator = new Locator(locator);
|
|
2593
|
+
|
|
2311
2594
|
if (locator.isAccessibilityId() && !this.isWeb) return this._locate(locator, true);
|
|
2312
2595
|
if (!locator.isFuzzy()) return this._locate(locator, true);
|
|
2313
2596
|
|
|
@@ -2622,4 +2905,8 @@ function prepareLocateFn(context) {
|
|
|
2622
2905
|
};
|
|
2623
2906
|
}
|
|
2624
2907
|
|
|
2908
|
+
function isWebDriver5() {
|
|
2909
|
+
return version && version.indexOf('5') === 0;
|
|
2910
|
+
}
|
|
2911
|
+
|
|
2625
2912
|
module.exports = WebDriver;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
module.exports.createValueEngine = () => {
|
|
2
|
+
return {
|
|
3
|
+
// Creates a selector that matches given target when queried at the root.
|
|
4
|
+
// Can return undefined if unable to create one.
|
|
5
|
+
create(root, target) {
|
|
6
|
+
return null;
|
|
7
|
+
},
|
|
8
|
+
|
|
9
|
+
// Returns the first element matching given selector in the root's subtree.
|
|
10
|
+
query(root, selector) {
|
|
11
|
+
if (!root) {
|
|
12
|
+
return null;
|
|
13
|
+
}
|
|
14
|
+
return `${root.value}`.includes(selector) ? root : null;
|
|
15
|
+
},
|
|
16
|
+
|
|
17
|
+
// Returns all elements matching given selector in the root's subtree.
|
|
18
|
+
queryAll(root, selector) {
|
|
19
|
+
if (!root) {
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
return `${root.value}`.includes(selector) ? root : null;
|
|
23
|
+
},
|
|
24
|
+
};
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
module.exports.createDisabledEngine = () => {
|
|
28
|
+
return {
|
|
29
|
+
// Creates a selector that matches given target when queried at the root.
|
|
30
|
+
// Can return undefined if unable to create one.
|
|
31
|
+
create(root, target) {
|
|
32
|
+
return null;
|
|
33
|
+
},
|
|
34
|
+
|
|
35
|
+
// Returns the first element matching given selector in the root's subtree.
|
|
36
|
+
query(root, value) {
|
|
37
|
+
const bool = value === 'true';
|
|
38
|
+
if (!root) {
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
return root.disabled === bool ? root : null;
|
|
42
|
+
},
|
|
43
|
+
|
|
44
|
+
// Returns all elements matching given selector in the root's subtree.
|
|
45
|
+
queryAll(root, value) {
|
|
46
|
+
const bool = value === 'true';
|
|
47
|
+
if (!root) {
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
return root.disabled === bool ? root : null;
|
|
51
|
+
},
|
|
52
|
+
};
|
|
53
|
+
};
|