codeceptjs 3.3.1 → 3.3.4
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 +49 -0
- package/README.md +4 -25
- package/docs/api.md +4 -0
- package/docs/basics.md +2 -0
- package/docs/build/Appium.js +18 -28
- package/docs/build/JSONResponse.js +44 -3
- package/docs/build/Nightmare.js +53 -53
- package/docs/build/Playwright.js +95 -103
- package/docs/build/Protractor.js +66 -66
- package/docs/build/Puppeteer.js +74 -74
- package/docs/build/REST.js +8 -4
- package/docs/build/TestCafe.js +53 -53
- package/docs/build/WebDriver.js +84 -86
- package/docs/changelog.md +49 -0
- package/docs/helpers/Appium.md +212 -268
- package/docs/helpers/JSONResponse.md +24 -0
- package/docs/helpers/Nightmare.md +92 -141
- package/docs/helpers/Playwright.md +302 -413
- package/docs/helpers/Puppeteer.md +171 -231
- package/docs/helpers/REST.md +2 -0
- package/docs/helpers/TestCafe.md +125 -174
- package/docs/helpers/WebDriver.md +184 -247
- package/docs/plugins.md +41 -1
- package/docs/secrets.md +30 -0
- package/docs/webapi/amOnPage.mustache +1 -1
- package/docs/webapi/appendField.mustache +1 -1
- package/docs/webapi/attachFile.mustache +1 -1
- package/docs/webapi/checkOption.mustache +1 -1
- package/docs/webapi/clearCookie.mustache +1 -1
- package/docs/webapi/clearField.mustache +1 -1
- package/docs/webapi/click.mustache +1 -1
- package/docs/webapi/clickLink.mustache +1 -1
- package/docs/webapi/closeCurrentTab.mustache +1 -1
- package/docs/webapi/closeOtherTabs.mustache +1 -1
- package/docs/webapi/dontSee.mustache +1 -1
- package/docs/webapi/dontSeeCheckboxIsChecked.mustache +1 -1
- package/docs/webapi/dontSeeCookie.mustache +1 -1
- package/docs/webapi/dontSeeCurrentUrlEquals.mustache +1 -1
- package/docs/webapi/dontSeeElement.mustache +1 -1
- package/docs/webapi/dontSeeElementInDOM.mustache +1 -1
- package/docs/webapi/dontSeeInCurrentUrl.mustache +1 -1
- package/docs/webapi/dontSeeInField.mustache +1 -1
- package/docs/webapi/dontSeeInSource.mustache +1 -1
- package/docs/webapi/dontSeeInTitle.mustache +1 -1
- package/docs/webapi/doubleClick.mustache +1 -1
- package/docs/webapi/downloadFile.mustache +1 -1
- package/docs/webapi/dragAndDrop.mustache +1 -1
- package/docs/webapi/dragSlider.mustache +1 -1
- package/docs/webapi/executeAsyncScript.mustache +1 -1
- package/docs/webapi/executeScript.mustache +1 -1
- package/docs/webapi/fillField.mustache +1 -1
- package/docs/webapi/forceClick.mustache +1 -1
- package/docs/webapi/forceRightClick.mustache +1 -1
- package/docs/webapi/grabDataFromPerformanceTiming.mustache +1 -1
- package/docs/webapi/moveCursorTo.mustache +1 -1
- package/docs/webapi/openNewTab.mustache +1 -1
- package/docs/webapi/pressKey.mustache +1 -1
- package/docs/webapi/pressKeyDown.mustache +1 -1
- package/docs/webapi/pressKeyUp.mustache +1 -1
- package/docs/webapi/pressKeyWithKeyNormalization.mustache +1 -1
- package/docs/webapi/refreshPage.mustache +1 -1
- package/docs/webapi/resizeWindow.mustache +1 -1
- package/docs/webapi/rightClick.mustache +1 -1
- package/docs/webapi/saveElementScreenshot.mustache +1 -1
- package/docs/webapi/saveScreenshot.mustache +1 -1
- package/docs/webapi/say.mustache +1 -1
- package/docs/webapi/scrollIntoView.mustache +1 -1
- package/docs/webapi/scrollPageToBottom.mustache +1 -1
- package/docs/webapi/scrollPageToTop.mustache +1 -1
- package/docs/webapi/scrollTo.mustache +1 -1
- package/docs/webapi/see.mustache +1 -1
- package/docs/webapi/seeAttributesOnElements.mustache +1 -1
- package/docs/webapi/seeCheckboxIsChecked.mustache +1 -1
- package/docs/webapi/seeCookie.mustache +1 -1
- package/docs/webapi/seeCssPropertiesOnElements.mustache +1 -1
- package/docs/webapi/seeCurrentUrlEquals.mustache +1 -1
- package/docs/webapi/seeElement.mustache +1 -1
- package/docs/webapi/seeElementInDOM.mustache +1 -1
- package/docs/webapi/seeInCurrentUrl.mustache +1 -1
- package/docs/webapi/seeInField.mustache +1 -1
- package/docs/webapi/seeInPopup.mustache +1 -1
- package/docs/webapi/seeInSource.mustache +1 -1
- package/docs/webapi/seeInTitle.mustache +1 -1
- package/docs/webapi/seeNumberOfElements.mustache +1 -1
- package/docs/webapi/seeNumberOfVisibleElements.mustache +1 -1
- package/docs/webapi/seeTextEquals.mustache +1 -1
- package/docs/webapi/seeTitleEquals.mustache +1 -1
- package/docs/webapi/selectOption.mustache +1 -1
- package/docs/webapi/setCookie.mustache +1 -1
- package/docs/webapi/setGeoLocation.mustache +1 -1
- package/docs/webapi/switchTo.mustache +1 -1
- package/docs/webapi/switchToNextTab.mustache +1 -1
- package/docs/webapi/switchToPreviousTab.mustache +1 -1
- package/docs/webapi/type.mustache +1 -1
- package/docs/webapi/uncheckOption.mustache +1 -1
- package/docs/webapi/wait.mustache +1 -1
- package/docs/webapi/waitForClickable.mustache +1 -1
- package/docs/webapi/waitForDetached.mustache +1 -1
- package/docs/webapi/waitForElement.mustache +1 -1
- package/docs/webapi/waitForEnabled.mustache +1 -1
- package/docs/webapi/waitForFunction.mustache +1 -1
- package/docs/webapi/waitForInvisible.mustache +1 -1
- package/docs/webapi/waitForText.mustache +1 -1
- package/docs/webapi/waitForValue.mustache +1 -1
- package/docs/webapi/waitForVisible.mustache +1 -1
- package/docs/webapi/waitInUrl.mustache +1 -1
- package/docs/webapi/waitNumberOfVisibleElements.mustache +1 -1
- package/docs/webapi/waitToHide.mustache +1 -1
- package/docs/webapi/waitUrlEquals.mustache +1 -1
- package/lib/cli.js +1 -1
- package/lib/command/interactive.js +1 -1
- package/lib/command/run-workers.js +1 -1
- package/lib/command/workers/runTests.js +15 -0
- package/lib/helper/Appium.js +0 -10
- package/lib/helper/JSONResponse.js +44 -3
- package/lib/helper/Playwright.js +24 -32
- package/lib/helper/REST.js +8 -4
- package/lib/helper/WebDriver.js +5 -7
- package/lib/output.js +4 -0
- package/lib/plugin/customLocator.js +50 -3
- package/lib/plugin/retryFailedStep.js +1 -1
- package/lib/plugin/retryTo.js +1 -8
- package/lib/secret.js +30 -0
- package/lib/step.js +1 -1
- package/package.json +4 -4
- package/typings/types.d.ts +1016 -520
|
@@ -8,4 +8,4 @@ I.waitForClickable('.btn.continue', 5); // wait for 5 secs
|
|
|
8
8
|
|
|
9
9
|
@param {CodeceptJS.LocatorOrString} locator element located by CSS|XPath|strict locator.
|
|
10
10
|
@param {number} [sec] (optional, `1` by default) time in seconds to wait
|
|
11
|
-
|
|
11
|
+
[!] returns a _promise_ which is synchronized internally by recorder
|
|
@@ -7,4 +7,4 @@ I.waitForDetached('#popup');
|
|
|
7
7
|
|
|
8
8
|
@param {CodeceptJS.LocatorOrString} locator element located by CSS|XPath|strict locator.
|
|
9
9
|
@param {number} [sec=1] (optional, `1` by default) time in seconds to wait
|
|
10
|
-
|
|
10
|
+
[!] returns a _promise_ which is synchronized internally by recorder
|
|
@@ -8,4 +8,4 @@ I.waitForElement('.btn.continue', 5); // wait for 5 secs
|
|
|
8
8
|
|
|
9
9
|
@param {CodeceptJS.LocatorOrString} locator element located by CSS|XPath|strict locator.
|
|
10
10
|
@param {number} [sec] (optional, `1` by default) time in seconds to wait
|
|
11
|
-
|
|
11
|
+
[!] returns a _promise_ which is synchronized internally by recorder
|
|
@@ -3,4 +3,4 @@ Element can be located by CSS or XPath.
|
|
|
3
3
|
|
|
4
4
|
@param {CodeceptJS.LocatorOrString} locator element located by CSS|XPath|strict locator.
|
|
5
5
|
@param {number} [sec=1] (optional) time in seconds to wait, 1 by default.
|
|
6
|
-
|
|
6
|
+
[!] returns a _promise_ which is synchronized internally by recorder
|
|
@@ -14,4 +14,4 @@ I.waitForFunction((count) => window.requests == count, [3], 5) // pass args and
|
|
|
14
14
|
@param {string|function} fn to be executed in browser context.
|
|
15
15
|
@param {any[]|number} [argsOrSec] (optional, `1` by default) arguments for function or seconds.
|
|
16
16
|
@param {number} [sec] (optional, `1` by default) time in seconds to wait
|
|
17
|
-
|
|
17
|
+
[!] returns a _promise_ which is synchronized internally by recorder
|
|
@@ -7,4 +7,4 @@ I.waitForInvisible('#popup');
|
|
|
7
7
|
|
|
8
8
|
@param {CodeceptJS.LocatorOrString} locator element located by CSS|XPath|strict locator.
|
|
9
9
|
@param {number} [sec=1] (optional, `1` by default) time in seconds to wait
|
|
10
|
-
|
|
10
|
+
[!] returns a _promise_ which is synchronized internally by recorder
|
|
@@ -10,4 +10,4 @@ I.waitForText('Thank you, form has been submitted', 5, '#modal');
|
|
|
10
10
|
@param {string }text to wait for.
|
|
11
11
|
@param {number} [sec=1] (optional, `1` by default) time in seconds to wait
|
|
12
12
|
@param {CodeceptJS.LocatorOrString} [context] (optional) element located by CSS|XPath|strict locator.
|
|
13
|
-
|
|
13
|
+
[!] returns a _promise_ which is synchronized internally by recorder
|
|
@@ -7,4 +7,4 @@ I.waitForValue('//input', "GoodValue");
|
|
|
7
7
|
@param {LocatorOrString} field input field.
|
|
8
8
|
@param {string }value expected value.
|
|
9
9
|
@param {number} [sec=1] (optional, `1` by default) time in seconds to wait
|
|
10
|
-
|
|
10
|
+
[!] returns a _promise_ which is synchronized internally by recorder
|
|
@@ -7,4 +7,4 @@ I.waitForVisible('#popup');
|
|
|
7
7
|
|
|
8
8
|
@param {CodeceptJS.LocatorOrString} locator element located by CSS|XPath|strict locator.
|
|
9
9
|
@param {number} [sec=1] (optional, `1` by default) time in seconds to wait
|
|
10
|
-
|
|
10
|
+
[!] returns a _promise_ which is synchronized internally by recorder
|
|
@@ -7,4 +7,4 @@ I.waitNumberOfVisibleElements('a', 3);
|
|
|
7
7
|
@param {CodeceptJS.LocatorOrString} locator element located by CSS|XPath|strict locator.
|
|
8
8
|
@param {number} num number of elements.
|
|
9
9
|
@param {number} [sec=1] (optional, `1` by default) time in seconds to wait
|
|
10
|
-
|
|
10
|
+
[!] returns a _promise_ which is synchronized internally by recorder
|
|
@@ -7,4 +7,4 @@ I.waitToHide('#popup');
|
|
|
7
7
|
|
|
8
8
|
@param {CodeceptJS.LocatorOrString} locator element located by CSS|XPath|strict locator.
|
|
9
9
|
@param {number} [sec=1] (optional, `1` by default) time in seconds to wait
|
|
10
|
-
|
|
10
|
+
[!] returns a _promise_ which is synchronized internally by recorder
|
package/lib/cli.js
CHANGED
|
@@ -19,7 +19,7 @@ class Cli extends Base {
|
|
|
19
19
|
if (opts.debug) level = 2;
|
|
20
20
|
if (opts.verbose) level = 3;
|
|
21
21
|
output.level(level);
|
|
22
|
-
output.print(`CodeceptJS v${require('./codecept').version()}`);
|
|
22
|
+
output.print(`CodeceptJS v${require('./codecept').version()} ${output.standWithUkraine()}`);
|
|
23
23
|
output.print(`Using test root "${global.codecept_dir}"`);
|
|
24
24
|
|
|
25
25
|
const showSteps = level >= 1;
|
|
@@ -21,7 +21,7 @@ module.exports = async function (path, options) {
|
|
|
21
21
|
|
|
22
22
|
if (options.verbose) output.level(3);
|
|
23
23
|
|
|
24
|
-
output.print('
|
|
24
|
+
output.print('Starting interactive shell for current suite...');
|
|
25
25
|
recorder.start();
|
|
26
26
|
event.emit(event.suite.before, {
|
|
27
27
|
fullTitle: () => 'Interactive Shell',
|
|
@@ -25,7 +25,7 @@ module.exports = async function (workerCount, options) {
|
|
|
25
25
|
|
|
26
26
|
const numberOfWorkers = parseInt(workerCount, 10);
|
|
27
27
|
|
|
28
|
-
output.print(`CodeceptJS v${require('../codecept').version()}`);
|
|
28
|
+
output.print(`CodeceptJS v${require('../codecept').version()} ${output.standWithUkraine()}`);
|
|
29
29
|
output.print(`Running tests in ${output.styles.bold(numberOfWorkers)} workers...`);
|
|
30
30
|
output.print();
|
|
31
31
|
|
|
@@ -114,6 +114,13 @@ function initializeListeners() {
|
|
|
114
114
|
parent.title = test.parent.title;
|
|
115
115
|
}
|
|
116
116
|
|
|
117
|
+
if (test.opts) {
|
|
118
|
+
Object.keys(test.opts).forEach(k => {
|
|
119
|
+
if (typeof test.opts[k] === 'object') delete test.opts[k];
|
|
120
|
+
if (typeof test.opts[k] === 'function') delete test.opts[k];
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
|
|
117
124
|
return {
|
|
118
125
|
opts: test.opts || {},
|
|
119
126
|
tags: test.tags || [],
|
|
@@ -148,6 +155,14 @@ function initializeListeners() {
|
|
|
148
155
|
if (step.metaStep) {
|
|
149
156
|
parent.title = step.metaStep.actor;
|
|
150
157
|
}
|
|
158
|
+
|
|
159
|
+
if (step.opts) {
|
|
160
|
+
Object.keys(step.opts).forEach(k => {
|
|
161
|
+
if (typeof step.opts[k] === 'object') delete step.opts[k];
|
|
162
|
+
if (typeof step.opts[k] === 'function') delete step.opts[k];
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
|
|
151
166
|
return {
|
|
152
167
|
opts: step.opts || {},
|
|
153
168
|
workerIndex,
|
package/lib/helper/Appium.js
CHANGED
|
@@ -332,7 +332,6 @@ class Appium extends Webdriver {
|
|
|
332
332
|
*
|
|
333
333
|
* @param {*} caps
|
|
334
334
|
* @param {*} fn
|
|
335
|
-
* @return {Promise<any>}
|
|
336
335
|
*/
|
|
337
336
|
async runOnIOS(caps, fn) {
|
|
338
337
|
if (this.platform !== 'ios') return;
|
|
@@ -375,7 +374,6 @@ class Appium extends Webdriver {
|
|
|
375
374
|
*
|
|
376
375
|
* @param {*} caps
|
|
377
376
|
* @param {*} fn
|
|
378
|
-
* @return {Promise<any>}
|
|
379
377
|
*/
|
|
380
378
|
async runOnAndroid(caps, fn) {
|
|
381
379
|
if (this.platform !== 'android') return;
|
|
@@ -396,7 +394,6 @@ class Appium extends Webdriver {
|
|
|
396
394
|
* ```
|
|
397
395
|
*
|
|
398
396
|
* @param {*} fn
|
|
399
|
-
* @return {Promise<any>}
|
|
400
397
|
*/
|
|
401
398
|
/* eslint-disable */
|
|
402
399
|
async runInWeb(fn) {
|
|
@@ -493,7 +490,6 @@ class Appium extends Webdriver {
|
|
|
493
490
|
*
|
|
494
491
|
* @param {string} appId
|
|
495
492
|
* @param {string} [bundleId] ID of bundle
|
|
496
|
-
* @return {Promise<any>}
|
|
497
493
|
*/
|
|
498
494
|
async removeApp(appId, bundleId) {
|
|
499
495
|
onlyForApps.call(this, 'Android');
|
|
@@ -601,7 +597,6 @@ class Appium extends Webdriver {
|
|
|
601
597
|
* ```
|
|
602
598
|
*
|
|
603
599
|
* @param {'LANDSCAPE'|'PORTRAIT'} orientation LANDSCAPE or PORTRAIT
|
|
604
|
-
* @return {Promise<any>}
|
|
605
600
|
*
|
|
606
601
|
* Appium: support Android and iOS
|
|
607
602
|
*/
|
|
@@ -730,7 +725,6 @@ class Appium extends Webdriver {
|
|
|
730
725
|
* Switch to the specified context.
|
|
731
726
|
*
|
|
732
727
|
* @param {*} context the context to switch to
|
|
733
|
-
* @return {Promise<any>}
|
|
734
728
|
*/
|
|
735
729
|
async _switchToContext(context) {
|
|
736
730
|
return this.browser.switchContext(context);
|
|
@@ -836,7 +830,6 @@ class Appium extends Webdriver {
|
|
|
836
830
|
* ```
|
|
837
831
|
*
|
|
838
832
|
* @param {object} settings object
|
|
839
|
-
* @return {Promise<any>}
|
|
840
833
|
*
|
|
841
834
|
* Appium: support Android and iOS
|
|
842
835
|
*/
|
|
@@ -861,7 +854,6 @@ class Appium extends Webdriver {
|
|
|
861
854
|
*
|
|
862
855
|
* @param {'tapOutside' | 'pressKey'} [strategy] Desired strategy to close keyboard (‘tapOutside’ or ‘pressKey’)
|
|
863
856
|
* @param {string} [key] Optional key
|
|
864
|
-
* @return {Promise<any>}
|
|
865
857
|
*/
|
|
866
858
|
async hideDeviceKeyboard(strategy, key) {
|
|
867
859
|
onlyForApps.call(this);
|
|
@@ -984,7 +976,6 @@ class Appium extends Webdriver {
|
|
|
984
976
|
*
|
|
985
977
|
* @param {object} from
|
|
986
978
|
* @param {object} to
|
|
987
|
-
* @return {Promise<any>}
|
|
988
979
|
*
|
|
989
980
|
* Appium: support Android and iOS
|
|
990
981
|
*/
|
|
@@ -1217,7 +1208,6 @@ class Appium extends Webdriver {
|
|
|
1217
1208
|
* Appium: support Android and iOS
|
|
1218
1209
|
*
|
|
1219
1210
|
* @param {Array} actions Array of touch actions
|
|
1220
|
-
* @return {Promise<any>}
|
|
1221
1211
|
*/
|
|
1222
1212
|
async touchPerform(actions) {
|
|
1223
1213
|
onlyForApps.call(this);
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
const assert = require('assert');
|
|
1
2
|
const chai = require('chai');
|
|
2
3
|
const joi = require('joi');
|
|
3
4
|
const chaiDeepMatch = require('chai-deep-match');
|
|
@@ -173,12 +174,30 @@ class JSONResponse extends Helper {
|
|
|
173
174
|
*
|
|
174
175
|
* I.seeResponseContainsJson({ user: { email: 'jon@doe.com' } });
|
|
175
176
|
* ```
|
|
177
|
+
* If an array is received, checks that at least one element contains JSON
|
|
178
|
+
* ```js
|
|
179
|
+
* // response.data == [{ user: { name: 'jon', email: 'jon@doe.com' } }]
|
|
180
|
+
*
|
|
181
|
+
* I.seeResponseContainsJson({ user: { email: 'jon@doe.com' } });
|
|
182
|
+
* ```
|
|
176
183
|
*
|
|
177
184
|
* @param {object} json
|
|
178
185
|
*/
|
|
179
186
|
seeResponseContainsJson(json = {}) {
|
|
180
187
|
this._checkResponseReady();
|
|
181
|
-
|
|
188
|
+
if (Array.isArray(this.response.data)) {
|
|
189
|
+
let fails = 0;
|
|
190
|
+
for (const el of this.response.data) {
|
|
191
|
+
try {
|
|
192
|
+
expect(el).to.deep.match(json);
|
|
193
|
+
} catch (err) {
|
|
194
|
+
fails++;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
assert.ok(fails < this.response.data.length, `No elements in array matched ${JSON.stringify(json)}`);
|
|
198
|
+
} else {
|
|
199
|
+
expect(this.response.data).to.deep.match(json);
|
|
200
|
+
}
|
|
182
201
|
}
|
|
183
202
|
|
|
184
203
|
/**
|
|
@@ -189,12 +208,22 @@ class JSONResponse extends Helper {
|
|
|
189
208
|
*
|
|
190
209
|
* I.dontSeeResponseContainsJson({ user: 2 });
|
|
191
210
|
* ```
|
|
211
|
+
* If an array is received, checks that no element of array contains json:
|
|
212
|
+
* ```js
|
|
213
|
+
* // response.data == [{ user: 1 }, { user: 3 }]
|
|
214
|
+
*
|
|
215
|
+
* I.dontSeeResponseContainsJson({ user: 2 });
|
|
216
|
+
* ```
|
|
192
217
|
*
|
|
193
218
|
* @param {object} json
|
|
194
219
|
*/
|
|
195
220
|
dontSeeResponseContainsJson(json = {}) {
|
|
196
221
|
this._checkResponseReady();
|
|
197
|
-
|
|
222
|
+
if (Array.isArray(this.response.data)) {
|
|
223
|
+
this.response.data.forEach(data => expect(data).not.to.deep.match(json));
|
|
224
|
+
} else {
|
|
225
|
+
expect(this.response.data).not.to.deep.match(json);
|
|
226
|
+
}
|
|
198
227
|
}
|
|
199
228
|
|
|
200
229
|
/**
|
|
@@ -206,11 +235,23 @@ class JSONResponse extends Helper {
|
|
|
206
235
|
* I.seeResponseContainsKeys(['user']);
|
|
207
236
|
* ```
|
|
208
237
|
*
|
|
238
|
+
* If an array is received, check is performed for each element of array:
|
|
239
|
+
*
|
|
240
|
+
* ```js
|
|
241
|
+
* // response.data == [{ user: 'jon' }, { user: 'matt'}]
|
|
242
|
+
*
|
|
243
|
+
* I.seeResponseContainsKeys(['user']);
|
|
244
|
+
* ```
|
|
245
|
+
*
|
|
209
246
|
* @param {array} keys
|
|
210
247
|
*/
|
|
211
248
|
seeResponseContainsKeys(keys = []) {
|
|
212
249
|
this._checkResponseReady();
|
|
213
|
-
|
|
250
|
+
if (Array.isArray(this.response.data)) {
|
|
251
|
+
this.response.data.forEach(data => expect(data).to.include.keys(keys));
|
|
252
|
+
} else {
|
|
253
|
+
expect(this.response.data).to.include.keys(keys);
|
|
254
|
+
}
|
|
214
255
|
}
|
|
215
256
|
|
|
216
257
|
/**
|
package/lib/helper/Playwright.js
CHANGED
|
@@ -167,7 +167,8 @@ const { createValueEngine, createDisabledEngine } = require('./extras/Playwright
|
|
|
167
167
|
* Playwright: {
|
|
168
168
|
* url: "http://localhost",
|
|
169
169
|
* chromium: {
|
|
170
|
-
* browserWSEndpoint: 'ws://localhost:9222/devtools/browser/c5aa6160-b5bc-4d53-bb49-6ecb36cd2e0a'
|
|
170
|
+
* browserWSEndpoint: 'ws://localhost:9222/devtools/browser/c5aa6160-b5bc-4d53-bb49-6ecb36cd2e0a',
|
|
171
|
+
* cdpConnection: false // default is false
|
|
171
172
|
* }
|
|
172
173
|
* }
|
|
173
174
|
* }
|
|
@@ -272,6 +273,7 @@ class Playwright extends Helper {
|
|
|
272
273
|
this.sessionPages = {};
|
|
273
274
|
this.activeSessionName = '';
|
|
274
275
|
this.isElectron = false;
|
|
276
|
+
this.isCDPConnection = false;
|
|
275
277
|
this.electronSessions = [];
|
|
276
278
|
this.storageState = null;
|
|
277
279
|
|
|
@@ -347,6 +349,7 @@ class Playwright extends Helper {
|
|
|
347
349
|
this.isRemoteBrowser = !!this.playwrightOptions.browserWSEndpoint;
|
|
348
350
|
this.isElectron = this.options.browser === 'electron';
|
|
349
351
|
this.userDataDir = this.playwrightOptions.userDataDir;
|
|
352
|
+
this.isCDPConnection = this.playwrightOptions.cdpConnection;
|
|
350
353
|
popupStore.defaultAction = this.options.defaultPopupAction;
|
|
351
354
|
}
|
|
352
355
|
|
|
@@ -553,8 +556,7 @@ class Playwright extends Helper {
|
|
|
553
556
|
* ```
|
|
554
557
|
*
|
|
555
558
|
* @param {string} description used to show in logs.
|
|
556
|
-
* @param {function} fn async function that executed with Playwright helper as
|
|
557
|
-
* @return {Promise<any>}
|
|
559
|
+
* @param {function} fn async function that executed with Playwright helper as argumen
|
|
558
560
|
*/
|
|
559
561
|
usePlaywrightTo(description, fn) {
|
|
560
562
|
return this._useTo(...arguments);
|
|
@@ -569,7 +571,6 @@ class Playwright extends Helper {
|
|
|
569
571
|
* I.click('#triggerPopup');
|
|
570
572
|
* I.acceptPopup();
|
|
571
573
|
* ```
|
|
572
|
-
* @return {Promise<any>}
|
|
573
574
|
*/
|
|
574
575
|
amAcceptingPopups() {
|
|
575
576
|
popupStore.actionType = 'accept';
|
|
@@ -579,7 +580,6 @@ class Playwright extends Helper {
|
|
|
579
580
|
* Accepts the active JavaScript native popup window, as created by window.alert|window.confirm|window.prompt.
|
|
580
581
|
* Don't confuse popups with modal windows, as created by [various
|
|
581
582
|
* libraries](http://jster.net/category/windows-modals-popups).
|
|
582
|
-
* @return {Promise<any>}
|
|
583
583
|
*/
|
|
584
584
|
acceptPopup() {
|
|
585
585
|
popupStore.assertPopupActionType('accept');
|
|
@@ -594,7 +594,6 @@ class Playwright extends Helper {
|
|
|
594
594
|
* I.click('#triggerPopup');
|
|
595
595
|
* I.cancelPopup();
|
|
596
596
|
* ```
|
|
597
|
-
* @return {Promise<any>}
|
|
598
597
|
*/
|
|
599
598
|
amCancellingPopups() {
|
|
600
599
|
popupStore.actionType = 'cancel';
|
|
@@ -602,7 +601,6 @@ class Playwright extends Helper {
|
|
|
602
601
|
|
|
603
602
|
/**
|
|
604
603
|
* Dismisses the active JavaScript popup, as created by window.alert|window.confirm|window.prompt.
|
|
605
|
-
* @return {Promise<any>}
|
|
606
604
|
*/
|
|
607
605
|
cancelPopup() {
|
|
608
606
|
popupStore.assertPopupActionType('cancel');
|
|
@@ -620,7 +618,6 @@ class Playwright extends Helper {
|
|
|
620
618
|
/**
|
|
621
619
|
* Set current page
|
|
622
620
|
* @param {object} page page to set
|
|
623
|
-
* @return {Promise<any>}
|
|
624
621
|
*/
|
|
625
622
|
async _setPage(page) {
|
|
626
623
|
page = await page;
|
|
@@ -645,7 +642,6 @@ class Playwright extends Helper {
|
|
|
645
642
|
/**
|
|
646
643
|
* Add the 'dialog' event listener to a page
|
|
647
644
|
* @page {playwright.Page}
|
|
648
|
-
* @return {Promise<any>}
|
|
649
645
|
*
|
|
650
646
|
* The popup listener handles the dialog with the predefined action when it appears on the page.
|
|
651
647
|
* It also saves a reference to the object which is used in seeInPopup.
|
|
@@ -676,7 +672,6 @@ class Playwright extends Helper {
|
|
|
676
672
|
|
|
677
673
|
/**
|
|
678
674
|
* Gets page URL including hash.
|
|
679
|
-
* @return {Promise<any>}
|
|
680
675
|
*/
|
|
681
676
|
async _getPageUrl() {
|
|
682
677
|
return this.executeScript(() => window.location.href);
|
|
@@ -700,6 +695,15 @@ class Playwright extends Helper {
|
|
|
700
695
|
async _startBrowser() {
|
|
701
696
|
if (this.isElectron) {
|
|
702
697
|
this.browser = await playwright._electron.launch(this.playwrightOptions);
|
|
698
|
+
} else if (this.isRemoteBrowser && this.isCDPConnection) {
|
|
699
|
+
try {
|
|
700
|
+
this.browser = await playwright[this.options.browser].connectOverCDP(this.playwrightOptions);
|
|
701
|
+
} catch (err) {
|
|
702
|
+
if (err.toString().indexOf('ECONNREFUSED')) {
|
|
703
|
+
throw new RemoteBrowserConnectionRefused(err);
|
|
704
|
+
}
|
|
705
|
+
throw err;
|
|
706
|
+
}
|
|
703
707
|
} else if (this.isRemoteBrowser) {
|
|
704
708
|
try {
|
|
705
709
|
this.browser = await playwright[this.options.browser].connect(this.playwrightOptions);
|
|
@@ -849,7 +853,6 @@ class Playwright extends Helper {
|
|
|
849
853
|
* ```
|
|
850
854
|
*
|
|
851
855
|
* @param {object} customHeaders headers to set
|
|
852
|
-
* @return {Promise<any>}
|
|
853
856
|
*/
|
|
854
857
|
async haveRequestHeaders(customHeaders) {
|
|
855
858
|
if (!customHeaders) {
|
|
@@ -996,7 +999,6 @@ class Playwright extends Helper {
|
|
|
996
999
|
* ```js
|
|
997
1000
|
* const elements = await this.helpers['Playwright']._locate({name: 'password'});
|
|
998
1001
|
* ```
|
|
999
|
-
* @return {Promise<any>}
|
|
1000
1002
|
*/
|
|
1001
1003
|
async _locate(locator) {
|
|
1002
1004
|
const context = await this.context || await this._getContext();
|
|
@@ -1010,7 +1012,6 @@ class Playwright extends Helper {
|
|
|
1010
1012
|
* ```js
|
|
1011
1013
|
* this.helpers['Playwright']._locateCheckable('I agree with terms and conditions').then // ...
|
|
1012
1014
|
* ```
|
|
1013
|
-
* @return {Promise<any>}
|
|
1014
1015
|
*/
|
|
1015
1016
|
async _locateCheckable(locator, providedContext = null) {
|
|
1016
1017
|
const context = providedContext || await this._getContext();
|
|
@@ -1025,7 +1026,6 @@ class Playwright extends Helper {
|
|
|
1025
1026
|
* ```js
|
|
1026
1027
|
* this.helpers['Playwright']._locateClickable('Next page').then // ...
|
|
1027
1028
|
* ```
|
|
1028
|
-
* @return {Promise<any>}
|
|
1029
1029
|
*/
|
|
1030
1030
|
async _locateClickable(locator) {
|
|
1031
1031
|
const context = await this._getContext();
|
|
@@ -1038,7 +1038,6 @@ class Playwright extends Helper {
|
|
|
1038
1038
|
* ```js
|
|
1039
1039
|
* this.helpers['Playwright']._locateFields('Your email').then // ...
|
|
1040
1040
|
* ```
|
|
1041
|
-
* @return {Promise<any>}
|
|
1042
1041
|
*/
|
|
1043
1042
|
async _locateFields(locator) {
|
|
1044
1043
|
return findFields.call(this, locator);
|
|
@@ -1053,7 +1052,6 @@ class Playwright extends Helper {
|
|
|
1053
1052
|
* ```
|
|
1054
1053
|
*
|
|
1055
1054
|
* @param {number} [num=1]
|
|
1056
|
-
* @return {Promise<any>}
|
|
1057
1055
|
*/
|
|
1058
1056
|
async switchToNextTab(num = 1) {
|
|
1059
1057
|
if (this.isElectron) {
|
|
@@ -1068,6 +1066,7 @@ class Playwright extends Helper {
|
|
|
1068
1066
|
if (!page) {
|
|
1069
1067
|
throw new Error(`There is no ability to switch to next tab with offset ${num}`);
|
|
1070
1068
|
}
|
|
1069
|
+
targetCreatedHandler.call(this, page);
|
|
1071
1070
|
await this._setPage(page);
|
|
1072
1071
|
return this._waitForAction();
|
|
1073
1072
|
}
|
|
@@ -1080,7 +1079,6 @@ class Playwright extends Helper {
|
|
|
1080
1079
|
* I.switchToPreviousTab(2);
|
|
1081
1080
|
* ```
|
|
1082
1081
|
* @param {number} [num=1]
|
|
1083
|
-
* @return {Promise<any>}
|
|
1084
1082
|
*/
|
|
1085
1083
|
async switchToPreviousTab(num = 1) {
|
|
1086
1084
|
if (this.isElectron) {
|
|
@@ -1105,7 +1103,6 @@ class Playwright extends Helper {
|
|
|
1105
1103
|
* ```js
|
|
1106
1104
|
* I.closeCurrentTab();
|
|
1107
1105
|
* ```
|
|
1108
|
-
* @return {Promise<any>}
|
|
1109
1106
|
*/
|
|
1110
1107
|
async closeCurrentTab() {
|
|
1111
1108
|
if (this.isElectron) {
|
|
@@ -1123,7 +1120,6 @@ class Playwright extends Helper {
|
|
|
1123
1120
|
* ```js
|
|
1124
1121
|
* I.closeOtherTabs();
|
|
1125
1122
|
* ```
|
|
1126
|
-
* @return {Promise<any>}
|
|
1127
1123
|
*/
|
|
1128
1124
|
async closeOtherTabs() {
|
|
1129
1125
|
const pages = await this.browserContext.pages();
|
|
@@ -1148,13 +1144,14 @@ class Playwright extends Helper {
|
|
|
1148
1144
|
* // enable mobile
|
|
1149
1145
|
* I.openNewTab({ isMobile: true });
|
|
1150
1146
|
* ```
|
|
1151
|
-
* @return {Promise<any>}
|
|
1152
1147
|
*/
|
|
1153
1148
|
async openNewTab(options) {
|
|
1154
1149
|
if (this.isElectron) {
|
|
1155
1150
|
throw new Error('Cannot open new tabs inside an Electron container');
|
|
1156
1151
|
}
|
|
1157
|
-
|
|
1152
|
+
const page = await this.browserContext.newPage(options);
|
|
1153
|
+
targetCreatedHandler.call(this, page);
|
|
1154
|
+
await this._setPage(page);
|
|
1158
1155
|
return this._waitForAction();
|
|
1159
1156
|
}
|
|
1160
1157
|
|
|
@@ -1255,7 +1252,6 @@ class Playwright extends Helper {
|
|
|
1255
1252
|
|
|
1256
1253
|
/**
|
|
1257
1254
|
* Clicks link and waits for navigation (deprecated)
|
|
1258
|
-
* @return {Promise<any>}
|
|
1259
1255
|
*/
|
|
1260
1256
|
async clickLink(locator, context = null) {
|
|
1261
1257
|
console.log('clickLink deprecated: Playwright automatically waits for navigation to happen.');
|
|
@@ -1577,9 +1573,11 @@ class Playwright extends Helper {
|
|
|
1577
1573
|
* Get JS log from browser.
|
|
1578
1574
|
*
|
|
1579
1575
|
* ```js
|
|
1580
|
-
*
|
|
1581
|
-
*
|
|
1576
|
+
* const logs = await I.grabBrowserLogs();
|
|
1577
|
+
* const errors = logs.map(l => ({ type: l.type(), text: l.text() })).filter(l => l.type === 'error');
|
|
1578
|
+
* console.log(JSON.stringify(errors));
|
|
1582
1579
|
* ```
|
|
1580
|
+
* [Learn more about console messages](https://playwright.dev/docs/api/class-consolemessage)
|
|
1583
1581
|
* @return {Promise<any[]>}
|
|
1584
1582
|
*/
|
|
1585
1583
|
async grabBrowserLogs() {
|
|
@@ -1702,7 +1700,6 @@ class Playwright extends Helper {
|
|
|
1702
1700
|
*
|
|
1703
1701
|
* @param {string|function} fn function to be executed in browser context.
|
|
1704
1702
|
* @param {any} [arg] optional argument to pass to the function
|
|
1705
|
-
* @return {Promise<any>}
|
|
1706
1703
|
*/
|
|
1707
1704
|
async executeScript(fn, arg) {
|
|
1708
1705
|
let context = this.page;
|
|
@@ -2303,7 +2300,6 @@ class Playwright extends Helper {
|
|
|
2303
2300
|
*
|
|
2304
2301
|
* @param {string|function} urlOrPredicate
|
|
2305
2302
|
* @param {?number} [sec=null] seconds to wait
|
|
2306
|
-
* @return {Promise<any>}
|
|
2307
2303
|
*/
|
|
2308
2304
|
async waitForRequest(urlOrPredicate, sec = null) {
|
|
2309
2305
|
const timeout = sec ? sec * 1000 : this.options.waitForTimeout;
|
|
@@ -2320,7 +2316,6 @@ class Playwright extends Helper {
|
|
|
2320
2316
|
*
|
|
2321
2317
|
* @param {string|function} urlOrPredicate
|
|
2322
2318
|
* @param {?number} [sec=null] number of seconds to wait
|
|
2323
|
-
* @return {Promise<any>}
|
|
2324
2319
|
*/
|
|
2325
2320
|
async waitForResponse(urlOrPredicate, sec = null) {
|
|
2326
2321
|
const timeout = sec ? sec * 1000 : this.options.waitForTimeout;
|
|
@@ -2392,7 +2387,6 @@ class Playwright extends Helper {
|
|
|
2392
2387
|
* See [Playwright's reference](https://playwright.dev/docs/api/class-page?_highlight=waitfornavi#pagewaitfornavigationoptions)
|
|
2393
2388
|
*
|
|
2394
2389
|
* @param {*} opts
|
|
2395
|
-
* @return {Promise<any>}
|
|
2396
2390
|
*/
|
|
2397
2391
|
async waitForNavigation(opts = {}) {
|
|
2398
2392
|
opts = {
|
|
@@ -2464,8 +2458,7 @@ class Playwright extends Helper {
|
|
|
2464
2458
|
* This method allows intercepting and mocking requests & responses. [Learn more about it](https://playwright.dev/docs/network#handle-requests)
|
|
2465
2459
|
*
|
|
2466
2460
|
* @param {string|RegExp} [url] URL, regex or pattern for to match URL
|
|
2467
|
-
* @param {function} [handler] a function to process
|
|
2468
|
-
* @return {Promise<any>}
|
|
2461
|
+
* @param {function} [handler] a function to process reques
|
|
2469
2462
|
*/
|
|
2470
2463
|
async mockRoute(url, handler) {
|
|
2471
2464
|
return this.browserContext.route(...arguments);
|
|
@@ -2481,8 +2474,7 @@ class Playwright extends Helper {
|
|
|
2481
2474
|
* If no handler is passed, all mock requests for the rote are disabled.
|
|
2482
2475
|
*
|
|
2483
2476
|
* @param {string|RegExp} [url] URL, regex or pattern for to match URL
|
|
2484
|
-
* @param {function} [handler] a function to process
|
|
2485
|
-
* @return {Promise<any>}
|
|
2477
|
+
* @param {function} [handler] a function to process reques
|
|
2486
2478
|
*/
|
|
2487
2479
|
async stopMockingRoute(url, handler) {
|
|
2488
2480
|
return this.browserContext.unroute(...arguments);
|
package/lib/helper/REST.js
CHANGED
|
@@ -2,6 +2,7 @@ const axios = require('axios').default;
|
|
|
2
2
|
const Secret = require('../secret');
|
|
3
3
|
|
|
4
4
|
const Helper = require('../helper');
|
|
5
|
+
const { beautify } = require('../utils');
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
8
|
* REST helper allows to send additional requests to the REST API during acceptance tests.
|
|
@@ -10,6 +11,7 @@ const Helper = require('../helper');
|
|
|
10
11
|
* ## Configuration
|
|
11
12
|
*
|
|
12
13
|
* * endpoint: API base URL
|
|
14
|
+
* * prettyPrintJson: pretty print json for response/request on console logs
|
|
13
15
|
* * timeout: timeout for requests in milliseconds. 10000ms by default
|
|
14
16
|
* * defaultHeaders: a list of default headers
|
|
15
17
|
* * onRequest: a async function which can update request object.
|
|
@@ -22,6 +24,7 @@ const Helper = require('../helper');
|
|
|
22
24
|
* helpers: {
|
|
23
25
|
* REST: {
|
|
24
26
|
* endpoint: 'http://site.com/api',
|
|
27
|
+
* prettyPrintJson: true,
|
|
25
28
|
* onRequest: (request) => {
|
|
26
29
|
* request.headers.auth = '123';
|
|
27
30
|
* }
|
|
@@ -49,6 +52,7 @@ class REST extends Helper {
|
|
|
49
52
|
timeout: 10000,
|
|
50
53
|
defaultHeaders: {},
|
|
51
54
|
endpoint: '',
|
|
55
|
+
prettyPrintJson: false,
|
|
52
56
|
};
|
|
53
57
|
|
|
54
58
|
if (this.options.maxContentLength) {
|
|
@@ -87,7 +91,7 @@ class REST extends Helper {
|
|
|
87
91
|
* @param {object} headers headers list
|
|
88
92
|
*/
|
|
89
93
|
haveRequestHeaders(headers) {
|
|
90
|
-
this.headers = { ...headers, ...
|
|
94
|
+
this.headers = { ...this.headers, ...headers };
|
|
91
95
|
}
|
|
92
96
|
|
|
93
97
|
/**
|
|
@@ -113,7 +117,7 @@ class REST extends Helper {
|
|
|
113
117
|
*/
|
|
114
118
|
async _executeRequest(request) {
|
|
115
119
|
// Add custom headers. They can be set by amBearerAuthenticated() or haveRequestHeaders()
|
|
116
|
-
request.headers = { ...
|
|
120
|
+
request.headers = { ...this.headers, ...request.headers };
|
|
117
121
|
|
|
118
122
|
const _debugRequest = { ...request };
|
|
119
123
|
this.axios.defaults.timeout = request.timeout || this.options.timeout;
|
|
@@ -137,7 +141,7 @@ class REST extends Helper {
|
|
|
137
141
|
await this.config.onRequest(request);
|
|
138
142
|
}
|
|
139
143
|
|
|
140
|
-
this.debugSection('Request', JSON.stringify(_debugRequest));
|
|
144
|
+
this.options.prettyPrintJson ? this.debugSection('Request', beautify(JSON.stringify(_debugRequest))) : this.debugSection('Request', JSON.stringify(_debugRequest));
|
|
141
145
|
|
|
142
146
|
let response;
|
|
143
147
|
try {
|
|
@@ -150,7 +154,7 @@ class REST extends Helper {
|
|
|
150
154
|
if (this.config.onResponse) {
|
|
151
155
|
await this.config.onResponse(response);
|
|
152
156
|
}
|
|
153
|
-
this.debugSection('Response', JSON.stringify(response.data));
|
|
157
|
+
this.options.prettyPrintJson ? this.debugSection('Response', beautify(JSON.stringify(response.data))) : this.debugSection('Response', JSON.stringify(response.data));
|
|
154
158
|
return response;
|
|
155
159
|
}
|
|
156
160
|
|