codeceptjs 3.5.9 → 3.5.11
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 +14 -16
- package/docs/build/Appium.js +49 -49
- package/docs/build/Expect.js +33 -33
- package/docs/build/Nightmare.js +50 -50
- package/docs/build/Playwright.js +239 -133
- package/docs/build/Protractor.js +59 -59
- package/docs/build/Puppeteer.js +127 -107
- package/docs/build/TestCafe.js +48 -48
- package/docs/build/WebDriver.js +112 -93
- package/docs/helpers/Appium.md +3 -3
- package/docs/helpers/Expect.md +33 -33
- package/docs/helpers/Playwright.md +431 -325
- package/docs/helpers/Puppeteer.md +50 -24
- package/docs/helpers/WebDriver.md +41 -13
- package/docs/internal-api.md +1 -0
- package/docs/parallel.md +114 -2
- package/docs/plugins.md +7 -5
- package/docs/react.md +2 -1
- package/docs/vue.md +22 -0
- package/docs/webapi/grabWebElement.mustache +9 -0
- package/docs/webapi/grabWebElements.mustache +9 -0
- package/docs/webapi/scrollIntoView.mustache +1 -1
- package/lib/ai.js +12 -3
- package/lib/colorUtils.js +10 -0
- package/lib/command/run-multiple.js +1 -1
- package/lib/command/run-workers.js +30 -4
- package/lib/command/workers/runTests.js +39 -0
- package/lib/event.js +2 -0
- package/lib/helper/Appium.js +13 -13
- package/lib/helper/Expect.js +33 -33
- package/lib/helper/Playwright.js +125 -37
- package/lib/helper/Puppeteer.js +49 -38
- package/lib/helper/WebDriver.js +29 -19
- package/lib/helper/extras/PlaywrightReactVueLocator.js +38 -0
- package/lib/html.js +3 -3
- package/lib/interfaces/gherkin.js +8 -1
- package/lib/interfaces/scenarioConfig.js +1 -0
- package/lib/locator.js +2 -2
- package/lib/pause.js +6 -3
- package/lib/plugin/autoLogin.js +4 -2
- package/lib/plugin/heal.js +40 -7
- package/lib/plugin/retryFailedStep.js +6 -1
- package/lib/plugin/stepByStepReport.js +2 -2
- package/lib/plugin/tryTo.js +5 -4
- package/lib/recorder.js +12 -5
- package/lib/ui.js +1 -0
- package/lib/workers.js +2 -0
- package/package.json +28 -25
- package/typings/index.d.ts +1 -1
- package/typings/promiseBasedTypes.d.ts +195 -76
- package/typings/types.d.ts +191 -145
- package/lib/helper/extras/PlaywrightReact.js +0 -9
package/lib/helper/Playwright.js
CHANGED
|
@@ -33,7 +33,7 @@ const ElementNotFound = require('./errors/ElementNotFound');
|
|
|
33
33
|
const RemoteBrowserConnectionRefused = require('./errors/RemoteBrowserConnectionRefused');
|
|
34
34
|
const Popup = require('./extras/Popup');
|
|
35
35
|
const Console = require('./extras/Console');
|
|
36
|
-
const findReact = require('./extras/
|
|
36
|
+
const { findReact, findVue } = require('./extras/PlaywrightReactVueLocator');
|
|
37
37
|
|
|
38
38
|
let playwright;
|
|
39
39
|
let perfTiming;
|
|
@@ -94,6 +94,7 @@ const pathSeparator = path.sep;
|
|
|
94
94
|
* @prop {boolean} [ignoreHTTPSErrors] - Allows access to untrustworthy pages, e.g. to a page with an expired certificate. Default value is `false`
|
|
95
95
|
* @prop {boolean} [bypassCSP] - bypass Content Security Policy or CSP
|
|
96
96
|
* @prop {boolean} [highlightElement] - highlight the interacting elements. Default: false. Note: only activate under verbose mode (--verbose).
|
|
97
|
+
* @prop {object} [recordHar] - record HAR and will be saved to `output/har`. See more of [HAR options](https://playwright.dev/docs/api/class-browser#browser-new-context-option-record-har).
|
|
97
98
|
*/
|
|
98
99
|
const config = {};
|
|
99
100
|
|
|
@@ -141,6 +142,21 @@ const config = {};
|
|
|
141
142
|
* * `trace`: enables trace recording for failed tests; trace are saved into `output/trace` folder
|
|
142
143
|
* * `keepTraceForPassedTests`: - save trace for passed tests
|
|
143
144
|
*
|
|
145
|
+
* #### HAR Recording Customization
|
|
146
|
+
*
|
|
147
|
+
* A HAR file is an HTTP Archive file that contains a record of all the network requests that are made when a page is loaded.
|
|
148
|
+
* It contains information about the request and response headers, cookies, content, timings, and more. You can use HAR files to mock network requests in your tests.
|
|
149
|
+
* HAR will be saved to `output/har`. More info could be found here https://playwright.dev/docs/api/class-browser#browser-new-context-option-record-har.
|
|
150
|
+
*
|
|
151
|
+
* ```
|
|
152
|
+
* ...
|
|
153
|
+
* recordHar: {
|
|
154
|
+
* mode: 'minimal', // possible values: 'minimal'|'full'.
|
|
155
|
+
* content: 'embed' // possible values: "omit"|"embed"|"attach".
|
|
156
|
+
* }
|
|
157
|
+
* ...
|
|
158
|
+
*```
|
|
159
|
+
*
|
|
144
160
|
* #### Example #1: Wait for 0 network connections.
|
|
145
161
|
*
|
|
146
162
|
* ```js
|
|
@@ -346,7 +362,7 @@ class Playwright extends Helper {
|
|
|
346
362
|
ignoreLog: ['warning', 'log'],
|
|
347
363
|
uniqueScreenshotNames: false,
|
|
348
364
|
manualStart: false,
|
|
349
|
-
getPageTimeout:
|
|
365
|
+
getPageTimeout: 30000,
|
|
350
366
|
waitForNavigation: 'load',
|
|
351
367
|
restart: false,
|
|
352
368
|
keepCookies: false,
|
|
@@ -455,9 +471,10 @@ class Playwright extends Helper {
|
|
|
455
471
|
}
|
|
456
472
|
}
|
|
457
473
|
|
|
458
|
-
async _before() {
|
|
474
|
+
async _before(test) {
|
|
475
|
+
this.currentRunningTest = test;
|
|
459
476
|
recorder.retry({
|
|
460
|
-
retries:
|
|
477
|
+
retries: process.env.FAILED_STEP_RETRIES || 3,
|
|
461
478
|
when: err => {
|
|
462
479
|
if (!err || typeof (err.message) !== 'string') {
|
|
463
480
|
return false;
|
|
@@ -487,6 +504,15 @@ class Playwright extends Helper {
|
|
|
487
504
|
}
|
|
488
505
|
if (this.options.bypassCSP) contextOptions.bypassCSP = this.options.bypassCSP;
|
|
489
506
|
if (this.options.recordVideo) contextOptions.recordVideo = this.options.recordVideo;
|
|
507
|
+
if (this.options.recordHar) {
|
|
508
|
+
const harExt = this.options.recordHar.content && this.options.recordHar.content === 'attach' ? 'zip' : 'har';
|
|
509
|
+
const fileName = `${`${global.output_dir}${path.sep}har${path.sep}${uuidv4()}_${clearString(this.currentRunningTest.title)}`.slice(0, 245)}.${harExt}`;
|
|
510
|
+
const dir = path.dirname(fileName);
|
|
511
|
+
if (!fileExists(dir)) fs.mkdirSync(dir);
|
|
512
|
+
this.options.recordHar.path = fileName;
|
|
513
|
+
this.currentRunningTest.artifacts.har = fileName;
|
|
514
|
+
contextOptions.recordHar = this.options.recordHar;
|
|
515
|
+
}
|
|
490
516
|
if (this.storageState) contextOptions.storageState = this.storageState;
|
|
491
517
|
if (this.options.userAgent) contextOptions.userAgent = this.options.userAgent;
|
|
492
518
|
if (this.options.locale) contextOptions.locale = this.options.locale;
|
|
@@ -640,7 +666,7 @@ class Playwright extends Helper {
|
|
|
640
666
|
* ```
|
|
641
667
|
*
|
|
642
668
|
* @param {string} description used to show in logs.
|
|
643
|
-
* @param {function} fn async function that executed with Playwright helper as
|
|
669
|
+
* @param {function} fn async function that executed with Playwright helper as arguments
|
|
644
670
|
*/
|
|
645
671
|
usePlaywrightTo(description, fn) {
|
|
646
672
|
return this._useTo(...arguments);
|
|
@@ -834,6 +860,7 @@ class Playwright extends Helper {
|
|
|
834
860
|
this.context = null;
|
|
835
861
|
this.frame = null;
|
|
836
862
|
popupStore.clear();
|
|
863
|
+
if (this.options.recordHar) await this.browserContext.close();
|
|
837
864
|
await this.browser.close();
|
|
838
865
|
}
|
|
839
866
|
|
|
@@ -919,10 +946,9 @@ class Playwright extends Helper {
|
|
|
919
946
|
}
|
|
920
947
|
|
|
921
948
|
/**
|
|
922
|
-
* {{> resizeWindow }}
|
|
923
949
|
*
|
|
924
950
|
* Unlike other drivers Playwright changes the size of a viewport, not the window!
|
|
925
|
-
* Playwright does not control the window of a browser so it can't adjust its real size.
|
|
951
|
+
* Playwright does not control the window of a browser, so it can't adjust its real size.
|
|
926
952
|
* It also can't maximize a window.
|
|
927
953
|
*
|
|
928
954
|
* Update configuration to change real window size on start:
|
|
@@ -932,6 +958,8 @@ class Playwright extends Helper {
|
|
|
932
958
|
* // @codeceptjs/configure package must be installed
|
|
933
959
|
* { setWindowSize } = require('@codeceptjs/configure');
|
|
934
960
|
* ````
|
|
961
|
+
*
|
|
962
|
+
* {{> resizeWindow }}
|
|
935
963
|
*/
|
|
936
964
|
async resizeWindow(width, height) {
|
|
937
965
|
if (width === 'maximize') {
|
|
@@ -1031,8 +1059,6 @@ class Playwright extends Helper {
|
|
|
1031
1059
|
}
|
|
1032
1060
|
|
|
1033
1061
|
/**
|
|
1034
|
-
* {{> dragAndDrop }}
|
|
1035
|
-
* @param {any} [options] [Additional options](https://playwright.dev/docs/api/class-page#page-drag-and-drop) can be passed as 3rd argument.
|
|
1036
1062
|
*
|
|
1037
1063
|
* ```js
|
|
1038
1064
|
* // specify coordinates for source position
|
|
@@ -1040,6 +1066,10 @@ class Playwright extends Helper {
|
|
|
1040
1066
|
* ```
|
|
1041
1067
|
*
|
|
1042
1068
|
* > When no option is set, custom drag and drop would be used, to use the dragAndDrop API from Playwright, please set options, for example `force: true`
|
|
1069
|
+
*
|
|
1070
|
+
* {{> dragAndDrop }}
|
|
1071
|
+
* @param {any} [options] [Additional options](https://playwright.dev/docs/api/class-page#page-drag-and-drop) can be passed as 3rd argument.
|
|
1072
|
+
*
|
|
1043
1073
|
*/
|
|
1044
1074
|
async dragAndDrop(srcElement, destElement, options) {
|
|
1045
1075
|
const src = new Locator(srcElement);
|
|
@@ -1089,6 +1119,33 @@ class Playwright extends Helper {
|
|
|
1089
1119
|
return this.page.reload({ timeout: this.options.getPageTimeout, waitUntil: this.options.waitForNavigation });
|
|
1090
1120
|
}
|
|
1091
1121
|
|
|
1122
|
+
/**
|
|
1123
|
+
* Replaying from HAR
|
|
1124
|
+
*
|
|
1125
|
+
* ```js
|
|
1126
|
+
* // Replay API requests from HAR.
|
|
1127
|
+
* // Either use a matching response from the HAR,
|
|
1128
|
+
* // or abort the request if nothing matches.
|
|
1129
|
+
* I.replayFromHar('./output/har/something.har', { url: "*\/**\/api/v1/fruits" });
|
|
1130
|
+
* I.amOnPage('https://demo.playwright.dev/api-mocking');
|
|
1131
|
+
* I.see('CodeceptJS');
|
|
1132
|
+
* ```
|
|
1133
|
+
*
|
|
1134
|
+
* @param {string} harFilePath Path to recorded HAR file
|
|
1135
|
+
* @param {object} [opts] [Options for replaying from HAR](https://playwright.dev/docs/api/class-page#page-route-from-har)
|
|
1136
|
+
*
|
|
1137
|
+
* @returns Promise<void>
|
|
1138
|
+
*/
|
|
1139
|
+
async replayFromHar(harFilePath, opts) {
|
|
1140
|
+
const file = path.join(global.codecept_dir, harFilePath);
|
|
1141
|
+
|
|
1142
|
+
if (!fileExists(file)) {
|
|
1143
|
+
throw new Error(`File at ${file} cannot be found on local system`);
|
|
1144
|
+
}
|
|
1145
|
+
|
|
1146
|
+
await this.page.routeFromHAR(harFilePath, opts);
|
|
1147
|
+
}
|
|
1148
|
+
|
|
1092
1149
|
/**
|
|
1093
1150
|
* {{> scrollPageToTop }}
|
|
1094
1151
|
*/
|
|
@@ -1250,6 +1307,22 @@ class Playwright extends Helper {
|
|
|
1250
1307
|
return findFields.call(this, locator);
|
|
1251
1308
|
}
|
|
1252
1309
|
|
|
1310
|
+
/**
|
|
1311
|
+
* {{> grabWebElements }}
|
|
1312
|
+
*
|
|
1313
|
+
*/
|
|
1314
|
+
async grabWebElements(locator) {
|
|
1315
|
+
return this._locate(locator);
|
|
1316
|
+
}
|
|
1317
|
+
|
|
1318
|
+
/**
|
|
1319
|
+
* {{> grabWebElement }}
|
|
1320
|
+
*
|
|
1321
|
+
*/
|
|
1322
|
+
async grabWebElement(locator) {
|
|
1323
|
+
return this._locateElement(locator);
|
|
1324
|
+
}
|
|
1325
|
+
|
|
1253
1326
|
/**
|
|
1254
1327
|
* Switch focus to a particular tab by its number. It waits tabs loading and then switch tab
|
|
1255
1328
|
*
|
|
@@ -1444,7 +1517,7 @@ class Playwright extends Helper {
|
|
|
1444
1517
|
*
|
|
1445
1518
|
* @param {any} [options] [Additional options](https://playwright.dev/docs/api/class-page#page-click) for click available as 3rd argument.
|
|
1446
1519
|
*
|
|
1447
|
-
*
|
|
1520
|
+
* @example
|
|
1448
1521
|
*
|
|
1449
1522
|
* ```js
|
|
1450
1523
|
* // click on element at position
|
|
@@ -1477,8 +1550,6 @@ class Playwright extends Helper {
|
|
|
1477
1550
|
|
|
1478
1551
|
/**
|
|
1479
1552
|
* {{> doubleClick }}
|
|
1480
|
-
*
|
|
1481
|
-
*
|
|
1482
1553
|
*/
|
|
1483
1554
|
async doubleClick(locator, context = null) {
|
|
1484
1555
|
return proceedClick.call(this, locator, context, { clickCount: 2 });
|
|
@@ -1486,15 +1557,12 @@ class Playwright extends Helper {
|
|
|
1486
1557
|
|
|
1487
1558
|
/**
|
|
1488
1559
|
* {{> rightClick }}
|
|
1489
|
-
*
|
|
1490
|
-
*
|
|
1491
1560
|
*/
|
|
1492
1561
|
async rightClick(locator, context = null) {
|
|
1493
1562
|
return proceedClick.call(this, locator, context, { button: 'right' });
|
|
1494
1563
|
}
|
|
1495
1564
|
|
|
1496
1565
|
/**
|
|
1497
|
-
* {{> checkOption }}
|
|
1498
1566
|
*
|
|
1499
1567
|
* [Additional options](https://playwright.dev/docs/api/class-elementhandle#element-handle-check) for check available as 3rd argument.
|
|
1500
1568
|
*
|
|
@@ -1505,6 +1573,9 @@ class Playwright extends Helper {
|
|
|
1505
1573
|
* I.checkOption('Agree', '.signup', { position: { x: 5, y: 5 } })
|
|
1506
1574
|
* ```
|
|
1507
1575
|
* > ⚠️ To avoid flakiness, option `force: true` is set by default
|
|
1576
|
+
*
|
|
1577
|
+
* {{> checkOption }}
|
|
1578
|
+
*
|
|
1508
1579
|
*/
|
|
1509
1580
|
async checkOption(field, context = null, options = { force: true }) {
|
|
1510
1581
|
const elm = await this._locateCheckable(field, context);
|
|
@@ -1513,7 +1584,6 @@ class Playwright extends Helper {
|
|
|
1513
1584
|
}
|
|
1514
1585
|
|
|
1515
1586
|
/**
|
|
1516
|
-
* {{> uncheckOption }}
|
|
1517
1587
|
*
|
|
1518
1588
|
* [Additional options](https://playwright.dev/docs/api/class-elementhandle#element-handle-uncheck) for uncheck available as 3rd argument.
|
|
1519
1589
|
*
|
|
@@ -1524,6 +1594,8 @@ class Playwright extends Helper {
|
|
|
1524
1594
|
* I.uncheckOption('Agree', '.signup', { position: { x: 5, y: 5 } })
|
|
1525
1595
|
* ```
|
|
1526
1596
|
* > ⚠️ To avoid flakiness, option `force: true` is set by default
|
|
1597
|
+
*
|
|
1598
|
+
* {{> uncheckOption }}
|
|
1527
1599
|
*/
|
|
1528
1600
|
async uncheckOption(field, context = null, options = { force: true }) {
|
|
1529
1601
|
const elm = await this._locateCheckable(field, context);
|
|
@@ -1564,9 +1636,10 @@ class Playwright extends Helper {
|
|
|
1564
1636
|
}
|
|
1565
1637
|
|
|
1566
1638
|
/**
|
|
1567
|
-
* {{> pressKeyWithKeyNormalization }}
|
|
1568
1639
|
*
|
|
1569
1640
|
* _Note:_ Shortcuts like `'Meta'` + `'A'` do not work on macOS ([GoogleChrome/Puppeteer#1313](https://github.com/GoogleChrome/puppeteer/issues/1313)).
|
|
1641
|
+
*
|
|
1642
|
+
* {{> pressKeyWithKeyNormalization }}
|
|
1570
1643
|
*/
|
|
1571
1644
|
async pressKey(key) {
|
|
1572
1645
|
const modifiers = [];
|
|
@@ -1658,8 +1731,6 @@ class Playwright extends Helper {
|
|
|
1658
1731
|
|
|
1659
1732
|
/**
|
|
1660
1733
|
* {{> appendField }}
|
|
1661
|
-
*
|
|
1662
|
-
*
|
|
1663
1734
|
*/
|
|
1664
1735
|
async appendField(field, value) {
|
|
1665
1736
|
const els = await findFields.call(this, field);
|
|
@@ -1711,8 +1782,15 @@ class Playwright extends Helper {
|
|
|
1711
1782
|
const el = els[0];
|
|
1712
1783
|
|
|
1713
1784
|
await highlightActiveElement.call(this, el);
|
|
1785
|
+
let optionToSelect = '';
|
|
1714
1786
|
|
|
1715
|
-
|
|
1787
|
+
try {
|
|
1788
|
+
optionToSelect = await el.locator('option', { hasText: option }).textContent();
|
|
1789
|
+
} catch (e) {
|
|
1790
|
+
optionToSelect = option;
|
|
1791
|
+
}
|
|
1792
|
+
|
|
1793
|
+
if (!Array.isArray(option)) option = [optionToSelect];
|
|
1716
1794
|
|
|
1717
1795
|
await el.selectOption(option);
|
|
1718
1796
|
return this._waitForAction();
|
|
@@ -1876,9 +1954,9 @@ class Playwright extends Helper {
|
|
|
1876
1954
|
}
|
|
1877
1955
|
|
|
1878
1956
|
/**
|
|
1879
|
-
* {{> grabCookie }}
|
|
1880
|
-
*
|
|
1881
1957
|
* Returns cookie in JSON format. If name not passed returns all cookies for this domain.
|
|
1958
|
+
*
|
|
1959
|
+
* {{> grabCookie }}
|
|
1882
1960
|
*/
|
|
1883
1961
|
async grabCookie(name) {
|
|
1884
1962
|
const cookies = await this.browserContext.cookies();
|
|
@@ -1909,8 +1987,8 @@ class Playwright extends Helper {
|
|
|
1909
1987
|
* ```js
|
|
1910
1988
|
* I.executeScript(({x, y}) => x + y, {x, y});
|
|
1911
1989
|
* ```
|
|
1912
|
-
* You can pass only one parameter into a function
|
|
1913
|
-
*
|
|
1990
|
+
* You can pass only one parameter into a function,
|
|
1991
|
+
* or you can pass in array or object.
|
|
1914
1992
|
*
|
|
1915
1993
|
* ```js
|
|
1916
1994
|
* I.executeScript(([x, y]) => x + y, [x, y]);
|
|
@@ -2042,19 +2120,17 @@ class Playwright extends Helper {
|
|
|
2042
2120
|
|
|
2043
2121
|
const cssPropertiesCamelCase = convertCssPropertiesToCamelCase(cssProperties);
|
|
2044
2122
|
const elemAmount = res.length;
|
|
2045
|
-
const commands = [];
|
|
2046
2123
|
let props = [];
|
|
2047
2124
|
|
|
2048
2125
|
for (const element of res) {
|
|
2049
|
-
const
|
|
2050
|
-
|
|
2051
|
-
Object.keys(cssPropertiesCamelCase).forEach(prop => {
|
|
2126
|
+
for (const prop of Object.keys(cssProperties)) {
|
|
2127
|
+
const cssProp = await this.grabCssPropertyFrom(locator, prop);
|
|
2052
2128
|
if (isColorProperty(prop)) {
|
|
2053
|
-
props.push(convertColorToRGBA(
|
|
2129
|
+
props.push(convertColorToRGBA(cssProp));
|
|
2054
2130
|
} else {
|
|
2055
|
-
props.push(
|
|
2131
|
+
props.push(cssProp);
|
|
2056
2132
|
}
|
|
2057
|
-
}
|
|
2133
|
+
}
|
|
2058
2134
|
}
|
|
2059
2135
|
|
|
2060
2136
|
const values = Object.keys(cssPropertiesCamelCase).map(key => cssPropertiesCamelCase[key]);
|
|
@@ -2062,7 +2138,8 @@ class Playwright extends Helper {
|
|
|
2062
2138
|
let chunked = chunkArray(props, values.length);
|
|
2063
2139
|
chunked = chunked.filter((val) => {
|
|
2064
2140
|
for (let i = 0; i < val.length; ++i) {
|
|
2065
|
-
|
|
2141
|
+
// eslint-disable-next-line eqeqeq
|
|
2142
|
+
if (val[i] != values[i]) return false;
|
|
2066
2143
|
}
|
|
2067
2144
|
return true;
|
|
2068
2145
|
});
|
|
@@ -2091,7 +2168,8 @@ class Playwright extends Helper {
|
|
|
2091
2168
|
let chunked = chunkArray(attrs, values.length);
|
|
2092
2169
|
chunked = chunked.filter((val) => {
|
|
2093
2170
|
for (let i = 0; i < val.length; ++i) {
|
|
2094
|
-
if
|
|
2171
|
+
// if the attribute doesn't exist, returns false as well
|
|
2172
|
+
if (!val[i] || !val[i].includes(values[i])) return false;
|
|
2095
2173
|
}
|
|
2096
2174
|
return true;
|
|
2097
2175
|
});
|
|
@@ -2262,6 +2340,10 @@ class Playwright extends Helper {
|
|
|
2262
2340
|
test.artifacts[`trace_${sessionName}`] = await saveTraceForContext(this.sessionPages[sessionName].context, `${test.title}_${sessionName}.failed`);
|
|
2263
2341
|
}
|
|
2264
2342
|
}
|
|
2343
|
+
|
|
2344
|
+
if (this.options.recordHar) {
|
|
2345
|
+
test.artifacts.har = this.currentRunningTest.artifacts.har;
|
|
2346
|
+
}
|
|
2265
2347
|
}
|
|
2266
2348
|
|
|
2267
2349
|
async _passed(test) {
|
|
@@ -2289,6 +2371,10 @@ class Playwright extends Helper {
|
|
|
2289
2371
|
await this.browserContext.tracing.stop();
|
|
2290
2372
|
}
|
|
2291
2373
|
}
|
|
2374
|
+
|
|
2375
|
+
if (this.options.recordHar) {
|
|
2376
|
+
test.artifacts.har = this.currentRunningTest.artifacts.har;
|
|
2377
|
+
}
|
|
2292
2378
|
}
|
|
2293
2379
|
|
|
2294
2380
|
/**
|
|
@@ -2409,9 +2495,9 @@ class Playwright extends Helper {
|
|
|
2409
2495
|
}
|
|
2410
2496
|
|
|
2411
2497
|
/**
|
|
2412
|
-
* {{> waitForVisible }}
|
|
2413
|
-
*
|
|
2414
2498
|
* This method accepts [React selectors](https://codecept.io/react).
|
|
2499
|
+
*
|
|
2500
|
+
* {{> waitForVisible }}
|
|
2415
2501
|
*/
|
|
2416
2502
|
async waitForVisible(locator, sec) {
|
|
2417
2503
|
const waitTimeout = sec ? sec * 1000 : this.options.waitForTimeout;
|
|
@@ -2581,7 +2667,7 @@ class Playwright extends Helper {
|
|
|
2581
2667
|
const _contextObject = this.frame ? this.frame : contextObject;
|
|
2582
2668
|
let count = 0;
|
|
2583
2669
|
do {
|
|
2584
|
-
waiter = await _contextObject.locator(`:has-text(
|
|
2670
|
+
waiter = await _contextObject.locator(`:has-text("${text}")`).first().isVisible();
|
|
2585
2671
|
if (waiter) break;
|
|
2586
2672
|
await this.wait(1);
|
|
2587
2673
|
count += 1000;
|
|
@@ -3176,7 +3262,7 @@ class Playwright extends Helper {
|
|
|
3176
3262
|
/**
|
|
3177
3263
|
* Returns all URLs of all network requests recorded so far during execution of test scenario.
|
|
3178
3264
|
*
|
|
3179
|
-
* @return {string} List of URLs recorded as a string,
|
|
3265
|
+
* @return {string} List of URLs recorded as a string, separated by new lines after each URL
|
|
3180
3266
|
* @private
|
|
3181
3267
|
*/
|
|
3182
3268
|
_getTrafficDump() {
|
|
@@ -3358,6 +3444,7 @@ function buildLocatorString(locator) {
|
|
|
3358
3444
|
|
|
3359
3445
|
async function findElements(matcher, locator) {
|
|
3360
3446
|
if (locator.react) return findReact(matcher, locator);
|
|
3447
|
+
if (locator.vue) return findVue(matcher, locator);
|
|
3361
3448
|
locator = new Locator(locator, 'css');
|
|
3362
3449
|
|
|
3363
3450
|
return matcher.locator(buildLocatorString(locator)).all();
|
|
@@ -3419,6 +3506,7 @@ async function proceedClick(locator, context = null, options = {}) {
|
|
|
3419
3506
|
|
|
3420
3507
|
async function findClickable(matcher, locator) {
|
|
3421
3508
|
if (locator.react) return findReact(matcher, locator);
|
|
3509
|
+
if (locator.vue) return findVue(matcher, locator);
|
|
3422
3510
|
|
|
3423
3511
|
locator = new Locator(locator);
|
|
3424
3512
|
if (!locator.isFuzzy()) return findElements.call(this, matcher, locator);
|
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
|
}
|
|
@@ -297,7 +298,7 @@ class Puppeteer extends Helper {
|
|
|
297
298
|
this.sessionPages = {};
|
|
298
299
|
this.currentRunningTest = test;
|
|
299
300
|
recorder.retry({
|
|
300
|
-
retries: 3,
|
|
301
|
+
retries: process.env.FAILED_STEP_RETRIES || 3,
|
|
301
302
|
when: err => {
|
|
302
303
|
if (!err || typeof (err.message) !== 'string') {
|
|
303
304
|
return false;
|
|
@@ -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 // ...
|
|
@@ -917,6 +921,14 @@ class Puppeteer extends Helper {
|
|
|
917
921
|
return findFields.call(this, locator);
|
|
918
922
|
}
|
|
919
923
|
|
|
924
|
+
/**
|
|
925
|
+
* {{> grabWebElements }}
|
|
926
|
+
*
|
|
927
|
+
*/
|
|
928
|
+
async grabWebElements(locator) {
|
|
929
|
+
return this._locate(locator);
|
|
930
|
+
}
|
|
931
|
+
|
|
920
932
|
/**
|
|
921
933
|
* Switch focus to a particular tab by its number. It waits tabs loading and then switch tab
|
|
922
934
|
*
|
|
@@ -1111,7 +1123,7 @@ class Puppeteer extends Helper {
|
|
|
1111
1123
|
* Sets a directory to where save files. Allows to test file downloads.
|
|
1112
1124
|
* Should be used with [FileSystem helper](https://codecept.io/helpers/FileSystem) to check that file were downloaded correctly.
|
|
1113
1125
|
*
|
|
1114
|
-
* By default files are saved to `output/downloads`.
|
|
1126
|
+
* By default, files are saved to `output/downloads`.
|
|
1115
1127
|
* This directory is cleaned on every `handleDownloads` call, to ensure no old files are kept.
|
|
1116
1128
|
*
|
|
1117
1129
|
* ```js
|
|
@@ -1283,9 +1295,9 @@ class Puppeteer extends Helper {
|
|
|
1283
1295
|
}
|
|
1284
1296
|
|
|
1285
1297
|
/**
|
|
1286
|
-
* {{> pressKeyWithKeyNormalization }}
|
|
1287
|
-
*
|
|
1288
1298
|
* _Note:_ Shortcuts like `'Meta'` + `'A'` do not work on macOS ([GoogleChrome/puppeteer#1313](https://github.com/GoogleChrome/puppeteer/issues/1313)).
|
|
1299
|
+
*
|
|
1300
|
+
* {{> pressKeyWithKeyNormalization }}
|
|
1289
1301
|
*/
|
|
1290
1302
|
async pressKey(key) {
|
|
1291
1303
|
const modifiers = [];
|
|
@@ -1387,9 +1399,9 @@ class Puppeteer extends Helper {
|
|
|
1387
1399
|
}
|
|
1388
1400
|
|
|
1389
1401
|
/**
|
|
1390
|
-
* {{> attachFile }}
|
|
1391
|
-
*
|
|
1392
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 }}
|
|
1393
1405
|
*/
|
|
1394
1406
|
async attachFile(locator, pathToFile) {
|
|
1395
1407
|
const file = path.join(global.codecept_dir, pathToFile);
|
|
@@ -1620,9 +1632,9 @@ class Puppeteer extends Helper {
|
|
|
1620
1632
|
}
|
|
1621
1633
|
|
|
1622
1634
|
/**
|
|
1623
|
-
*
|
|
1635
|
+
* If a function returns a Promise, tt will wait for its resolution.
|
|
1624
1636
|
*
|
|
1625
|
-
*
|
|
1637
|
+
* {{> executeScript }}
|
|
1626
1638
|
*/
|
|
1627
1639
|
async executeScript(...args) {
|
|
1628
1640
|
let context = this.page;
|
|
@@ -1633,9 +1645,8 @@ class Puppeteer extends Helper {
|
|
|
1633
1645
|
}
|
|
1634
1646
|
|
|
1635
1647
|
/**
|
|
1636
|
-
* {{> executeAsyncScript }}
|
|
1637
|
-
*
|
|
1638
1648
|
* Asynchronous scripts can also be executed with `executeScript` if a function returns a Promise.
|
|
1649
|
+
* {{> executeAsyncScript }}
|
|
1639
1650
|
*/
|
|
1640
1651
|
async executeAsyncScript(...args) {
|
|
1641
1652
|
const asyncFn = function () {
|
|
@@ -1762,29 +1773,26 @@ class Puppeteer extends Helper {
|
|
|
1762
1773
|
|
|
1763
1774
|
const cssPropertiesCamelCase = convertCssPropertiesToCamelCase(cssProperties);
|
|
1764
1775
|
const elemAmount = res.length;
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
.
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
}));
|
|
1779
|
-
});
|
|
1780
|
-
});
|
|
1781
|
-
let props = await Promise.all(commands);
|
|
1776
|
+
let props = [];
|
|
1777
|
+
|
|
1778
|
+
for (const element of res) {
|
|
1779
|
+
for (const prop of Object.keys(cssProperties)) {
|
|
1780
|
+
const cssProp = await this.grabCssPropertyFrom(locator, prop);
|
|
1781
|
+
if (isColorProperty(prop)) {
|
|
1782
|
+
props.push(convertColorToRGBA(cssProp));
|
|
1783
|
+
} else {
|
|
1784
|
+
props.push(cssProp);
|
|
1785
|
+
}
|
|
1786
|
+
}
|
|
1787
|
+
}
|
|
1788
|
+
|
|
1782
1789
|
const values = Object.keys(cssPropertiesCamelCase).map(key => cssPropertiesCamelCase[key]);
|
|
1783
1790
|
if (!Array.isArray(props)) props = [props];
|
|
1784
1791
|
let chunked = chunkArray(props, values.length);
|
|
1785
1792
|
chunked = chunked.filter((val) => {
|
|
1786
1793
|
for (let i = 0; i < val.length; ++i) {
|
|
1787
|
-
|
|
1794
|
+
// eslint-disable-next-line eqeqeq
|
|
1795
|
+
if (val[i] != values[i]) return false;
|
|
1788
1796
|
}
|
|
1789
1797
|
return true;
|
|
1790
1798
|
});
|
|
@@ -1815,7 +1823,10 @@ class Puppeteer extends Helper {
|
|
|
1815
1823
|
let chunked = chunkArray(attrs, values.length);
|
|
1816
1824
|
chunked = chunked.filter((val) => {
|
|
1817
1825
|
for (let i = 0; i < val.length; ++i) {
|
|
1818
|
-
|
|
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;
|
|
1819
1830
|
}
|
|
1820
1831
|
return true;
|
|
1821
1832
|
});
|
|
@@ -2085,7 +2096,7 @@ class Puppeteer extends Helper {
|
|
|
2085
2096
|
/**
|
|
2086
2097
|
* {{> waitForVisible }}
|
|
2087
2098
|
*
|
|
2088
|
-
*
|
|
2099
|
+
* {{ react }}
|
|
2089
2100
|
*/
|
|
2090
2101
|
async waitForVisible(locator, sec) {
|
|
2091
2102
|
const waitTimeout = sec ? sec * 1000 : this.options.waitForTimeout;
|
|
@@ -2314,9 +2325,9 @@ class Puppeteer extends Helper {
|
|
|
2314
2325
|
}
|
|
2315
2326
|
|
|
2316
2327
|
/**
|
|
2317
|
-
* Waits for navigation to finish. By default takes configured `waitForNavigation` option.
|
|
2328
|
+
* Waits for navigation to finish. By default, takes configured `waitForNavigation` option.
|
|
2318
2329
|
*
|
|
2319
|
-
* See [
|
|
2330
|
+
* See [Puppeteer's reference](https://github.com/GoogleChrome/puppeteer/blob/master/docs/api.md#pagewaitfornavigationoptions)
|
|
2320
2331
|
*
|
|
2321
2332
|
* @param {*} opts
|
|
2322
2333
|
*/
|