codeceptjs 3.0.3 → 3.0.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +114 -18
- package/bin/codecept.js +1 -0
- package/docs/basics.md +2 -2
- package/docs/bdd.md +12 -1
- package/docs/build/Appium.js +2 -1
- package/docs/build/GraphQL.js +9 -10
- package/docs/build/Nightmare.js +4 -5
- package/docs/build/Playwright.js +164 -37
- package/docs/build/Protractor.js +1 -1
- package/docs/build/Puppeteer.js +1 -1
- package/docs/build/REST.js +24 -4
- package/docs/build/TestCafe.js +1 -1
- package/docs/build/WebDriver.js +85 -17
- package/docs/changelog.md +114 -18
- package/docs/data.md +5 -5
- package/docs/detox.md +2 -2
- package/docs/docker.md +11 -11
- package/docs/email.md +8 -8
- package/docs/helpers/Appium.md +1 -1
- package/docs/helpers/Nightmare.md +4 -5
- package/docs/helpers/Playwright.md +94 -64
- package/docs/helpers/Protractor.md +1 -1
- package/docs/helpers/Puppeteer.md +1 -1
- package/docs/helpers/REST.md +9 -0
- package/docs/helpers/TestCafe.md +1 -1
- package/docs/helpers/WebDriver.md +2 -1
- package/docs/locators.md +29 -2
- package/docs/mobile-react-native-locators.md +2 -2
- package/docs/mobile.md +3 -3
- package/docs/nightmare.md +0 -5
- package/docs/pageobjects.md +3 -1
- package/docs/parallel.md +35 -10
- package/docs/playwright.md +55 -8
- package/docs/plugins.md +73 -29
- package/docs/reports.md +8 -7
- package/docs/typescript.md +47 -5
- package/docs/webapi/fillField.mustache +1 -1
- package/lib/cli.js +25 -10
- package/lib/codecept.js +9 -1
- package/lib/command/interactive.js +10 -9
- package/lib/command/run.js +1 -1
- package/lib/command/workers/runTests.js +11 -6
- package/lib/config.js +8 -3
- package/lib/event.js +2 -0
- package/lib/helper/Appium.js +1 -0
- package/lib/helper/GraphQL.js +9 -10
- package/lib/helper/Nightmare.js +1 -1
- package/lib/helper/Playwright.js +131 -38
- package/lib/helper/REST.js +24 -4
- package/lib/helper/WebDriver.js +84 -16
- package/lib/interfaces/gherkin.js +11 -4
- package/lib/output.js +7 -4
- package/lib/plugin/allure.js +3 -7
- package/lib/plugin/fakerTransform.js +51 -0
- package/lib/plugin/screenshotOnFail.js +6 -2
- package/lib/recorder.js +9 -0
- package/lib/step.js +2 -1
- package/lib/transform.js +26 -0
- package/lib/ui.js +6 -2
- package/lib/within.js +1 -1
- package/lib/workers.js +39 -25
- package/package.json +14 -9
- package/typings/index.d.ts +49 -21
- package/typings/types.d.ts +72 -26
package/lib/helper/Playwright.js
CHANGED
|
@@ -35,9 +35,10 @@ let defaultSelectorEnginesInitialized = false;
|
|
|
35
35
|
|
|
36
36
|
const popupStore = new Popup();
|
|
37
37
|
const consoleLogStore = new Console();
|
|
38
|
-
const availableBrowsers = ['chromium', 'webkit', 'firefox'];
|
|
38
|
+
const availableBrowsers = ['chromium', 'webkit', 'firefox', 'electron'];
|
|
39
39
|
|
|
40
40
|
const { createValueEngine, createDisabledEngine } = require('./extras/PlaywrightPropEngine');
|
|
41
|
+
|
|
41
42
|
/**
|
|
42
43
|
* Uses [Playwright](https://github.com/microsoft/playwright) library to run tests inside:
|
|
43
44
|
*
|
|
@@ -58,7 +59,7 @@ const { createValueEngine, createDisabledEngine } = require('./extras/Playwright
|
|
|
58
59
|
* This helper should be configured in codecept.json or codecept.conf.js
|
|
59
60
|
*
|
|
60
61
|
* * `url`: base url of website to be tested
|
|
61
|
-
* * `browser`: a browser to test on, either: `chromium`, `firefox`, `webkit`. Default: chromium.
|
|
62
|
+
* * `browser`: a browser to test on, either: `chromium`, `firefox`, `webkit`, `electron`. Default: chromium.
|
|
62
63
|
* * `show`: (optional, default: false) - show browser window.
|
|
63
64
|
* * `restart`: (optional, default: true) - restart browser between tests.
|
|
64
65
|
* * `disableScreenshots`: (optional, default: false) - don't save screenshot on failure.
|
|
@@ -77,6 +78,7 @@ const { createValueEngine, createDisabledEngine } = require('./extras/Playwright
|
|
|
77
78
|
* * `userAgent`: (optional) user-agent string.
|
|
78
79
|
* * `manualStart`: (optional, default: false) - do not start browser before a test, start it manually inside a helper with `this.helpers["Playwright"]._startBrowser()`.
|
|
79
80
|
* * `chromium`: (optional) pass additional chromium options
|
|
81
|
+
* * `electron`: (optional) pass additional electron options
|
|
80
82
|
*
|
|
81
83
|
* #### Example #1: Wait for 0 network connections.
|
|
82
84
|
*
|
|
@@ -121,7 +123,7 @@ const { createValueEngine, createDisabledEngine } = require('./extras/Playwright
|
|
|
121
123
|
* }
|
|
122
124
|
* ```
|
|
123
125
|
*
|
|
124
|
-
* #### Example #4: Connect to remote browser by specifying [websocket endpoint](https://
|
|
126
|
+
* #### Example #4: Connect to remote browser by specifying [websocket endpoint](https://playwright.dev/docs/api/class-browsertype#browsertypeconnectparams)
|
|
125
127
|
*
|
|
126
128
|
* ```js
|
|
127
129
|
* {
|
|
@@ -129,7 +131,7 @@ const { createValueEngine, createDisabledEngine } = require('./extras/Playwright
|
|
|
129
131
|
* Playwright: {
|
|
130
132
|
* url: "http://localhost",
|
|
131
133
|
* chromium: {
|
|
132
|
-
* browserWSEndpoint:
|
|
134
|
+
* browserWSEndpoint: { wsEndpoint: 'ws://localhost:9222/devtools/browser/c5aa6160-b5bc-4d53-bb49-6ecb36cd2e0a' }
|
|
133
135
|
* }
|
|
134
136
|
* }
|
|
135
137
|
* }
|
|
@@ -147,6 +149,7 @@ const { createValueEngine, createDisabledEngine } = require('./extras/Playwright
|
|
|
147
149
|
* url: "http://localhost",
|
|
148
150
|
* show: true // headless mode not supported for extensions
|
|
149
151
|
* chromium: {
|
|
152
|
+
* userDataDir: '/tmp/playwright-tmp', // necessary to launch the browser in normal mode instead of incognito,
|
|
150
153
|
* args: [
|
|
151
154
|
* `--disable-extensions-except=${pathToExtension}`,
|
|
152
155
|
* `--load-extension=${pathToExtension}`
|
|
@@ -206,6 +209,8 @@ class Playwright extends Helper {
|
|
|
206
209
|
this.isAuthenticated = false;
|
|
207
210
|
this.sessionPages = {};
|
|
208
211
|
this.activeSessionName = '';
|
|
212
|
+
this.isElectron = false;
|
|
213
|
+
this.electronSessions = [];
|
|
209
214
|
|
|
210
215
|
// override defaults with config
|
|
211
216
|
this._setConfig(config);
|
|
@@ -257,6 +262,8 @@ class Playwright extends Helper {
|
|
|
257
262
|
...this._getOptionsForBrowser(config),
|
|
258
263
|
};
|
|
259
264
|
this.isRemoteBrowser = !!this.playwrightOptions.browserWSEndpoint;
|
|
265
|
+
this.isElectron = this.options.browser === 'electron';
|
|
266
|
+
this.userDataDir = this.playwrightOptions.userDataDir;
|
|
260
267
|
popupStore.defaultAction = this.options.defaultPopupAction;
|
|
261
268
|
}
|
|
262
269
|
|
|
@@ -268,7 +275,7 @@ class Playwright extends Helper {
|
|
|
268
275
|
},
|
|
269
276
|
{
|
|
270
277
|
name: 'browser',
|
|
271
|
-
message: 'Browser in which testing will be performed. Possible options: chromium, firefox or
|
|
278
|
+
message: 'Browser in which testing will be performed. Possible options: chromium, firefox, webkit or electron',
|
|
272
279
|
default: 'chromium',
|
|
273
280
|
},
|
|
274
281
|
];
|
|
@@ -320,11 +327,21 @@ class Playwright extends Helper {
|
|
|
320
327
|
async _after() {
|
|
321
328
|
if (!this.isRunning) return;
|
|
322
329
|
|
|
330
|
+
if (this.isElectron) {
|
|
331
|
+
this.browser.close();
|
|
332
|
+
this.electronSessions.forEach(session => session.close());
|
|
333
|
+
return;
|
|
334
|
+
}
|
|
335
|
+
|
|
323
336
|
// close other sessions
|
|
324
|
-
|
|
325
|
-
|
|
337
|
+
try {
|
|
338
|
+
const contexts = await this.browser.contexts();
|
|
339
|
+
contexts.shift();
|
|
326
340
|
|
|
327
|
-
|
|
341
|
+
await Promise.all(contexts.map(c => c.close()));
|
|
342
|
+
} catch (e) {
|
|
343
|
+
console.log(e);
|
|
344
|
+
}
|
|
328
345
|
|
|
329
346
|
if (this.options.restart) {
|
|
330
347
|
this.isRunning = false;
|
|
@@ -371,12 +388,22 @@ class Playwright extends Helper {
|
|
|
371
388
|
this.debugSection('New Context', config ? JSON.stringify(config) : 'opened');
|
|
372
389
|
this.activeSessionName = sessionName;
|
|
373
390
|
|
|
374
|
-
|
|
375
|
-
|
|
391
|
+
let browserContext;
|
|
392
|
+
let page;
|
|
393
|
+
if (this.isElectron) {
|
|
394
|
+
const browser = await playwright._electron.launch(this.playwrightOptions);
|
|
395
|
+
this.electronSessions.push(browser);
|
|
396
|
+
browserContext = browser.context();
|
|
397
|
+
page = await browser.firstWindow();
|
|
398
|
+
} else {
|
|
399
|
+
browserContext = await this.browser.newContext(config);
|
|
400
|
+
page = await browserContext.newPage();
|
|
401
|
+
}
|
|
402
|
+
|
|
376
403
|
targetCreatedHandler.call(this, page);
|
|
377
404
|
this._setPage(page);
|
|
378
405
|
// Create a new page inside context.
|
|
379
|
-
return
|
|
406
|
+
return browserContext;
|
|
380
407
|
},
|
|
381
408
|
stop: async () => {
|
|
382
409
|
// is closed by _after
|
|
@@ -489,6 +516,7 @@ class Playwright extends Helper {
|
|
|
489
516
|
if (!page) return;
|
|
490
517
|
page.setDefaultNavigationTimeout(this.options.getPageTimeout);
|
|
491
518
|
this.context = await this.page;
|
|
519
|
+
this.contextLocator = null;
|
|
492
520
|
if (this.config.browser === 'chrome') {
|
|
493
521
|
await page.bringToFront();
|
|
494
522
|
}
|
|
@@ -547,15 +575,19 @@ class Playwright extends Helper {
|
|
|
547
575
|
}
|
|
548
576
|
|
|
549
577
|
async _startBrowser() {
|
|
550
|
-
if (this.
|
|
578
|
+
if (this.isElectron) {
|
|
579
|
+
this.browser = await playwright._electron.launch(this.playwrightOptions);
|
|
580
|
+
} else if (this.isRemoteBrowser) {
|
|
551
581
|
try {
|
|
552
|
-
this.browser = await playwright[this.options.browser].connect(this.playwrightOptions);
|
|
582
|
+
this.browser = await playwright[this.options.browser].connect(this.playwrightOptions.browserWSEndpoint);
|
|
553
583
|
} catch (err) {
|
|
554
584
|
if (err.toString().indexOf('ECONNREFUSED')) {
|
|
555
585
|
throw new RemoteBrowserConnectionRefused(err);
|
|
556
586
|
}
|
|
557
587
|
throw err;
|
|
558
588
|
}
|
|
589
|
+
} else if (this.userDataDir) {
|
|
590
|
+
this.browser = await playwright[this.options.browser].launchPersistentContext(this.userDataDir, this.playwrightOptions);
|
|
559
591
|
} else {
|
|
560
592
|
this.browser = await playwright[this.options.browser].launch(this.playwrightOptions);
|
|
561
593
|
}
|
|
@@ -564,11 +596,22 @@ class Playwright extends Helper {
|
|
|
564
596
|
this.browser.on('targetchanged', (target) => {
|
|
565
597
|
this.debugSection('Url', target.url());
|
|
566
598
|
});
|
|
567
|
-
this.browserContext = await this.browser.newContext({ ignoreHTTPSErrors: this.options.ignoreHTTPSErrors, acceptDownloads: true, ...this.options.emulate });// Adding the HTTPSError ignore in the context so that we can ignore those errors
|
|
568
599
|
|
|
569
|
-
|
|
600
|
+
if (this.isElectron) {
|
|
601
|
+
this.browserContext = this.browser.context();
|
|
602
|
+
} else if (this.userDataDir) {
|
|
603
|
+
this.browserContext = this.browser;
|
|
604
|
+
} else {
|
|
605
|
+
this.browserContext = await this.browser.newContext({ ignoreHTTPSErrors: this.options.ignoreHTTPSErrors, acceptDownloads: true, ...this.options.emulate });// Adding the HTTPSError ignore in the context so that we can ignore those errors
|
|
606
|
+
}
|
|
570
607
|
|
|
571
|
-
|
|
608
|
+
let mainPage;
|
|
609
|
+
if (this.isElectron) {
|
|
610
|
+
mainPage = await this.browser.firstWindow();
|
|
611
|
+
} else {
|
|
612
|
+
const existingPages = await this.browserContext.pages();
|
|
613
|
+
mainPage = existingPages[0] || await this.browserContext.newPage();
|
|
614
|
+
}
|
|
572
615
|
targetCreatedHandler.call(this, mainPage);
|
|
573
616
|
|
|
574
617
|
await this._setPage(mainPage);
|
|
@@ -577,6 +620,10 @@ class Playwright extends Helper {
|
|
|
577
620
|
this.isRunning = true;
|
|
578
621
|
}
|
|
579
622
|
|
|
623
|
+
_getType() {
|
|
624
|
+
return this.browser._type;
|
|
625
|
+
}
|
|
626
|
+
|
|
580
627
|
async _stopBrowser() {
|
|
581
628
|
this.withinLocator = null;
|
|
582
629
|
this._setPage(null);
|
|
@@ -611,6 +658,7 @@ class Playwright extends Helper {
|
|
|
611
658
|
const els = await this._locate(locator);
|
|
612
659
|
assertElementExists(els, locator);
|
|
613
660
|
this.context = els[0];
|
|
661
|
+
this.contextLocator = locator;
|
|
614
662
|
|
|
615
663
|
this.withinLocator = new Locator(locator);
|
|
616
664
|
}
|
|
@@ -618,6 +666,7 @@ class Playwright extends Helper {
|
|
|
618
666
|
async _withinEnd() {
|
|
619
667
|
this.withinLocator = null;
|
|
620
668
|
this.context = await this.page;
|
|
669
|
+
this.contextLocator = null;
|
|
621
670
|
}
|
|
622
671
|
|
|
623
672
|
_extractDataFromPerformanceTiming(timing, ...dataNames) {
|
|
@@ -635,6 +684,9 @@ class Playwright extends Helper {
|
|
|
635
684
|
* {{> amOnPage }}
|
|
636
685
|
*/
|
|
637
686
|
async amOnPage(url) {
|
|
687
|
+
if (this.isElectron) {
|
|
688
|
+
throw new Error('Cannot open pages inside an Electron container');
|
|
689
|
+
}
|
|
638
690
|
if (!(/^\w+\:\/\//.test(url))) {
|
|
639
691
|
url = this.options.url + url;
|
|
640
692
|
}
|
|
@@ -800,11 +852,7 @@ class Playwright extends Helper {
|
|
|
800
852
|
}
|
|
801
853
|
|
|
802
854
|
/**
|
|
803
|
-
*
|
|
804
|
-
*
|
|
805
|
-
* ```js
|
|
806
|
-
* I.seeTitleEquals('Test title.');
|
|
807
|
-
* ```
|
|
855
|
+
* {{> seeTitleEquals }}
|
|
808
856
|
*/
|
|
809
857
|
async seeTitleEquals(text) {
|
|
810
858
|
const title = await this.page.title();
|
|
@@ -890,6 +938,9 @@ class Playwright extends Helper {
|
|
|
890
938
|
* @param {number} [num=1]
|
|
891
939
|
*/
|
|
892
940
|
async switchToNextTab(num = 1) {
|
|
941
|
+
if (this.isElectron) {
|
|
942
|
+
throw new Error('Cannot switch tabs inside an Electron container');
|
|
943
|
+
}
|
|
893
944
|
const pages = await this.browserContext.pages();
|
|
894
945
|
|
|
895
946
|
const index = pages.indexOf(this.page);
|
|
@@ -913,6 +964,9 @@ class Playwright extends Helper {
|
|
|
913
964
|
* @param {number} [num=1]
|
|
914
965
|
*/
|
|
915
966
|
async switchToPreviousTab(num = 1) {
|
|
967
|
+
if (this.isElectron) {
|
|
968
|
+
throw new Error('Cannot switch tabs inside an Electron container');
|
|
969
|
+
}
|
|
916
970
|
const pages = await this.browserContext.pages();
|
|
917
971
|
const index = pages.indexOf(this.page);
|
|
918
972
|
this.withinLocator = null;
|
|
@@ -934,6 +988,9 @@ class Playwright extends Helper {
|
|
|
934
988
|
* ```
|
|
935
989
|
*/
|
|
936
990
|
async closeCurrentTab() {
|
|
991
|
+
if (this.isElectron) {
|
|
992
|
+
throw new Error('Cannot close current tab inside an Electron container');
|
|
993
|
+
}
|
|
937
994
|
const oldPage = this.page;
|
|
938
995
|
await this.switchToPreviousTab();
|
|
939
996
|
await oldPage.close();
|
|
@@ -972,6 +1029,9 @@ class Playwright extends Helper {
|
|
|
972
1029
|
* ```
|
|
973
1030
|
*/
|
|
974
1031
|
async openNewTab(options) {
|
|
1032
|
+
if (this.isElectron) {
|
|
1033
|
+
throw new Error('Cannot open new tabs inside an Electron container');
|
|
1034
|
+
}
|
|
975
1035
|
await this._setPage(await this.browserContext.newPage(options));
|
|
976
1036
|
return this._waitForAction();
|
|
977
1037
|
}
|
|
@@ -1069,14 +1129,7 @@ class Playwright extends Helper {
|
|
|
1069
1129
|
}
|
|
1070
1130
|
|
|
1071
1131
|
/**
|
|
1072
|
-
*
|
|
1073
|
-
* Force clicks an element without waiting for it to become visible and not animating.
|
|
1074
|
-
*
|
|
1075
|
-
* ```js
|
|
1076
|
-
* I.forceClick('#hiddenButton');
|
|
1077
|
-
* I.forceClick('Click me', '#hidden');
|
|
1078
|
-
* ```
|
|
1079
|
-
*
|
|
1132
|
+
* {{> forceClick }}
|
|
1080
1133
|
*/
|
|
1081
1134
|
async forceClick(locator, context = null) {
|
|
1082
1135
|
return proceedClick.call(this, locator, context, { force: true });
|
|
@@ -1500,6 +1553,10 @@ class Playwright extends Helper {
|
|
|
1500
1553
|
* I.executeScript(([x, y]) => x + y, [x, y]);
|
|
1501
1554
|
* ```
|
|
1502
1555
|
* If a function returns a Promise it will wait for its resolution.
|
|
1556
|
+
*
|
|
1557
|
+
* @param {string|function} fn function to be executed in browser context.
|
|
1558
|
+
* @param {any} [arg] optional argument to pass to the function
|
|
1559
|
+
* @return {Promise<any>}
|
|
1503
1560
|
*/
|
|
1504
1561
|
async executeScript(fn, arg) {
|
|
1505
1562
|
let context = this.page;
|
|
@@ -1509,15 +1566,32 @@ class Playwright extends Helper {
|
|
|
1509
1566
|
return context.evaluate.apply(context, [fn, arg]);
|
|
1510
1567
|
}
|
|
1511
1568
|
|
|
1569
|
+
/**
|
|
1570
|
+
* Grab Locator if called within Context
|
|
1571
|
+
*
|
|
1572
|
+
* @param {*} locator
|
|
1573
|
+
*/
|
|
1574
|
+
_contextLocator(locator) {
|
|
1575
|
+
locator = buildLocatorString(new Locator(locator, 'css'));
|
|
1576
|
+
|
|
1577
|
+
if (this.contextLocator) {
|
|
1578
|
+
const contextLocator = buildLocatorString(new Locator(this.contextLocator, 'css'));
|
|
1579
|
+
locator = `${contextLocator} >> ${locator}`;
|
|
1580
|
+
}
|
|
1581
|
+
|
|
1582
|
+
return locator;
|
|
1583
|
+
}
|
|
1584
|
+
|
|
1512
1585
|
/**
|
|
1513
1586
|
* {{> grabTextFrom }}
|
|
1514
1587
|
*
|
|
1515
1588
|
*/
|
|
1516
1589
|
async grabTextFrom(locator) {
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1590
|
+
locator = this._contextLocator(locator);
|
|
1591
|
+
const text = await this.page.textContent(locator);
|
|
1592
|
+
assertElementExists(text, locator);
|
|
1593
|
+
this.debugSection('Text', text);
|
|
1594
|
+
return text;
|
|
1521
1595
|
}
|
|
1522
1596
|
|
|
1523
1597
|
/**
|
|
@@ -1590,8 +1664,7 @@ class Playwright extends Helper {
|
|
|
1590
1664
|
async grabCssPropertyFromAll(locator, cssProperty) {
|
|
1591
1665
|
const els = await this._locate(locator);
|
|
1592
1666
|
this.debug(`Matched ${els.length} elements`);
|
|
1593
|
-
const
|
|
1594
|
-
const cssValues = res.map(props => props[toCamelCase(cssProperty)]);
|
|
1667
|
+
const cssValues = await Promise.all(els.map(el => el.$eval('xpath=.', (el, cssProperty) => getComputedStyle(el).getPropertyValue(cssProperty), cssProperty)));
|
|
1595
1668
|
|
|
1596
1669
|
return cssValues;
|
|
1597
1670
|
}
|
|
@@ -2041,6 +2114,7 @@ class Playwright extends Helper {
|
|
|
2041
2114
|
|
|
2042
2115
|
if (locator >= 0 && locator < childFrames.length) {
|
|
2043
2116
|
this.context = childFrames[locator];
|
|
2117
|
+
this.contextLocator = locator;
|
|
2044
2118
|
} else {
|
|
2045
2119
|
throw new Error('Element #invalidIframeSelector was not found by text|CSS|XPath');
|
|
2046
2120
|
}
|
|
@@ -2048,6 +2122,7 @@ class Playwright extends Helper {
|
|
|
2048
2122
|
}
|
|
2049
2123
|
if (!locator) {
|
|
2050
2124
|
this.context = this.page;
|
|
2125
|
+
this.contextLocator = null;
|
|
2051
2126
|
return;
|
|
2052
2127
|
}
|
|
2053
2128
|
|
|
@@ -2058,8 +2133,10 @@ class Playwright extends Helper {
|
|
|
2058
2133
|
|
|
2059
2134
|
if (contentFrame) {
|
|
2060
2135
|
this.context = contentFrame;
|
|
2136
|
+
this.contextLocator = null;
|
|
2061
2137
|
} else {
|
|
2062
2138
|
this.context = els[0];
|
|
2139
|
+
this.contextLocator = locator;
|
|
2063
2140
|
}
|
|
2064
2141
|
}
|
|
2065
2142
|
|
|
@@ -2083,7 +2160,7 @@ class Playwright extends Helper {
|
|
|
2083
2160
|
/**
|
|
2084
2161
|
* Waits for navigation to finish. By default takes configured `waitForNavigation` option.
|
|
2085
2162
|
*
|
|
2086
|
-
* See [
|
|
2163
|
+
* See [Playwright's reference](https://playwright.dev/docs/api/class-page?_highlight=waitfornavi#pagewaitfornavigationoptions)
|
|
2087
2164
|
*
|
|
2088
2165
|
* @param {*} opts
|
|
2089
2166
|
*/
|
|
@@ -2176,6 +2253,19 @@ async function findElements(matcher, locator) {
|
|
|
2176
2253
|
return matcher.$$(buildLocatorString(locator));
|
|
2177
2254
|
}
|
|
2178
2255
|
|
|
2256
|
+
async function getVisibleElements(elements) {
|
|
2257
|
+
const visibleElements = [];
|
|
2258
|
+
for (const element of elements) {
|
|
2259
|
+
if (await element.isVisible()) {
|
|
2260
|
+
visibleElements.push(element);
|
|
2261
|
+
}
|
|
2262
|
+
}
|
|
2263
|
+
if (visibleElements.length === 0) {
|
|
2264
|
+
return elements;
|
|
2265
|
+
}
|
|
2266
|
+
return visibleElements;
|
|
2267
|
+
}
|
|
2268
|
+
|
|
2179
2269
|
async function proceedClick(locator, context = null, options = {}) {
|
|
2180
2270
|
let matcher = await this._getContext();
|
|
2181
2271
|
if (context) {
|
|
@@ -2195,7 +2285,8 @@ async function proceedClick(locator, context = null, options = {}) {
|
|
|
2195
2285
|
if (options.force) {
|
|
2196
2286
|
await els[0].dispatchEvent('click');
|
|
2197
2287
|
} else {
|
|
2198
|
-
await els[0]
|
|
2288
|
+
const element = els.length > 1 ? (await getVisibleElements(els))[0] : els[0];
|
|
2289
|
+
await element.click(options);
|
|
2199
2290
|
}
|
|
2200
2291
|
const promises = [];
|
|
2201
2292
|
if (options.waitForNavigation) {
|
|
@@ -2453,11 +2544,13 @@ async function targetCreatedHandler(page) {
|
|
|
2453
2544
|
// we are inside iframe?
|
|
2454
2545
|
const frameEl = await this.context.frameElement();
|
|
2455
2546
|
this.context = await frameEl.contentFrame();
|
|
2547
|
+
this.contextLocator = null;
|
|
2456
2548
|
return;
|
|
2457
2549
|
}
|
|
2458
2550
|
// if context element was in iframe - keep it
|
|
2459
2551
|
// if (await this.context.ownerFrame()) return;
|
|
2460
2552
|
this.context = page;
|
|
2553
|
+
this.contextLocator = null;
|
|
2461
2554
|
});
|
|
2462
2555
|
});
|
|
2463
2556
|
page.on('console', (msg) => {
|
|
@@ -2468,7 +2561,7 @@ async function targetCreatedHandler(page) {
|
|
|
2468
2561
|
if (this.options.userAgent) {
|
|
2469
2562
|
await page.setUserAgent(this.options.userAgent);
|
|
2470
2563
|
}
|
|
2471
|
-
if (this.options.windowSize && this.options.windowSize.indexOf('x') > 0) {
|
|
2564
|
+
if (this.options.windowSize && this.options.windowSize.indexOf('x') > 0 && this._getType() === 'Browser') {
|
|
2472
2565
|
const dimensions = this.options.windowSize.split('x');
|
|
2473
2566
|
const width = parseInt(dimensions[0], 10);
|
|
2474
2567
|
const height = parseInt(dimensions[1], 10);
|
package/lib/helper/REST.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
const axios = require('axios').default;
|
|
2
|
+
const Secret = require('../secret');
|
|
2
3
|
|
|
3
4
|
const Helper = require('../helper');
|
|
4
5
|
|
|
@@ -58,7 +59,8 @@ class REST extends Helper {
|
|
|
58
59
|
|
|
59
60
|
this.options = { ...this.options, ...config };
|
|
60
61
|
this.headers = { ...this.options.defaultHeaders };
|
|
61
|
-
axios
|
|
62
|
+
this.axios = axios.create();
|
|
63
|
+
this.axios.defaults.headers = this.options.defaultHeaders;
|
|
62
64
|
}
|
|
63
65
|
|
|
64
66
|
static _checkRequirements() {
|
|
@@ -75,12 +77,18 @@ class REST extends Helper {
|
|
|
75
77
|
* @param {*} request
|
|
76
78
|
*/
|
|
77
79
|
async _executeRequest(request) {
|
|
78
|
-
|
|
80
|
+
const _debugRequest = { ...request };
|
|
81
|
+
this.axios.defaults.timeout = request.timeout || this.options.timeout;
|
|
79
82
|
|
|
80
83
|
if (this.headers && this.headers.auth) {
|
|
81
84
|
request.auth = this.headers.auth;
|
|
82
85
|
}
|
|
83
86
|
|
|
87
|
+
if (request.data instanceof Secret) {
|
|
88
|
+
_debugRequest.data = '*****';
|
|
89
|
+
request.data = typeof request.data === 'object' ? { ...request.data.toString() } : request.data.toString();
|
|
90
|
+
}
|
|
91
|
+
|
|
84
92
|
if ((typeof request.data) === 'string') {
|
|
85
93
|
if (!request.headers || !request.headers['Content-Type']) {
|
|
86
94
|
request.headers = { ...request.headers, ...{ 'Content-Type': 'application/x-www-form-urlencoded' } };
|
|
@@ -91,11 +99,11 @@ class REST extends Helper {
|
|
|
91
99
|
await this.config.onRequest(request);
|
|
92
100
|
}
|
|
93
101
|
|
|
94
|
-
this.debugSection('Request', JSON.stringify(
|
|
102
|
+
this.debugSection('Request', JSON.stringify(_debugRequest));
|
|
95
103
|
|
|
96
104
|
let response;
|
|
97
105
|
try {
|
|
98
|
-
response = await axios(request);
|
|
106
|
+
response = await this.axios(request);
|
|
99
107
|
} catch (err) {
|
|
100
108
|
if (!err.response) throw err;
|
|
101
109
|
this.debugSection('Response', `Response error. Status code: ${err.response.status}`);
|
|
@@ -149,6 +157,10 @@ class REST extends Helper {
|
|
|
149
157
|
*
|
|
150
158
|
* ```js
|
|
151
159
|
* I.sendPostRequest('/api/users.json', { "email": "user@user.com" });
|
|
160
|
+
*
|
|
161
|
+
* // To mask the payload in logs
|
|
162
|
+
* I.sendPostRequest('/api/users.json', secret({ "email": "user@user.com" }));
|
|
163
|
+
*
|
|
152
164
|
* ```
|
|
153
165
|
*
|
|
154
166
|
* @param {*} url
|
|
@@ -176,6 +188,10 @@ class REST extends Helper {
|
|
|
176
188
|
*
|
|
177
189
|
* ```js
|
|
178
190
|
* I.sendPatchRequest('/api/users.json', { "email": "user@user.com" });
|
|
191
|
+
*
|
|
192
|
+
* // To mask the payload in logs
|
|
193
|
+
* I.sendPatchRequest('/api/users.json', secret({ "email": "user@user.com" }));
|
|
194
|
+
*
|
|
179
195
|
* ```
|
|
180
196
|
*
|
|
181
197
|
* @param {string} url
|
|
@@ -203,6 +219,10 @@ class REST extends Helper {
|
|
|
203
219
|
*
|
|
204
220
|
* ```js
|
|
205
221
|
* I.sendPutRequest('/api/users.json', { "email": "user@user.com" });
|
|
222
|
+
*
|
|
223
|
+
* // To mask the payload in logs
|
|
224
|
+
* I.sendPutRequest('/api/users.json', secret({ "email": "user@user.com" }));
|
|
225
|
+
*
|
|
206
226
|
* ```
|
|
207
227
|
*
|
|
208
228
|
* @param {string} url
|