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.
Files changed (126) hide show
  1. package/CHANGELOG.md +49 -0
  2. package/README.md +4 -25
  3. package/docs/api.md +4 -0
  4. package/docs/basics.md +2 -0
  5. package/docs/build/Appium.js +18 -28
  6. package/docs/build/JSONResponse.js +44 -3
  7. package/docs/build/Nightmare.js +53 -53
  8. package/docs/build/Playwright.js +95 -103
  9. package/docs/build/Protractor.js +66 -66
  10. package/docs/build/Puppeteer.js +74 -74
  11. package/docs/build/REST.js +8 -4
  12. package/docs/build/TestCafe.js +53 -53
  13. package/docs/build/WebDriver.js +84 -86
  14. package/docs/changelog.md +49 -0
  15. package/docs/helpers/Appium.md +212 -268
  16. package/docs/helpers/JSONResponse.md +24 -0
  17. package/docs/helpers/Nightmare.md +92 -141
  18. package/docs/helpers/Playwright.md +302 -413
  19. package/docs/helpers/Puppeteer.md +171 -231
  20. package/docs/helpers/REST.md +2 -0
  21. package/docs/helpers/TestCafe.md +125 -174
  22. package/docs/helpers/WebDriver.md +184 -247
  23. package/docs/plugins.md +41 -1
  24. package/docs/secrets.md +30 -0
  25. package/docs/webapi/amOnPage.mustache +1 -1
  26. package/docs/webapi/appendField.mustache +1 -1
  27. package/docs/webapi/attachFile.mustache +1 -1
  28. package/docs/webapi/checkOption.mustache +1 -1
  29. package/docs/webapi/clearCookie.mustache +1 -1
  30. package/docs/webapi/clearField.mustache +1 -1
  31. package/docs/webapi/click.mustache +1 -1
  32. package/docs/webapi/clickLink.mustache +1 -1
  33. package/docs/webapi/closeCurrentTab.mustache +1 -1
  34. package/docs/webapi/closeOtherTabs.mustache +1 -1
  35. package/docs/webapi/dontSee.mustache +1 -1
  36. package/docs/webapi/dontSeeCheckboxIsChecked.mustache +1 -1
  37. package/docs/webapi/dontSeeCookie.mustache +1 -1
  38. package/docs/webapi/dontSeeCurrentUrlEquals.mustache +1 -1
  39. package/docs/webapi/dontSeeElement.mustache +1 -1
  40. package/docs/webapi/dontSeeElementInDOM.mustache +1 -1
  41. package/docs/webapi/dontSeeInCurrentUrl.mustache +1 -1
  42. package/docs/webapi/dontSeeInField.mustache +1 -1
  43. package/docs/webapi/dontSeeInSource.mustache +1 -1
  44. package/docs/webapi/dontSeeInTitle.mustache +1 -1
  45. package/docs/webapi/doubleClick.mustache +1 -1
  46. package/docs/webapi/downloadFile.mustache +1 -1
  47. package/docs/webapi/dragAndDrop.mustache +1 -1
  48. package/docs/webapi/dragSlider.mustache +1 -1
  49. package/docs/webapi/executeAsyncScript.mustache +1 -1
  50. package/docs/webapi/executeScript.mustache +1 -1
  51. package/docs/webapi/fillField.mustache +1 -1
  52. package/docs/webapi/forceClick.mustache +1 -1
  53. package/docs/webapi/forceRightClick.mustache +1 -1
  54. package/docs/webapi/grabDataFromPerformanceTiming.mustache +1 -1
  55. package/docs/webapi/moveCursorTo.mustache +1 -1
  56. package/docs/webapi/openNewTab.mustache +1 -1
  57. package/docs/webapi/pressKey.mustache +1 -1
  58. package/docs/webapi/pressKeyDown.mustache +1 -1
  59. package/docs/webapi/pressKeyUp.mustache +1 -1
  60. package/docs/webapi/pressKeyWithKeyNormalization.mustache +1 -1
  61. package/docs/webapi/refreshPage.mustache +1 -1
  62. package/docs/webapi/resizeWindow.mustache +1 -1
  63. package/docs/webapi/rightClick.mustache +1 -1
  64. package/docs/webapi/saveElementScreenshot.mustache +1 -1
  65. package/docs/webapi/saveScreenshot.mustache +1 -1
  66. package/docs/webapi/say.mustache +1 -1
  67. package/docs/webapi/scrollIntoView.mustache +1 -1
  68. package/docs/webapi/scrollPageToBottom.mustache +1 -1
  69. package/docs/webapi/scrollPageToTop.mustache +1 -1
  70. package/docs/webapi/scrollTo.mustache +1 -1
  71. package/docs/webapi/see.mustache +1 -1
  72. package/docs/webapi/seeAttributesOnElements.mustache +1 -1
  73. package/docs/webapi/seeCheckboxIsChecked.mustache +1 -1
  74. package/docs/webapi/seeCookie.mustache +1 -1
  75. package/docs/webapi/seeCssPropertiesOnElements.mustache +1 -1
  76. package/docs/webapi/seeCurrentUrlEquals.mustache +1 -1
  77. package/docs/webapi/seeElement.mustache +1 -1
  78. package/docs/webapi/seeElementInDOM.mustache +1 -1
  79. package/docs/webapi/seeInCurrentUrl.mustache +1 -1
  80. package/docs/webapi/seeInField.mustache +1 -1
  81. package/docs/webapi/seeInPopup.mustache +1 -1
  82. package/docs/webapi/seeInSource.mustache +1 -1
  83. package/docs/webapi/seeInTitle.mustache +1 -1
  84. package/docs/webapi/seeNumberOfElements.mustache +1 -1
  85. package/docs/webapi/seeNumberOfVisibleElements.mustache +1 -1
  86. package/docs/webapi/seeTextEquals.mustache +1 -1
  87. package/docs/webapi/seeTitleEquals.mustache +1 -1
  88. package/docs/webapi/selectOption.mustache +1 -1
  89. package/docs/webapi/setCookie.mustache +1 -1
  90. package/docs/webapi/setGeoLocation.mustache +1 -1
  91. package/docs/webapi/switchTo.mustache +1 -1
  92. package/docs/webapi/switchToNextTab.mustache +1 -1
  93. package/docs/webapi/switchToPreviousTab.mustache +1 -1
  94. package/docs/webapi/type.mustache +1 -1
  95. package/docs/webapi/uncheckOption.mustache +1 -1
  96. package/docs/webapi/wait.mustache +1 -1
  97. package/docs/webapi/waitForClickable.mustache +1 -1
  98. package/docs/webapi/waitForDetached.mustache +1 -1
  99. package/docs/webapi/waitForElement.mustache +1 -1
  100. package/docs/webapi/waitForEnabled.mustache +1 -1
  101. package/docs/webapi/waitForFunction.mustache +1 -1
  102. package/docs/webapi/waitForInvisible.mustache +1 -1
  103. package/docs/webapi/waitForText.mustache +1 -1
  104. package/docs/webapi/waitForValue.mustache +1 -1
  105. package/docs/webapi/waitForVisible.mustache +1 -1
  106. package/docs/webapi/waitInUrl.mustache +1 -1
  107. package/docs/webapi/waitNumberOfVisibleElements.mustache +1 -1
  108. package/docs/webapi/waitToHide.mustache +1 -1
  109. package/docs/webapi/waitUrlEquals.mustache +1 -1
  110. package/lib/cli.js +1 -1
  111. package/lib/command/interactive.js +1 -1
  112. package/lib/command/run-workers.js +1 -1
  113. package/lib/command/workers/runTests.js +15 -0
  114. package/lib/helper/Appium.js +0 -10
  115. package/lib/helper/JSONResponse.js +44 -3
  116. package/lib/helper/Playwright.js +24 -32
  117. package/lib/helper/REST.js +8 -4
  118. package/lib/helper/WebDriver.js +5 -7
  119. package/lib/output.js +4 -0
  120. package/lib/plugin/customLocator.js +50 -3
  121. package/lib/plugin/retryFailedStep.js +1 -1
  122. package/lib/plugin/retryTo.js +1 -8
  123. package/lib/secret.js +30 -0
  124. package/lib/step.js +1 -1
  125. package/package.json +4 -4
  126. 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
