codeceptjs 3.5.15 → 3.6.0-beta.1.ai-healers
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 +2 -2
- package/bin/codecept.js +66 -30
- package/docs/advanced.md +351 -0
- package/docs/ai.md +365 -0
- package/docs/api.md +323 -0
- package/docs/basics.md +979 -0
- package/docs/bdd.md +539 -0
- package/docs/best.md +237 -0
- package/docs/books.md +37 -0
- package/docs/bootstrap.md +135 -0
- package/docs/build/AI.js +124 -0
- package/docs/build/ApiDataFactory.js +410 -0
- package/docs/build/Appium.js +2027 -0
- package/docs/build/Expect.js +422 -0
- package/docs/build/FileSystem.js +228 -0
- package/docs/build/GraphQL.js +229 -0
- package/docs/build/GraphQLDataFactory.js +309 -0
- package/docs/build/JSONResponse.js +338 -0
- package/docs/build/Mochawesome.js +71 -0
- package/docs/build/Nightmare.js +2152 -0
- package/docs/build/Playwright.js +5110 -0
- package/docs/build/Protractor.js +2706 -0
- package/docs/build/Puppeteer.js +3905 -0
- package/docs/build/REST.js +344 -0
- package/docs/build/TestCafe.js +2125 -0
- package/docs/build/WebDriver.js +4240 -0
- package/docs/changelog.md +2572 -0
- package/docs/commands.md +266 -0
- package/docs/community-helpers.md +58 -0
- package/docs/configuration.md +157 -0
- package/docs/continuous-integration.md +22 -0
- package/docs/custom-helpers.md +306 -0
- package/docs/data.md +379 -0
- package/docs/detox.md +235 -0
- package/docs/docker.md +136 -0
- package/docs/email.md +183 -0
- package/docs/examples.md +149 -0
- package/docs/heal.md +186 -0
- package/docs/helpers/ApiDataFactory.md +266 -0
- package/docs/helpers/Appium.md +1374 -0
- package/docs/helpers/Detox.md +586 -0
- package/docs/helpers/Expect.md +275 -0
- package/docs/helpers/FileSystem.md +152 -0
- package/docs/helpers/GraphQL.md +151 -0
- package/docs/helpers/GraphQLDataFactory.md +226 -0
- package/docs/helpers/JSONResponse.md +254 -0
- package/docs/helpers/Mochawesome.md +8 -0
- package/docs/helpers/MockRequest.md +377 -0
- package/docs/helpers/Nightmare.md +1305 -0
- package/docs/helpers/OpenAI.md +70 -0
- package/docs/helpers/Playwright.md +2759 -0
- package/docs/helpers/Polly.md +44 -0
- package/docs/helpers/Protractor.md +1769 -0
- package/docs/helpers/Puppeteer-firefox.md +86 -0
- package/docs/helpers/Puppeteer.md +2317 -0
- package/docs/helpers/REST.md +218 -0
- package/docs/helpers/TestCafe.md +1321 -0
- package/docs/helpers/WebDriver.md +2547 -0
- package/docs/hooks.md +340 -0
- package/docs/index.md +111 -0
- package/docs/installation.md +75 -0
- package/docs/internal-api.md +266 -0
- package/docs/locators.md +339 -0
- package/docs/mobile-react-native-locators.md +67 -0
- package/docs/mobile.md +338 -0
- package/docs/pageobjects.md +291 -0
- package/docs/parallel.md +400 -0
- package/docs/playwright.md +632 -0
- package/docs/plugins.md +1247 -0
- package/docs/puppeteer.md +316 -0
- package/docs/quickstart.md +162 -0
- package/docs/react.md +70 -0
- package/docs/reports.md +392 -0
- package/docs/secrets.md +36 -0
- package/docs/shadow.md +68 -0
- package/docs/shared/keys.mustache +31 -0
- package/docs/shared/react.mustache +1 -0
- package/docs/testcafe.md +174 -0
- package/docs/translation.md +247 -0
- package/docs/tutorial.md +271 -0
- package/docs/typescript.md +180 -0
- package/docs/ui.md +59 -0
- package/docs/videos.md +28 -0
- package/docs/visual.md +202 -0
- package/docs/vue.md +143 -0
- package/docs/webdriver.md +701 -0
- package/docs/wiki/Books-&-Posts.md +27 -0
- package/docs/wiki/Community-Helpers-&-Plugins.md +53 -0
- package/docs/wiki/Converting-Playwright-to-Istanbul-Coverage.md +61 -0
- package/docs/wiki/Examples.md +145 -0
- package/docs/wiki/Google-Summer-of-Code-(GSoC)-2020.md +68 -0
- package/docs/wiki/Home.md +16 -0
- package/docs/wiki/Migration-to-Appium-v2---CodeceptJS.md +83 -0
- package/docs/wiki/Release-Process.md +24 -0
- package/docs/wiki/Roadmap.md +23 -0
- package/docs/wiki/Tests.md +1393 -0
- package/docs/wiki/Upgrading-to-CodeceptJS-3.md +153 -0
- package/docs/wiki/Videos.md +19 -0
- package/lib/actor.js +3 -6
- package/lib/ai.js +152 -80
- package/lib/cli.js +1 -0
- package/lib/command/dryRun.js +13 -44
- package/lib/command/generate.js +34 -0
- package/lib/command/run-workers.js +3 -0
- package/lib/command/run.js +3 -0
- package/lib/container.js +2 -0
- package/lib/heal.js +172 -0
- package/lib/helper/AI.js +124 -0
- package/lib/helper/Appium.js +12 -36
- package/lib/helper/Expect.js +8 -11
- package/lib/helper/JSONResponse.js +8 -8
- package/lib/helper/Playwright.js +240 -100
- package/lib/helper/Puppeteer.js +68 -182
- package/lib/helper/REST.js +1 -4
- package/lib/helper/WebDriver.js +10 -324
- package/lib/index.js +3 -0
- package/lib/listener/steps.js +0 -2
- package/lib/locator.js +4 -13
- package/lib/plugin/coverage.js +99 -112
- package/lib/plugin/heal.js +26 -117
- package/lib/recorder.js +11 -5
- package/lib/step.js +1 -3
- package/lib/store.js +2 -0
- package/lib/template/heal.js +39 -0
- package/package.json +35 -47
- package/typings/index.d.ts +0 -17
- package/typings/promiseBasedTypes.d.ts +57 -340
- package/typings/types.d.ts +73 -433
- package/docs/webapi/dontSeeTraffic.mustache +0 -13
- package/docs/webapi/flushNetworkTraffics.mustache +0 -5
- package/docs/webapi/grabRecordedNetworkTraffics.mustache +0 -10
- package/docs/webapi/seeTraffic.mustache +0 -36
- package/docs/webapi/startRecordingTraffic.mustache +0 -8
- package/docs/webapi/stopRecordingTraffic.mustache +0 -5
- package/docs/webapi/waitForCookie.mustache +0 -9
- package/lib/helper/MockServer.js +0 -221
- package/lib/helper/errors/ElementAssertion.js +0 -38
- package/lib/helper/networkTraffics/utils.js +0 -137
- /package/{lib/helper → docs/build}/OpenAI.js +0 -0
package/lib/helper/Puppeteer.js
CHANGED
|
@@ -5,7 +5,6 @@ const path = require('path');
|
|
|
5
5
|
|
|
6
6
|
const Helper = require('@codeceptjs/helper');
|
|
7
7
|
const { v4: uuidv4 } = require('uuid');
|
|
8
|
-
const promiseRetry = require('promise-retry');
|
|
9
8
|
const Locator = require('../locator');
|
|
10
9
|
const recorder = require('../recorder');
|
|
11
10
|
const store = require('../store');
|
|
@@ -40,9 +39,6 @@ const findReact = require('./extras/React');
|
|
|
40
39
|
const { highlightElement } = require('./scripts/highlightElement');
|
|
41
40
|
const { blurElement } = require('./scripts/blurElement');
|
|
42
41
|
const { focusElement } = require('./scripts/focusElement');
|
|
43
|
-
const {
|
|
44
|
-
dontSeeElementError, seeElementError, dontSeeElementInDOMError, seeElementInDOMError,
|
|
45
|
-
} = require('./errors/ElementAssertion');
|
|
46
42
|
|
|
47
43
|
let puppeteer;
|
|
48
44
|
let perfTiming;
|
|
@@ -78,7 +74,7 @@ const consoleLogStore = new Console();
|
|
|
78
74
|
* @prop {string} [browser=chrome] - can be changed to `firefox` when using [puppeteer-firefox](https://codecept.io/helpers/Puppeteer-firefox).
|
|
79
75
|
* @prop {object} [chrome] - pass additional [Puppeteer run options](https://github.com/GoogleChrome/puppeteer/blob/master/docs/api.md#puppeteerlaunchoptions).
|
|
80
76
|
* @prop {boolean} [highlightElement] - highlight the interacting elements. Default: false. Note: only activate under verbose mode (--verbose).
|
|
81
|
-
|
|
77
|
+
*/
|
|
82
78
|
const config = {};
|
|
83
79
|
|
|
84
80
|
/**
|
|
@@ -369,7 +365,7 @@ class Puppeteer extends Helper {
|
|
|
369
365
|
this.debugSection('Incognito Tab', 'opened');
|
|
370
366
|
this.activeSessionName = name;
|
|
371
367
|
|
|
372
|
-
const bc = await this.browser.
|
|
368
|
+
const bc = await this.browser.createIncognitoBrowserContext();
|
|
373
369
|
await bc.newPage();
|
|
374
370
|
|
|
375
371
|
// Create a new page inside context.
|
|
@@ -486,7 +482,7 @@ class Puppeteer extends Helper {
|
|
|
486
482
|
if (!page) return;
|
|
487
483
|
page.setDefaultNavigationTimeout(this.options.getPageTimeout);
|
|
488
484
|
this.context = await this.page.$('body');
|
|
489
|
-
if (this.
|
|
485
|
+
if (this.config.browser === 'chrome') {
|
|
490
486
|
await page.bringToFront();
|
|
491
487
|
}
|
|
492
488
|
}
|
|
@@ -599,7 +595,14 @@ class Puppeteer extends Helper {
|
|
|
599
595
|
}
|
|
600
596
|
|
|
601
597
|
async _evaluateHandeInContext(...args) {
|
|
602
|
-
|
|
598
|
+
let context = await this._getContext();
|
|
599
|
+
|
|
600
|
+
if (context.constructor.name === 'Frame') {
|
|
601
|
+
// Currently there is no evalateHandle for the Frame object
|
|
602
|
+
// https://github.com/GoogleChrome/puppeteer/issues/1051
|
|
603
|
+
context = await context.executionContext();
|
|
604
|
+
}
|
|
605
|
+
|
|
603
606
|
return context.evaluateHandle(...args);
|
|
604
607
|
}
|
|
605
608
|
|
|
@@ -651,9 +654,9 @@ class Puppeteer extends Helper {
|
|
|
651
654
|
url = this.options.url + url;
|
|
652
655
|
}
|
|
653
656
|
|
|
654
|
-
if (this.
|
|
657
|
+
if (this.config.basicAuth && (this.isAuthenticated !== true)) {
|
|
655
658
|
if (url.includes(this.options.url)) {
|
|
656
|
-
await this.page.authenticate(this.
|
|
659
|
+
await this.page.authenticate(this.config.basicAuth);
|
|
657
660
|
this.isAuthenticated = true;
|
|
658
661
|
}
|
|
659
662
|
}
|
|
@@ -877,8 +880,7 @@ class Puppeteer extends Helper {
|
|
|
877
880
|
* {{ react }}
|
|
878
881
|
*/
|
|
879
882
|
async _locate(locator) {
|
|
880
|
-
|
|
881
|
-
return findElements.call(this, context, locator);
|
|
883
|
+
return findElements(await this.context, locator);
|
|
882
884
|
}
|
|
883
885
|
|
|
884
886
|
/**
|
|
@@ -904,7 +906,7 @@ class Puppeteer extends Helper {
|
|
|
904
906
|
* ```
|
|
905
907
|
*/
|
|
906
908
|
async _locateClickable(locator) {
|
|
907
|
-
const context = await this.
|
|
909
|
+
const context = await this._getContext();
|
|
908
910
|
return findClickable.call(this, context, locator);
|
|
909
911
|
}
|
|
910
912
|
|
|
@@ -1036,11 +1038,8 @@ class Puppeteer extends Helper {
|
|
|
1036
1038
|
els = (await Promise.all(els.map(el => el.boundingBox() && el))).filter(v => v);
|
|
1037
1039
|
// Puppeteer visibility was ignored? | Remove when Puppeteer is fixed
|
|
1038
1040
|
els = await Promise.all(els.map(async el => (await el.evaluate(node => window.getComputedStyle(node).visibility !== 'hidden' && window.getComputedStyle(node).display !== 'none')) && el));
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
} catch (e) {
|
|
1042
|
-
dontSeeElementError(locator);
|
|
1043
|
-
}
|
|
1041
|
+
|
|
1042
|
+
return empty('visible elements').negate(els.filter(v => v).fill('ELEMENT'));
|
|
1044
1043
|
}
|
|
1045
1044
|
|
|
1046
1045
|
/**
|
|
@@ -1052,11 +1051,8 @@ class Puppeteer extends Helper {
|
|
|
1052
1051
|
els = (await Promise.all(els.map(el => el.boundingBox() && el))).filter(v => v);
|
|
1053
1052
|
// Puppeteer visibility was ignored? | Remove when Puppeteer is fixed
|
|
1054
1053
|
els = await Promise.all(els.map(async el => (await el.evaluate(node => window.getComputedStyle(node).visibility !== 'hidden' && window.getComputedStyle(node).display !== 'none')) && el));
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
} catch (e) {
|
|
1058
|
-
seeElementError(locator);
|
|
1059
|
-
}
|
|
1054
|
+
|
|
1055
|
+
return empty('visible elements').assert(els.filter(v => v).fill('ELEMENT'));
|
|
1060
1056
|
}
|
|
1061
1057
|
|
|
1062
1058
|
/**
|
|
@@ -1064,11 +1060,7 @@ class Puppeteer extends Helper {
|
|
|
1064
1060
|
*/
|
|
1065
1061
|
async seeElementInDOM(locator) {
|
|
1066
1062
|
const els = await this._locate(locator);
|
|
1067
|
-
|
|
1068
|
-
return empty('elements on page').negate(els.filter(v => v).fill('ELEMENT'));
|
|
1069
|
-
} catch (e) {
|
|
1070
|
-
dontSeeElementInDOMError(locator);
|
|
1071
|
-
}
|
|
1063
|
+
return empty('elements on page').negate(els.filter(v => v).fill('ELEMENT'));
|
|
1072
1064
|
}
|
|
1073
1065
|
|
|
1074
1066
|
/**
|
|
@@ -1076,11 +1068,7 @@ class Puppeteer extends Helper {
|
|
|
1076
1068
|
*/
|
|
1077
1069
|
async dontSeeElementInDOM(locator) {
|
|
1078
1070
|
const els = await this._locate(locator);
|
|
1079
|
-
|
|
1080
|
-
return empty('elements on a page').assert(els.filter(v => v).fill('ELEMENT'));
|
|
1081
|
-
} catch (e) {
|
|
1082
|
-
seeElementInDOMError(locator);
|
|
1083
|
-
}
|
|
1071
|
+
return empty('elements on a page').assert(els.filter(v => v).fill('ELEMENT'));
|
|
1084
1072
|
}
|
|
1085
1073
|
|
|
1086
1074
|
/**
|
|
@@ -1630,38 +1618,6 @@ class Puppeteer extends Helper {
|
|
|
1630
1618
|
if (cookie[0]) return cookie[0];
|
|
1631
1619
|
}
|
|
1632
1620
|
|
|
1633
|
-
/**
|
|
1634
|
-
* {{> waitForCookie }}
|
|
1635
|
-
*/
|
|
1636
|
-
async waitForCookie(name, sec) {
|
|
1637
|
-
// by default, we will retry 3 times
|
|
1638
|
-
let retries = 3;
|
|
1639
|
-
const waitTimeout = sec ? sec * 1000 : this.options.waitForTimeout;
|
|
1640
|
-
|
|
1641
|
-
if (sec) {
|
|
1642
|
-
retries = sec;
|
|
1643
|
-
} else {
|
|
1644
|
-
retries = Math.ceil(waitTimeout / 1000) - 1;
|
|
1645
|
-
}
|
|
1646
|
-
|
|
1647
|
-
return promiseRetry(async (retry, number) => {
|
|
1648
|
-
const _grabCookie = async (name) => {
|
|
1649
|
-
const cookies = await this.page.cookies();
|
|
1650
|
-
const cookie = cookies.filter(c => c.name === name);
|
|
1651
|
-
if (cookie.length === 0) throw Error(`Cookie ${name} is not found after ${retries}s`);
|
|
1652
|
-
};
|
|
1653
|
-
|
|
1654
|
-
this.debugSection('Wait for cookie: ', name);
|
|
1655
|
-
if (number > 1) this.debugSection('Retrying... Attempt #', number);
|
|
1656
|
-
|
|
1657
|
-
try {
|
|
1658
|
-
await _grabCookie(name);
|
|
1659
|
-
} catch (e) {
|
|
1660
|
-
retry(e);
|
|
1661
|
-
}
|
|
1662
|
-
}, { retries, maxTimeout: 1000 });
|
|
1663
|
-
}
|
|
1664
|
-
|
|
1665
1621
|
/**
|
|
1666
1622
|
* {{> clearCookie }}
|
|
1667
1623
|
*/
|
|
@@ -1681,8 +1637,8 @@ class Puppeteer extends Helper {
|
|
|
1681
1637
|
* {{> executeScript }}
|
|
1682
1638
|
*/
|
|
1683
1639
|
async executeScript(...args) {
|
|
1684
|
-
let context =
|
|
1685
|
-
if (this.context && this.context.constructor.name === '
|
|
1640
|
+
let context = this.page;
|
|
1641
|
+
if (this.context && this.context.constructor.name === 'Frame') {
|
|
1686
1642
|
context = this.context; // switching to iframe context
|
|
1687
1643
|
}
|
|
1688
1644
|
return context.evaluate.apply(context, args);
|
|
@@ -1763,7 +1719,7 @@ class Puppeteer extends Helper {
|
|
|
1763
1719
|
*/
|
|
1764
1720
|
async grabHTMLFromAll(locator) {
|
|
1765
1721
|
const els = await this._locate(locator);
|
|
1766
|
-
const values = await Promise.all(els.map(el => el.evaluate(element => element.innerHTML, el)));
|
|
1722
|
+
const values = await Promise.all(els.map(el => el.executionContext().evaluate(element => element.innerHTML, el)));
|
|
1767
1723
|
return values;
|
|
1768
1724
|
}
|
|
1769
1725
|
|
|
@@ -1786,7 +1742,7 @@ class Puppeteer extends Helper {
|
|
|
1786
1742
|
*/
|
|
1787
1743
|
async grabCssPropertyFromAll(locator, cssProperty) {
|
|
1788
1744
|
const els = await this._locate(locator);
|
|
1789
|
-
const res = await Promise.all(els.map(el => el.evaluate(el => JSON.parse(JSON.stringify(getComputedStyle(el))), el)));
|
|
1745
|
+
const res = await Promise.all(els.map(el => el.executionContext().evaluate(el => JSON.parse(JSON.stringify(getComputedStyle(el))), el)));
|
|
1790
1746
|
const cssValues = res.map(props => props[toCamelCase(cssProperty)]);
|
|
1791
1747
|
|
|
1792
1748
|
return cssValues;
|
|
@@ -1848,34 +1804,33 @@ class Puppeteer extends Helper {
|
|
|
1848
1804
|
* {{ react }}
|
|
1849
1805
|
*/
|
|
1850
1806
|
async seeAttributesOnElements(locator, attributes) {
|
|
1851
|
-
const
|
|
1852
|
-
assertElementExists(
|
|
1853
|
-
|
|
1854
|
-
const expectedAttributes = Object.entries(attributes);
|
|
1807
|
+
const res = await this._locate(locator);
|
|
1808
|
+
assertElementExists(res, locator);
|
|
1855
1809
|
|
|
1856
|
-
const
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1810
|
+
const elemAmount = res.length;
|
|
1811
|
+
const commands = [];
|
|
1812
|
+
res.forEach((el) => {
|
|
1813
|
+
Object.keys(attributes).forEach((prop) => {
|
|
1814
|
+
commands.push(el
|
|
1815
|
+
.executionContext()
|
|
1816
|
+
.evaluateHandle((el, attr) => el[attr] || el.getAttribute(attr), el, prop)
|
|
1817
|
+
.then(el => el.jsonValue()));
|
|
1818
|
+
});
|
|
1863
1819
|
});
|
|
1864
|
-
|
|
1865
|
-
const
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
return equals(`all elements (${(new Locator(locator))}) to have attributes ${JSON.stringify(attributes)}`)
|
|
1878
|
-
.assert(matchingCount, elementsCount);
|
|
1820
|
+
let attrs = await Promise.all(commands);
|
|
1821
|
+
const values = Object.keys(attributes).map(key => attributes[key]);
|
|
1822
|
+
if (!Array.isArray(attrs)) attrs = [attrs];
|
|
1823
|
+
let chunked = chunkArray(attrs, values.length);
|
|
1824
|
+
chunked = chunked.filter((val) => {
|
|
1825
|
+
for (let i = 0; i < val.length; ++i) {
|
|
1826
|
+
const _actual = Number.isNaN(val[i]) || (typeof values[i]) === 'string' ? val[i] : Number.parseInt(values[i], 10);
|
|
1827
|
+
const _expected = Number.isNaN(values[i]) || (typeof values[i]) === 'string' ? values[i] : Number.parseInt(values[i], 10);
|
|
1828
|
+
// if the attribute doesn't exist, returns false as well
|
|
1829
|
+
if (!_actual || !_actual.includes(_expected)) return false;
|
|
1830
|
+
}
|
|
1831
|
+
return true;
|
|
1832
|
+
});
|
|
1833
|
+
return equals(`all elements (${(new Locator(locator))}) to have attributes ${JSON.stringify(attributes)}`).assert(chunked.length, elemAmount);
|
|
1879
1834
|
}
|
|
1880
1835
|
|
|
1881
1836
|
/**
|
|
@@ -2131,7 +2086,7 @@ class Puppeteer extends Helper {
|
|
|
2131
2086
|
if (locator.isCSS()) {
|
|
2132
2087
|
waiter = context.waitForSelector(locator.simplify(), { timeout: waitTimeout });
|
|
2133
2088
|
} else {
|
|
2134
|
-
waiter =
|
|
2089
|
+
waiter = context.waitForXPath(locator.value, { timeout: waitTimeout });
|
|
2135
2090
|
}
|
|
2136
2091
|
return waiter.catch((err) => {
|
|
2137
2092
|
throw new Error(`element (${locator.toString()}) still not present on page after ${waitTimeout / 1000} sec\n${err.message}`);
|
|
@@ -2152,7 +2107,7 @@ class Puppeteer extends Helper {
|
|
|
2152
2107
|
if (locator.isCSS()) {
|
|
2153
2108
|
waiter = context.waitForSelector(locator.simplify(), { timeout: waitTimeout, visible: true });
|
|
2154
2109
|
} else {
|
|
2155
|
-
waiter =
|
|
2110
|
+
waiter = context.waitForXPath(locator.value, { timeout: waitTimeout, visible: true });
|
|
2156
2111
|
}
|
|
2157
2112
|
return waiter.catch((err) => {
|
|
2158
2113
|
throw new Error(`element (${locator.toString()}) still not visible after ${waitTimeout / 1000} sec\n${err.message}`);
|
|
@@ -2171,7 +2126,7 @@ class Puppeteer extends Helper {
|
|
|
2171
2126
|
if (locator.isCSS()) {
|
|
2172
2127
|
waiter = context.waitForSelector(locator.simplify(), { timeout: waitTimeout, hidden: true });
|
|
2173
2128
|
} else {
|
|
2174
|
-
waiter =
|
|
2129
|
+
waiter = context.waitForXPath(locator.value, { timeout: waitTimeout, hidden: true });
|
|
2175
2130
|
}
|
|
2176
2131
|
return waiter.catch((err) => {
|
|
2177
2132
|
throw new Error(`element (${locator.toString()}) still visible after ${waitTimeout / 1000} sec\n${err.message}`);
|
|
@@ -2189,7 +2144,7 @@ class Puppeteer extends Helper {
|
|
|
2189
2144
|
if (locator.isCSS()) {
|
|
2190
2145
|
waiter = context.waitForSelector(locator.simplify(), { timeout: waitTimeout, hidden: true });
|
|
2191
2146
|
} else {
|
|
2192
|
-
waiter =
|
|
2147
|
+
waiter = context.waitForXPath(locator.value, { timeout: waitTimeout, hidden: true });
|
|
2193
2148
|
}
|
|
2194
2149
|
return waiter.catch((err) => {
|
|
2195
2150
|
throw new Error(`element (${locator.toString()}) still not hidden after ${waitTimeout / 1000} sec\n${err.message}`);
|
|
@@ -2215,7 +2170,7 @@ class Puppeteer extends Helper {
|
|
|
2215
2170
|
}
|
|
2216
2171
|
|
|
2217
2172
|
async _getContext() {
|
|
2218
|
-
if (this.context && this.context.constructor.name === '
|
|
2173
|
+
if (this.context && this.context.constructor.name === 'Frame') {
|
|
2219
2174
|
return this.context;
|
|
2220
2175
|
}
|
|
2221
2176
|
return this.page;
|
|
@@ -2338,37 +2293,35 @@ class Puppeteer extends Helper {
|
|
|
2338
2293
|
async switchTo(locator) {
|
|
2339
2294
|
if (Number.isInteger(locator)) {
|
|
2340
2295
|
// Select by frame index of current context
|
|
2341
|
-
|
|
2296
|
+
|
|
2297
|
+
let childFrames = null;
|
|
2342
2298
|
if (this.context && typeof this.context.childFrames === 'function') {
|
|
2343
|
-
|
|
2299
|
+
childFrames = this.context.childFrames();
|
|
2344
2300
|
} else {
|
|
2345
|
-
|
|
2301
|
+
childFrames = this.page.mainFrame().childFrames();
|
|
2346
2302
|
}
|
|
2347
2303
|
|
|
2348
|
-
if (locator >= 0 && locator <
|
|
2349
|
-
this.context =
|
|
2304
|
+
if (locator >= 0 && locator < childFrames.length) {
|
|
2305
|
+
this.context = childFrames[locator];
|
|
2350
2306
|
} else {
|
|
2351
|
-
throw new Error('
|
|
2307
|
+
throw new Error('Element #invalidIframeSelector was not found by text|CSS|XPath');
|
|
2352
2308
|
}
|
|
2353
2309
|
return;
|
|
2354
2310
|
}
|
|
2355
|
-
|
|
2356
2311
|
if (!locator) {
|
|
2357
|
-
this.context = await this.page.mainFrame();
|
|
2312
|
+
this.context = await this.page.mainFrame().$('body');
|
|
2358
2313
|
return;
|
|
2359
2314
|
}
|
|
2360
2315
|
|
|
2361
|
-
//
|
|
2316
|
+
// iframe by selector
|
|
2362
2317
|
const els = await this._locate(locator);
|
|
2363
2318
|
assertElementExists(els, locator);
|
|
2364
|
-
|
|
2365
|
-
const iframeElement = els[0];
|
|
2366
|
-
const contentFrame = await iframeElement.contentFrame();
|
|
2319
|
+
const contentFrame = await els[0].contentFrame();
|
|
2367
2320
|
|
|
2368
2321
|
if (contentFrame) {
|
|
2369
2322
|
this.context = contentFrame;
|
|
2370
2323
|
} else {
|
|
2371
|
-
|
|
2324
|
+
this.context = els[0];
|
|
2372
2325
|
}
|
|
2373
2326
|
}
|
|
2374
2327
|
|
|
@@ -2464,7 +2417,7 @@ class Puppeteer extends Helper {
|
|
|
2464
2417
|
module.exports = Puppeteer;
|
|
2465
2418
|
|
|
2466
2419
|
async function findElements(matcher, locator) {
|
|
2467
|
-
if (locator.react) return
|
|
2420
|
+
if (locator.react) return findReact(matcher.executionContext(), locator);
|
|
2468
2421
|
locator = new Locator(locator, 'css');
|
|
2469
2422
|
if (!locator.isXPath()) return matcher.$$(locator.simplify());
|
|
2470
2423
|
// puppeteer version < 19.4.0 is no longer supported. This one is backward support.
|
|
@@ -2501,7 +2454,7 @@ async function proceedClick(locator, context = null, options = {}) {
|
|
|
2501
2454
|
}
|
|
2502
2455
|
|
|
2503
2456
|
async function findClickable(matcher, locator) {
|
|
2504
|
-
if (locator.react) return
|
|
2457
|
+
if (locator.react) return findReact(matcher.executionContext(), locator);
|
|
2505
2458
|
locator = new Locator(locator);
|
|
2506
2459
|
if (!locator.isFuzzy()) return findElements.call(this, matcher, locator);
|
|
2507
2460
|
|
|
@@ -2850,70 +2803,3 @@ function highlightActiveElement(element, context) {
|
|
|
2850
2803
|
highlightElement(element, context);
|
|
2851
2804
|
}
|
|
2852
2805
|
}
|
|
2853
|
-
|
|
2854
|
-
function _waitForElement(locator, options) {
|
|
2855
|
-
try {
|
|
2856
|
-
return this.context.waitForXPath(locator.value, options);
|
|
2857
|
-
} catch (e) {
|
|
2858
|
-
return this.context.waitForSelector(`::-p-xpath(${locator.value})`, options);
|
|
2859
|
-
}
|
|
2860
|
-
}
|
|
2861
|
-
|
|
2862
|
-
async function findReactElements(locator, props = {}, state = {}) {
|
|
2863
|
-
const resqScript = await fs.promises.readFile(require.resolve('resq'), 'utf-8');
|
|
2864
|
-
await this.page.evaluate(resqScript.toString());
|
|
2865
|
-
|
|
2866
|
-
await this.page.evaluate(() => window.resq.waitToLoadReact());
|
|
2867
|
-
const arrayHandle = await this.page.evaluateHandle((obj) => {
|
|
2868
|
-
const { selector, props, state } = obj;
|
|
2869
|
-
let elements = window.resq.resq$$(selector);
|
|
2870
|
-
if (Object.keys(props).length) {
|
|
2871
|
-
elements = elements.byProps(props);
|
|
2872
|
-
}
|
|
2873
|
-
if (Object.keys(state).length) {
|
|
2874
|
-
elements = elements.byState(state);
|
|
2875
|
-
}
|
|
2876
|
-
|
|
2877
|
-
if (!elements.length) {
|
|
2878
|
-
return [];
|
|
2879
|
-
}
|
|
2880
|
-
|
|
2881
|
-
// resq returns an array of HTMLElements if the React component is a fragment
|
|
2882
|
-
// this avoids having nested arrays of nodes which the driver does not understand
|
|
2883
|
-
// [[div, div], [div, div]] => [div, div, div, div]
|
|
2884
|
-
let nodes = [];
|
|
2885
|
-
|
|
2886
|
-
elements.forEach((element) => {
|
|
2887
|
-
let { node, isFragment } = element;
|
|
2888
|
-
|
|
2889
|
-
if (!node) {
|
|
2890
|
-
isFragment = true;
|
|
2891
|
-
node = element.children;
|
|
2892
|
-
}
|
|
2893
|
-
|
|
2894
|
-
if (isFragment) {
|
|
2895
|
-
nodes = nodes.concat(node);
|
|
2896
|
-
} else {
|
|
2897
|
-
nodes.push(node);
|
|
2898
|
-
}
|
|
2899
|
-
});
|
|
2900
|
-
|
|
2901
|
-
return [...nodes];
|
|
2902
|
-
}, {
|
|
2903
|
-
selector: locator.react,
|
|
2904
|
-
props: locator.props || {},
|
|
2905
|
-
state: locator.state || {},
|
|
2906
|
-
});
|
|
2907
|
-
|
|
2908
|
-
const properties = await arrayHandle.getProperties();
|
|
2909
|
-
const result = [];
|
|
2910
|
-
for (const property of properties.values()) {
|
|
2911
|
-
const elementHandle = property.asElement();
|
|
2912
|
-
if (elementHandle) {
|
|
2913
|
-
result.push(elementHandle);
|
|
2914
|
-
}
|
|
2915
|
-
}
|
|
2916
|
-
|
|
2917
|
-
await arrayHandle.dispose();
|
|
2918
|
-
return result;
|
|
2919
|
-
}
|
package/lib/helper/REST.js
CHANGED
|
@@ -72,12 +72,9 @@ class REST extends Helper {
|
|
|
72
72
|
this.options.maxBodyLength = maxContentLength;
|
|
73
73
|
}
|
|
74
74
|
|
|
75
|
-
|
|
76
|
-
this._setConfig(config);
|
|
77
|
-
|
|
75
|
+
this.options = { ...this.options, ...config };
|
|
78
76
|
this.headers = { ...this.options.defaultHeaders };
|
|
79
77
|
this.axios = axios.create();
|
|
80
|
-
// @ts-ignore
|
|
81
78
|
this.axios.defaults.headers = this.options.defaultHeaders;
|
|
82
79
|
}
|
|
83
80
|
|