- @return {Promise<any>}
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
- @return {Promise<any>}
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
- @return {Promise<any>}
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
- @return {Promise<any>}
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
- @return {Promise<any>}
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
- @return {Promise<any>}
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
- @return {Promise<any>}
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
- @return {Promise<any>}
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
- @return {Promise<any>}
10
+ [!] returns a _promise_ which is synchronized internally by recorder
@@ -6,4 +6,4 @@ I.waitInUrl('/info', 2);
6
6
 
7
7
  @param {string} urlPart value to check.
8
8
  @param {number} [sec=1] (optional, `1` by default) time in seconds to wait
9
- @return {Promise<any>}
9
+ [!] 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
- @return {Promise<any>}
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
- @return {Promise<any>}
10
+ [!] returns a _promise_ which is synchronized internally by recorder
@@ -7,4 +7,4 @@ I.waitUrlEquals('http://127.0.0.1:8000/info');
7
7
 
8
8
  @param {string} urlPart value to check.
9
9
  @param {number} [sec=1] (optional, `1` by default) time in seconds to wait
10
- @return {Promise<any>}
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('String interactive shell for current suite...');
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,
@@ -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
- expect(this.response.data).to.deep.match(json);
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
- expect(this.response.data).not.to.deep.match(json);
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
- expect(this.response.data).to.include.keys(keys);
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
  /**
@@ -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 argument
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
- await this._setPage(await this.browserContext.newPage(options));
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
- * let logs = await I.grabBrowserLogs();
1581
- * console.log(JSON.stringify(logs))
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 request
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 request
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);
@@ -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, ...this.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 = { ...request.headers, ...this.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