codeceptjs 2.1.3 → 2.2.1

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 (173) hide show
  1. package/CHANGELOG.md +125 -37
  2. package/README.md +15 -22
  3. package/bin/codecept.js +4 -1
  4. package/docs/acceptance.md +44 -1
  5. package/docs/advanced.md +1 -1
  6. package/docs/angular.md +6 -9
  7. package/docs/basics.md +388 -75
  8. package/docs/bdd.md +4 -3
  9. package/docs/best.md +1 -1
  10. package/docs/books.md +31 -0
  11. package/docs/build/Appium.js +215 -176
  12. package/docs/build/Nightmare.js +618 -489
  13. package/docs/build/Polly.js +189 -0
  14. package/docs/build/Protractor.js +747 -608
  15. package/docs/build/Puppeteer.js +914 -633
  16. package/docs/build/REST.js +1 -1
  17. package/docs/build/TestCafe.js +1835 -0
  18. package/docs/build/WebDriver.js +861 -805
  19. package/docs/build/WebDriverIO.js +616 -617
  20. package/docs/changelog.md +410 -316
  21. package/docs/commands.md +6 -6
  22. package/docs/community-helpers.md +2 -0
  23. package/docs/detox.md +235 -0
  24. package/docs/examples.md +23 -0
  25. package/docs/helpers/ApiDataFactory.md +11 -10
  26. package/docs/helpers/Appium.md +130 -61
  27. package/docs/helpers/Detox.md +579 -0
  28. package/docs/helpers/FileSystem.md +2 -1
  29. package/docs/helpers/Mochawesome.md +1 -0
  30. package/docs/helpers/Nightmare.md +348 -128
  31. package/docs/helpers/Polly.md +85 -0
  32. package/docs/helpers/Protractor.md +451 -184
  33. package/docs/helpers/Puppeteer-firefox.md +55 -0
  34. package/docs/helpers/Puppeteer.md +619 -183
  35. package/docs/helpers/REST.md +17 -16
  36. package/docs/helpers/SeleniumWebdriver.md +9 -8
  37. package/docs/helpers/TestCafe.md +1168 -0
  38. package/docs/helpers/WebDriver.md +600 -291
  39. package/docs/helpers/WebDriverIO.md +393 -278
  40. package/docs/helpers.md +37 -18
  41. package/docs/locators.md +2 -0
  42. package/docs/mobile-react-native-locators.md +64 -0
  43. package/docs/mobile.md +5 -0
  44. package/docs/plugins.md +54 -13
  45. package/docs/puppeteer.md +74 -26
  46. package/docs/quickstart.md +47 -12
  47. package/docs/react.md +67 -0
  48. package/docs/reports.md +1 -1
  49. package/docs/{webapi/_keys.mustache → shared/keys.mustache} +0 -0
  50. package/docs/shared/react.mustache +1 -0
  51. package/docs/testcafe.md +157 -0
  52. package/docs/videos.md +19 -0
  53. package/docs/webapi/amOnPage.mustache +1 -1
  54. package/docs/webapi/appendField.mustache +2 -2
  55. package/docs/webapi/attachFile.mustache +2 -2
  56. package/docs/webapi/checkOption.mustache +2 -2
  57. package/docs/webapi/clearCookie.mustache +1 -1
  58. package/docs/webapi/clearField.mustache +1 -1
  59. package/docs/webapi/click.mustache +2 -2
  60. package/docs/webapi/clickLink.mustache +3 -3
  61. package/docs/webapi/dontSee.mustache +6 -3
  62. package/docs/webapi/dontSeeCheckboxIsChecked.mustache +7 -1
  63. package/docs/webapi/dontSeeCookie.mustache +5 -1
  64. package/docs/webapi/dontSeeCurrentUrlEquals.mustache +6 -1
  65. package/docs/webapi/dontSeeElement.mustache +5 -1
  66. package/docs/webapi/dontSeeElementInDOM.mustache +5 -1
  67. package/docs/webapi/dontSeeInCurrentUrl.mustache +1 -1
  68. package/docs/webapi/dontSeeInField.mustache +7 -2
  69. package/docs/webapi/dontSeeInSource.mustache +5 -1
  70. package/docs/webapi/dontSeeInTitle.mustache +5 -1
  71. package/docs/webapi/doubleClick.mustache +2 -2
  72. package/docs/webapi/downloadFile.mustache +2 -2
  73. package/docs/webapi/dragAndDrop.mustache +2 -2
  74. package/docs/webapi/dragSlider.mustache +2 -2
  75. package/docs/webapi/executeAsyncScript.mustache +1 -1
  76. package/docs/webapi/executeScript.mustache +1 -1
  77. package/docs/webapi/fillField.mustache +2 -2
  78. package/docs/webapi/grabAttributeFrom.mustache +3 -2
  79. package/docs/webapi/grabBrowserLogs.mustache +3 -1
  80. package/docs/webapi/grabCookie.mustache +2 -1
  81. package/docs/webapi/grabCssPropertyFrom.mustache +3 -2
  82. package/docs/webapi/grabCurrentUrl.mustache +3 -1
  83. package/docs/webapi/grabDataFromPerformanceTiming.mustache +19 -0
  84. package/docs/webapi/grabHTMLFrom.mustache +2 -1
  85. package/docs/webapi/grabNumberOfOpenTabs.mustache +4 -2
  86. package/docs/webapi/grabNumberOfVisibleElements.mustache +3 -2
  87. package/docs/webapi/grabPageScrollPosition.mustache +3 -1
  88. package/docs/webapi/grabSource.mustache +3 -1
  89. package/docs/webapi/grabTextFrom.mustache +2 -1
  90. package/docs/webapi/grabTitle.mustache +3 -1
  91. package/docs/webapi/grabValueFrom.mustache +2 -1
  92. package/docs/webapi/moveCursorTo.mustache +3 -3
  93. package/docs/webapi/pressKey.mustache +1 -1
  94. package/docs/webapi/resizeWindow.mustache +2 -2
  95. package/docs/webapi/rightClick.mustache +2 -2
  96. package/docs/webapi/saveScreenshot.mustache +3 -3
  97. package/docs/webapi/say.mustache +2 -2
  98. package/docs/webapi/scrollPageToBottom.mustache +1 -1
  99. package/docs/webapi/scrollPageToTop.mustache +1 -1
  100. package/docs/webapi/scrollTo.mustache +3 -3
  101. package/docs/webapi/see.mustache +2 -2
  102. package/docs/webapi/seeAttributesOnElements.mustache +3 -3
  103. package/docs/webapi/seeCheckboxIsChecked.mustache +2 -1
  104. package/docs/webapi/seeCookie.mustache +1 -1
  105. package/docs/webapi/seeCssPropertiesOnElements.mustache +2 -2
  106. package/docs/webapi/seeCurrentUrlEquals.mustache +1 -1
  107. package/docs/webapi/seeElement.mustache +1 -1
  108. package/docs/webapi/seeElementInDOM.mustache +1 -1
  109. package/docs/webapi/seeInCurrentUrl.mustache +1 -1
  110. package/docs/webapi/seeInField.mustache +2 -2
  111. package/docs/webapi/seeInSource.mustache +1 -1
  112. package/docs/webapi/seeInTitle.mustache +5 -1
  113. package/docs/webapi/seeNumberOfElements.mustache +10 -0
  114. package/docs/webapi/seeNumberOfVisibleElements.mustache +2 -2
  115. package/docs/webapi/selectOption.mustache +2 -2
  116. package/docs/webapi/setCookie.mustache +1 -1
  117. package/docs/webapi/switchTo.mustache +6 -1
  118. package/docs/webapi/uncheckOption.mustache +2 -2
  119. package/docs/webapi/wait.mustache +1 -2
  120. package/docs/webapi/waitForDetached.mustache +3 -3
  121. package/docs/webapi/waitForElement.mustache +2 -2
  122. package/docs/webapi/waitForEnabled.mustache +1 -1
  123. package/docs/webapi/waitForFunction.mustache +3 -3
  124. package/docs/webapi/waitForInvisible.mustache +3 -3
  125. package/docs/webapi/waitForText.mustache +3 -3
  126. package/docs/webapi/waitForValue.mustache +3 -3
  127. package/docs/webapi/waitForVisible.mustache +3 -3
  128. package/docs/webapi/waitInUrl.mustache +2 -2
  129. package/docs/webapi/waitNumberOfVisibleElements.mustache +3 -3
  130. package/docs/webapi/waitToHide.mustache +3 -3
  131. package/docs/webapi/waitUntil.mustache +3 -3
  132. package/docs/webapi/waitUrlEquals.mustache +2 -2
  133. package/docs/webdriver.md +453 -0
  134. package/lib/codecept.js +11 -9
  135. package/lib/command/definitions.js +183 -30
  136. package/lib/command/gherkin/snippets.js +29 -9
  137. package/lib/command/init.js +31 -9
  138. package/lib/command/run-multiple.js +46 -59
  139. package/lib/command/utils.js +1 -1
  140. package/lib/container.js +30 -4
  141. package/lib/data/dataScenarioConfig.js +18 -0
  142. package/lib/helper/Appium.js +24 -24
  143. package/lib/helper/Nightmare.js +81 -84
  144. package/lib/helper/Polly.js +189 -0
  145. package/lib/helper/Protractor.js +96 -86
  146. package/lib/helper/Puppeteer.js +238 -113
  147. package/lib/helper/REST.js +1 -1
  148. package/lib/helper/TestCafe.js +1257 -0
  149. package/lib/helper/WebDriver.js +217 -277
  150. package/lib/helper/WebDriverIO.js +75 -75
  151. package/lib/helper/clientscripts/nightmare.js +8 -0
  152. package/lib/helper/extras/React.js +55 -0
  153. package/lib/helper/testcafe/testControllerHolder.js +42 -0
  154. package/lib/helper/testcafe/testcafe-utils.js +63 -0
  155. package/lib/history.js +39 -0
  156. package/lib/hooks.js +25 -1
  157. package/lib/interfaces/gherkin.js +17 -1
  158. package/lib/interfaces/scenarioConfig.js +2 -2
  159. package/lib/listener/config.js +3 -3
  160. package/lib/locator.js +6 -0
  161. package/lib/pause.js +22 -1
  162. package/lib/plugin/allure.js +63 -0
  163. package/lib/plugin/autoLogin.js +65 -16
  164. package/lib/plugin/puppeteerCoverage.js +6 -1
  165. package/lib/plugin/stepByStepReport.js +4 -3
  166. package/lib/scenario.js +23 -17
  167. package/lib/step.js +5 -2
  168. package/lib/ui.js +1 -1
  169. package/lib/utils.js +70 -20
  170. package/package.json +20 -19
  171. package/translations/de-DE.js +69 -0
  172. package/translations/index.js +1 -0
  173. package/docs/video.md +0 -26
@@ -11,7 +11,6 @@ const {
11
11
  xpathLocator,
12
12
  ucfirst,
13
13
  fileExists,
14
- clearString,
15
14
  chunkArray,
16
15
  convertCssPropertiesToCamelCase,
17
16
  screenshotOutputFolder,
@@ -25,11 +24,13 @@ const ElementNotFound = require('./errors/ElementNotFound');
25
24
  const RemoteBrowserConnectionRefused = require('./errors/RemoteBrowserConnectionRefused');
26
25
  const Popup = require('./extras/Popup');
27
26
  const Console = require('./extras/Console');
27
+ const findReact = require('./extras/React');
28
28
  const axios = require('axios');
29
29
  const fs = require('fs');
30
-
30
+ const fsExtra = require('fs-extra');
31
31
 
32
32
  let puppeteer;
33
+ let perfTiming;
33
34
  const popupStore = new Popup();
34
35
  const consoleLogStore = new Console();
35
36
 
@@ -40,6 +41,8 @@ const consoleLogStore = new Console();
40
41
  *
41
42
  * Requires `puppeteer` package to be installed.
42
43
  *
44
+ * > Experiemental Firefox support [can be activated](https://codecept.io/helpers/Puppeteer-firefox).
45
+ *
43
46
  * ## Configuration
44
47
  *
45
48
  * This helper should be configured in codecept.json or codecept.conf.js
@@ -54,11 +57,13 @@ const consoleLogStore = new Console();
54
57
  * * `keepCookies`: (optional, default: false) - keep cookies between tests when `restart` is set to false.
55
58
  * * `waitForAction`: (optional) how long to wait after click, doubleClick or PressKey actions in ms. Default: 100.
56
59
  * * `waitForNavigation`: (optional, default: 'load'). When to consider navigation succeeded. Possible options: `load`, `domcontentloaded`, `networkidle0`, `networkidle2`. See [Puppeteer API](https://github.com/GoogleChrome/puppeteer/blob/master/docs/api.md#pagewaitfornavigationoptions). Array values are accepted as well.
60
+ * * `pressKeyDelay`: (optional, default: '10'). Delay between key presses in ms. Used when calling Puppeteers page.type(...) in fillField/appendField
57
61
  * * `getPageTimeout` (optional, default: '0') config option to set maximum navigation time in milliseconds.
58
62
  * * `waitForTimeout`: (optional) default wait* timeout in ms. Default: 1000.
59
63
  * * `windowSize`: (optional) default window size. Set a dimension like `640x480`.
60
64
  * * `userAgent`: (optional) user-agent string.
61
65
  * * `manualStart`: (optional, default: false) - do not start browser before a test, start it manually inside a helper with `this.helpers["Puppeteer"]._startBrowser()`.
66
+ * * `browser`: (optional, default: chrome) - can be changed to `firefox` when using [puppeteer-firefox](https://codecept.io/helpers/Puppeteer-firefox).
62
67
  * * `chrome`: (optional) pass additional [Puppeteer run options](https://github.com/GoogleChrome/puppeteer/blob/master/docs/api.md#puppeteerlaunchoptions).
63
68
  *
64
69
  *
@@ -139,7 +144,8 @@ const consoleLogStore = new Console();
139
144
  class Puppeteer extends Helper {
140
145
  constructor(config) {
141
146
  super(config);
142
- puppeteer = requireg('puppeteer');
147
+
148
+ puppeteer = requireg(config.browser === 'firefox' ? 'puppeteer-firefox' : 'puppeteer');
143
149
 
144
150
  // set defaults
145
151
  this.isRemoteBrowser = false;
@@ -151,8 +157,10 @@ class Puppeteer extends Helper {
151
157
 
152
158
  _validateConfig(config) {
153
159
  const defaults = {
160
+ browser: 'chrome',
154
161
  waitForAction: 100,
155
162
  waitForTimeout: 1000,
163
+ pressKeyDelay: 10,
156
164
  fullPageScreenshots: false,
157
165
  disableScreenshots: false,
158
166
  uniqueScreenshotNames: false,
@@ -169,9 +177,13 @@ class Puppeteer extends Helper {
169
177
  return Object.assign(defaults, config);
170
178
  }
171
179
 
180
+ _getOptions(config) {
181
+ return config.browser === 'firefox' ? this.options.firefox : this.options.chrome;
182
+ }
183
+
172
184
  _setConfig(config) {
173
185
  this.options = this._validateConfig(config);
174
- this.puppeteerOptions = Object.assign({ headless: !this.options.show }, this.options.chrome);
186
+ this.puppeteerOptions = Object.assign({ headless: !this.options.show }, this._getOptions(config));
175
187
  this.isRemoteBrowser = !!this.puppeteerOptions.browserWSEndpoint;
176
188
  popupStore.defaultAction = this.options.defaultPopupAction;
177
189
  }
@@ -179,6 +191,9 @@ class Puppeteer extends Helper {
179
191
  static _config() {
180
192
  return [
181
193
  { name: 'url', message: 'Base url of site to be tested', default: 'http://localhost' },
194
+ {
195
+ name: 'show', message: 'Show browser window', default: true, type: 'confirm',
196
+ },
182
197
  ];
183
198
  }
184
199
 
@@ -339,7 +354,9 @@ class Puppeteer extends Helper {
339
354
  if (!page) return;
340
355
  page.setDefaultNavigationTimeout(this.options.getPageTimeout);
341
356
  this.context = await this.page.$('body');
342
- await page.bringToFront();
357
+ if (this.config.browser === 'chrome') {
358
+ await page.bringToFront();
359
+ }
343
360
  }
344
361
 
345
362
  /**
@@ -488,19 +505,41 @@ class Puppeteer extends Helper {
488
505
  this.context = await this.page.mainFrame().$('body');
489
506
  }
490
507
 
508
+ _extractDataFromPerformanceTiming(timing, ...dataNames) {
509
+ const navigationStart = timing.navigationStart;
510
+
511
+ const extractedData = {};
512
+ dataNames.forEach((name) => {
513
+ extractedData[name] = timing[name] - navigationStart;
514
+ });
515
+
516
+ return extractedData;
517
+ }
518
+
491
519
  /**
492
- * {{> ../webapi/amOnPage }}
520
+ * {{> amOnPage }}
493
521
  */
494
522
  async amOnPage(url) {
495
523
  if (!(/^\w+\:\/\//.test(url))) {
496
524
  url = this.options.url + url;
497
525
  }
498
526
  await this.page.goto(url, { waitUntil: this.options.waitForNavigation });
527
+
528
+ const performanceTiming = JSON.parse(await this.page.evaluate(() => JSON.stringify(window.performance.timing)));
529
+
530
+ perfTiming = this._extractDataFromPerformanceTiming(
531
+ performanceTiming,
532
+ 'responseEnd',
533
+ 'domInteractive',
534
+ 'domContentLoadedEventEnd',
535
+ 'loadEventEnd',
536
+ );
537
+
499
538
  return this._waitForAction();
500
539
  }
501
540
 
502
541
  /**
503
- * {{> ../webapi/resizeWindow }}
542
+ * {{> resizeWindow }}
504
543
  *
505
544
  * Unlike other drivers Puppeteer changes the size of a viewport, not the window!
506
545
  * Puppeteer does not control the window of a browser so it can't adjust its real size.
@@ -523,6 +562,8 @@ class Puppeteer extends Helper {
523
562
  * 'X-Sent-By': 'CodeceptJS',
524
563
  * });
525
564
  * ```
565
+ *
566
+ * @param {object} customHeaders headers to set
526
567
  */
527
568
  async haveRequestHeaders(customHeaders) {
528
569
  if (!customHeaders) {
@@ -532,7 +573,8 @@ class Puppeteer extends Helper {
532
573
  }
533
574
 
534
575
  /**
535
- * {{> ../webapi/moveCursorTo}}
576
+ * {{> moveCursorTo }}
577
+ * {{ react }}
536
578
  */
537
579
  async moveCursorTo(locator, offsetX = 0, offsetY = 0) {
538
580
  const els = await this._locate(locator);
@@ -545,33 +587,33 @@ class Puppeteer extends Helper {
545
587
  }
546
588
 
547
589
  /**
548
- * {{> ../webapi/dragAndDrop }}
590
+ * {{> dragAndDrop }}
549
591
  */
550
592
  async dragAndDrop(srcElement, destElement) {
551
593
  return proceedDragAndDrop.call(this, srcElement, destElement);
552
594
  }
553
595
 
554
596
  /**
555
- * {{> ../webapi/refreshPage }}
597
+ * {{> refreshPage }}
556
598
  */
557
599
  async refreshPage() {
558
600
  return this.page.reload({ timeout: this.options.getPageTimeout, waitUntil: this.options.waitForNavigation });
559
601
  }
560
602
 
561
603
  /**
562
- * {{> ../webapi/scrollPageToTop }}
604
+ * {{> scrollPageToTop }}
563
605
  */
564
606
  scrollPageToTop() {
565
- return this.page.evaluate(() => {
607
+ return this.executeScript(() => {
566
608
  window.scrollTo(0, 0);
567
609
  });
568
610
  }
569
611
 
570
612
  /**
571
- * {{> ../webapi/scrollPageToBottom }}
613
+ * {{> scrollPageToBottom }}
572
614
  */
573
615
  scrollPageToBottom() {
574
- return this.page.evaluate(() => {
616
+ return this.executeScript(() => {
575
617
  const body = document.body;
576
618
  const html = document.documentElement;
577
619
  window.scrollTo(0, Math.max(
@@ -582,7 +624,7 @@ class Puppeteer extends Helper {
582
624
  }
583
625
 
584
626
  /**
585
- * {{> ../webapi/scrollTo }}
627
+ * {{> scrollTo }}
586
628
  */
587
629
  async scrollTo(locator, offsetX = 0, offsetY = 0) {
588
630
  if (typeof locator === 'number' && typeof offsetX === 'number') {
@@ -590,25 +632,21 @@ class Puppeteer extends Helper {
590
632
  offsetX = locator;
591
633
  locator = null;
592
634
  }
593
- let x = 0;
594
- let y = 0;
635
+
595
636
  if (locator) {
596
637
  const els = await this._locate(locator);
597
638
  assertElementExists(els, locator, 'Element');
598
639
  await els[0]._scrollIntoViewIfNeeded();
599
640
  const elementCoordinates = await els[0]._clickablePoint();
600
- x = elementCoordinates.x;
601
- y = elementCoordinates.y;
641
+ await this.executeScript((x, y) => window.scrollBy(x, y), elementCoordinates.x + offsetX, elementCoordinates.y + offsetY);
642
+ } else {
643
+ await this.executeScript((x, y) => window.scrollTo(x, y), offsetX, offsetY);
602
644
  }
603
-
604
- await this.page.evaluate((x, y) => {
605
- window.scrollTo(x, y);
606
- }, x + offsetX, y + offsetY);
607
645
  return this._waitForAction();
608
646
  }
609
647
 
610
648
  /**
611
- * {{> ../webapi/seeInTitle }}
649
+ * {{> seeInTitle }}
612
650
  */
613
651
  async seeInTitle(text) {
614
652
  const title = await this.page.title();
@@ -616,7 +654,7 @@ class Puppeteer extends Helper {
616
654
  }
617
655
 
618
656
  /**
619
- * {{> ../webapi/grabPageScrollPosition}}
657
+ * {{> grabPageScrollPosition }}
620
658
  */
621
659
  async grabPageScrollPosition() {
622
660
  /* eslint-disable comma-dangle */
@@ -643,7 +681,7 @@ class Puppeteer extends Helper {
643
681
  }
644
682
 
645
683
  /**
646
- * {{> ../webapi/dontSeeInTitle }}
684
+ * {{> dontSeeInTitle }}
647
685
  */
648
686
  async dontSeeInTitle(text) {
649
687
  const title = await this.page.title();
@@ -651,7 +689,7 @@ class Puppeteer extends Helper {
651
689
  }
652
690
 
653
691
  /**
654
- * {{> ../webapi/grabTitle }}
692
+ * {{> grabTitle }}
655
693
  */
656
694
  async grabTitle() {
657
695
  return this.page.title();
@@ -664,6 +702,8 @@ class Puppeteer extends Helper {
664
702
  * ```js
665
703
  * const elements = await this.helpers['Puppeteer']._locate({name: 'password'});
666
704
  * ```
705
+ *
706
+ * {{ react }}
667
707
  */
668
708
  async _locate(locator) {
669
709
  return findElements(await this.context, locator);
@@ -714,6 +754,8 @@ class Puppeteer extends Helper {
714
754
  * I.switchToNextTab();
715
755
  * I.switchToNextTab(2);
716
756
  * ```
757
+ *
758
+ * @param {number} [num=1]
717
759
  */
718
760
  async switchToNextTab(num = 1) {
719
761
  const pages = await this.browser.pages();
@@ -736,6 +778,7 @@ class Puppeteer extends Helper {
736
778
  * I.switchToPreviousTab();
737
779
  * I.switchToPreviousTab(2);
738
780
  * ```
781
+ * @param {number} [num=1]
739
782
  */
740
783
  async switchToPreviousTab(num = 1) {
741
784
  const pages = await this.browser.pages();
@@ -797,7 +840,7 @@ class Puppeteer extends Helper {
797
840
  }
798
841
 
799
842
  /**
800
- * {{> ../webapi/grabNumberOfOpenTabs }}
843
+ * {{> grabNumberOfOpenTabs }}
801
844
  */
802
845
  async grabNumberOfOpenTabs() {
803
846
  const pages = await this.browser.pages();
@@ -805,9 +848,9 @@ class Puppeteer extends Helper {
805
848
  }
806
849
 
807
850
  /**
808
- * {{> ../webapi/seeElement }}
851
+ * {{> seeElement }}
852
+ * {{ react }}
809
853
  */
810
-
811
854
  async seeElement(locator) {
812
855
  let els = await this._locate(locator);
813
856
  els = await Promise.all(els.map(el => el.boundingBox()));
@@ -815,7 +858,8 @@ class Puppeteer extends Helper {
815
858
  }
816
859
 
817
860
  /**
818
- * {{> ../webapi/dontSeeElement }}
861
+ * {{> dontSeeElement }}
862
+ * {{ react }}
819
863
  */
820
864
  async dontSeeElement(locator) {
821
865
  let els = await this._locate(locator);
@@ -824,7 +868,7 @@ class Puppeteer extends Helper {
824
868
  }
825
869
 
826
870
  /**
827
- * {{> ../webapi/seeElementInDOM }}
871
+ * {{> seeElementInDOM }}
828
872
  */
829
873
  async seeElementInDOM(locator) {
830
874
  const els = await this._locate(locator);
@@ -832,7 +876,7 @@ class Puppeteer extends Helper {
832
876
  }
833
877
 
834
878
  /**
835
- * {{> ../webapi/dontSeeElementInDOM }}
879
+ * {{> dontSeeElementInDOM }}
836
880
  */
837
881
  async dontSeeElementInDOM(locator) {
838
882
  const els = await this._locate(locator);
@@ -840,37 +884,77 @@ class Puppeteer extends Helper {
840
884
  }
841
885
 
842
886
  /**
843
- * {{> ../webapi/click }}
887
+ * {{> click }}
888
+ *
889
+ * {{ react }}
844
890
  */
845
891
  async click(locator, context = null) {
846
892
  return proceedClick.call(this, locator, context);
847
893
  }
848
894
 
849
895
  /**
850
- * {{> ../webapi/clickLink }}
896
+ * {{> clickLink }}
897
+ *
898
+ * {{ react }}
851
899
  */
852
900
  async clickLink(locator, context = null) {
853
901
  return proceedClick.call(this, locator, context, { waitForNavigation: true });
854
902
  }
855
903
 
856
904
  /**
857
- * {{> ../webapi/downloadFile }}
905
+ * Sets a directory to where save files. Allows to test file downloads.
906
+ * Should be used with [FileSystem helper](https://codecept.io/helpers/FileSystem) to check that file were downloaded correctly.
907
+ *
908
+ * By default files are saved to `output/downloads`.
909
+ * This directory is cleaned on every `handleDownloads` call, to ensure no old files are kept.
910
+ *
911
+ * ```js
912
+ * I.handleDownloads();
913
+ * I.click('Download Avatar');
914
+ * I.amInPath('output/downloads');
915
+ * I.seeFile('avatar.jpg');
916
+ *
917
+ * ```
918
+ *
919
+ * @param {string} [downloadPath='downloads'] change this parameter to set another directory for saving
920
+ */
921
+ async handleDownloads(downloadPath = 'downloads') {
922
+ downloadPath = path.join(global.output_dir, downloadPath);
923
+ if (!fs.existsSync(downloadPath)) {
924
+ fs.mkdirSync(downloadPath, '0777');
925
+ }
926
+ fsExtra.emptyDirSync(downloadPath);
927
+ return this.page._client.send('Page.setDownloadBehavior', { behavior: 'allow', downloadPath });
928
+ }
929
+
930
+ /**
931
+ * This method is **depreacted**.
932
+ *
933
+ * Please use `handleDownloads()` instead.
858
934
  */
859
935
  async downloadFile(locator, customName) {
860
936
  let fileName;
861
937
  await this.page.setRequestInterception(true);
862
- this.click(locator);
863
938
 
864
939
  const xRequest = await new Promise((resolve) => {
865
940
  this.page.on('request', (request) => {
941
+ console.log('rq', request, customName);
866
942
  const grabbedFileName = request.url().split('/')[request.url().split('/').length - 1];
867
943
  const fileExtension = request.url().split('/')[request.url().split('/').length - 1].split('.')[1];
944
+ console.log('nm', customName, fileExtension);
945
+ if (customName && path.extname(customName) !== fileExtension) {
946
+ console.log('bypassing a request');
947
+ request.continue();
948
+ return;
949
+ }
868
950
  customName ? fileName = `${customName}.${fileExtension}` : fileName = grabbedFileName;
869
951
  request.abort();
870
952
  resolve(request);
871
953
  });
872
954
  });
873
955
 
956
+ await this.click(locator);
957
+
874
958
  const options = {
875
959
  encoding: null,
876
960
  method: xRequest._method,
@@ -883,14 +967,21 @@ class Puppeteer extends Helper {
883
967
  options.headers.Cookie = cookies.map(ck => `${ck.name}=${ck.value}`).join(';');
884
968
 
885
969
  const response = await axios({
886
- method: options.method, url: options.uri, headers: options.headers, responseType: 'arraybuffer',
970
+ method: options.method,
971
+ url: options.uri,
972
+ headers: options.headers,
973
+ responseType: 'arraybuffer',
974
+ onDownloadProgress(e) {
975
+ console.log('+', e);
976
+ },
887
977
  });
888
978
 
889
979
  const outputFile = path.join(`${global.output_dir}/${fileName}`);
890
980
 
891
981
  try {
892
- return new Promise((resolve, reject) => {
982
+ await new Promise((resolve, reject) => {
893
983
  const wstream = fs.createWriteStream(outputFile);
984
+ console.log(response);
894
985
  wstream.write(response.data);
895
986
  wstream.end();
896
987
  this.debug(`File is downloaded in ${outputFile}`);
@@ -903,21 +994,25 @@ class Puppeteer extends Helper {
903
994
  }
904
995
 
905
996
  /**
906
- * {{> ../webapi/doubleClick }}
997
+ * {{> doubleClick }}
998
+ *
999
+ * {{ react }}
907
1000
  */
908
1001
  async doubleClick(locator, context = null) {
909
1002
  return proceedClick.call(this, locator, context, { clickCount: 2 });
910
1003
  }
911
1004
 
912
1005
  /**
913
- * {{> ../webapi/rightClick }}
1006
+ * {{> rightClick }}
1007
+ *
1008
+ * {{ react }}
914
1009
  */
915
1010
  async rightClick(locator, context = null) {
916
1011
  return proceedClick.call(this, locator, context, { button: 'right' });
917
1012
  }
918
1013
 
919
1014
  /**
920
- * {{> ../webapi/checkOption }}
1015
+ * {{> checkOption }}
921
1016
  */
922
1017
  async checkOption(field, context = null) {
923
1018
  const elm = await this._locateCheckable(field, context);
@@ -931,7 +1026,7 @@ class Puppeteer extends Helper {
931
1026
  }
932
1027
 
933
1028
  /**
934
- * {{> ../webapi/uncheckOption }}
1029
+ * {{> uncheckOption }}
935
1030
  */
936
1031
  async uncheckOption(field, context = null) {
937
1032
  const elm = await this._locateCheckable(field, context);
@@ -945,21 +1040,23 @@ class Puppeteer extends Helper {
945
1040
  }
946
1041
 
947
1042
  /**
948
- * {{> ../webapi/seeCheckboxIsChecked }}
1043
+ * {{> seeCheckboxIsChecked }}
949
1044
  */
950
1045
  async seeCheckboxIsChecked(field) {
951
1046
  return proceedIsChecked.call(this, 'assert', field);
952
1047
  }
953
1048
 
954
1049
  /**
955
- * {{> ../webapi/dontSeeCheckboxIsChecked }}
1050
+ * {{> dontSeeCheckboxIsChecked }}
956
1051
  */
957
1052
  async dontSeeCheckboxIsChecked(field) {
958
1053
  return proceedIsChecked.call(this, 'negate', field);
959
1054
  }
960
1055
 
961
1056
  /**
962
- * {{> ../webapi/pressKey }}
1057
+ * {{> pressKey }}
1058
+ *
1059
+ * {{ keys }}
963
1060
  */
964
1061
  async pressKey(key) {
965
1062
  let modifier;
@@ -975,7 +1072,8 @@ class Puppeteer extends Helper {
975
1072
  }
976
1073
 
977
1074
  /**
978
- * {{> ../webapi/fillField }}
1075
+ * {{> fillField }}
1076
+ * {{ react }}
979
1077
  */
980
1078
  async fillField(field, value) {
981
1079
  const els = await findFields.call(this, field);
@@ -989,38 +1087,40 @@ class Puppeteer extends Helper {
989
1087
  } else if (editable) {
990
1088
  await this._evaluateHandeInContext(el => el.innerHTML = '', el);
991
1089
  }
992
- await el.type(value.toString(), { delay: 10 });
1090
+ await el.type(value.toString(), { delay: this.options.pressKeyDelay });
993
1091
  return this._waitForAction();
994
1092
  }
995
1093
 
996
1094
 
997
1095
  /**
998
- * {{> ../webapi/clearField }}
1096
+ * {{> clearField }}
999
1097
  */
1000
1098
  async clearField(field) {
1001
1099
  return this.fillField(field, '');
1002
1100
  }
1003
1101
 
1004
1102
  /**
1005
- * {{> ../webapi/appendField }}
1103
+ * {{> appendField }}
1104
+ *
1105
+ * {{ react }}
1006
1106
  */
1007
1107
  async appendField(field, value) {
1008
1108
  const els = await findFields.call(this, field);
1009
1109
  assertElementExists(els, field, 'Field');
1010
1110
  await els[0].press('End');
1011
- await els[0].type(value, { delay: 10 });
1111
+ await els[0].type(value, { delay: this.options.pressKeyDelay });
1012
1112
  return this._waitForAction();
1013
1113
  }
1014
1114
 
1015
1115
  /**
1016
- * {{> ../webapi/seeInField }}
1116
+ * {{> seeInField }}
1017
1117
  */
1018
1118
  async seeInField(field, value) {
1019
1119
  return proceedSeeInField.call(this, 'assert', field, value);
1020
1120
  }
1021
1121
 
1022
1122
  /**
1023
- * {{> ../webapi/dontSeeInField }}
1123
+ * {{> dontSeeInField }}
1024
1124
  */
1025
1125
  async dontSeeInField(field, value) {
1026
1126
  return proceedSeeInField.call(this, 'negate', field, value);
@@ -1028,8 +1128,7 @@ class Puppeteer extends Helper {
1028
1128
 
1029
1129
 
1030
1130
  /**
1031
- * {{> ../webapi/attachFile }}
1032
- *
1131
+ * {{> attachFile }}
1033
1132
  */
1034
1133
  async attachFile(locator, pathToFile) {
1035
1134
  const file = path.join(global.codecept_dir, pathToFile);
@@ -1044,7 +1143,7 @@ class Puppeteer extends Helper {
1044
1143
  }
1045
1144
 
1046
1145
  /**
1047
- * {{> ../webapi/selectOption }}
1146
+ * {{> selectOption }}
1048
1147
  */
1049
1148
  async selectOption(select, option) {
1050
1149
  const els = await findFields.call(this, select);
@@ -1076,7 +1175,8 @@ class Puppeteer extends Helper {
1076
1175
  }
1077
1176
 
1078
1177
  /**
1079
- * {{> ../webapi/grabNumberOfVisibleElements }}
1178
+ * {{> grabNumberOfVisibleElements }}
1179
+ * {{ react }}
1080
1180
  */
1081
1181
  async grabNumberOfVisibleElements(locator) {
1082
1182
  let els = await this._locate(locator);
@@ -1085,35 +1185,37 @@ class Puppeteer extends Helper {
1085
1185
  }
1086
1186
 
1087
1187
  /**
1088
- * {{> ../webapi/seeInCurrentUrl }}
1188
+ * {{> seeInCurrentUrl }}
1089
1189
  */
1090
1190
  async seeInCurrentUrl(url) {
1091
1191
  stringIncludes('url').assert(url, await this._getPageUrl());
1092
1192
  }
1093
1193
 
1094
1194
  /**
1095
- * {{> ../webapi/dontSeeInCurrentUrl }}
1195
+ * {{> dontSeeInCurrentUrl }}
1096
1196
  */
1097
1197
  async dontSeeInCurrentUrl(url) {
1098
1198
  stringIncludes('url').negate(url, await this._getPageUrl());
1099
1199
  }
1100
1200
 
1101
1201
  /**
1102
- * {{> ../webapi/seeCurrentUrlEquals }}
1202
+ * {{> seeCurrentUrlEquals }}
1103
1203
  */
1104
1204
  async seeCurrentUrlEquals(url) {
1105
1205
  urlEquals(this.options.url).assert(url, await this._getPageUrl());
1106
1206
  }
1107
1207
 
1108
1208
  /**
1109
- * {{> ../webapi/dontSeeCurrentUrlEquals }}
1209
+ * {{> dontSeeCurrentUrlEquals }}
1110
1210
  */
1111
1211
  async dontSeeCurrentUrlEquals(url) {
1112
1212
  urlEquals(this.options.url).negate(url, await this._getPageUrl());
1113
1213
  }
1114
1214
 
1115
1215
  /**
1116
- * {{> ../webapi/see }}
1216
+ * {{> see }}
1217
+ *
1218
+ * {{ react }}
1117
1219
  */
1118
1220
  async see(text, context = null) {
1119
1221
  return proceedSee.call(this, 'assert', text, context);
@@ -1131,14 +1233,16 @@ class Puppeteer extends Helper {
1131
1233
  }
1132
1234
 
1133
1235
  /**
1134
- * {{> ../webapi/dontSee }}
1236
+ * {{> dontSee }}
1237
+ *
1238
+ * {{ react }}
1135
1239
  */
1136
1240
  async dontSee(text, context = null) {
1137
1241
  return proceedSee.call(this, 'negate', text, context);
1138
1242
  }
1139
1243
 
1140
1244
  /**
1141
- * {{> ../webapi/grabSource }}
1245
+ * {{> grabSource }}
1142
1246
  */
1143
1247
  async grabSource() {
1144
1248
  return this.page.content();
@@ -1159,14 +1263,14 @@ class Puppeteer extends Helper {
1159
1263
  }
1160
1264
 
1161
1265
  /**
1162
- * {{> ../webapi/grabCurrentUrl }}
1266
+ * {{> grabCurrentUrl }}
1163
1267
  */
1164
1268
  async grabCurrentUrl() {
1165
1269
  return this._getPageUrl();
1166
1270
  }
1167
1271
 
1168
1272
  /**
1169
- * {{> ../webapi/seeInSource }}
1273
+ * {{> seeInSource }}
1170
1274
  */
1171
1275
  async seeInSource(text) {
1172
1276
  const source = await this.page.content();
@@ -1174,7 +1278,7 @@ class Puppeteer extends Helper {
1174
1278
  }
1175
1279
 
1176
1280
  /**
1177
- * {{> ../webapi/dontSeeInSource }}
1281
+ * {{> dontSeeInSource }}
1178
1282
  */
1179
1283
  async dontSeeInSource(text) {
1180
1284
  const source = await this.page.content();
@@ -1183,20 +1287,19 @@ class Puppeteer extends Helper {
1183
1287
 
1184
1288
 
1185
1289
  /**
1186
- * asserts that an element appears a given number of times in the DOM
1187
- * Element is located by label or name or CSS or XPath.
1290
+ * {{> seeNumberOfElements }}
1188
1291
  *
1189
- * ```js
1190
- * I.seeNumberOfElements('#submitBtn', 1);
1191
- * ```
1292
+ * {{ react }}
1192
1293
  */
1193
- async seeNumberOfElements(selector, num) {
1194
- const elements = await this._locate(selector);
1195
- return equals(`expected number of elements (${selector}) is ${num}, but found ${elements.length}`).assert(elements.length, num);
1294
+ async seeNumberOfElements(locator, num) {
1295
+ const elements = await this._locate(locator);
1296
+ return equals(`expected number of elements (${locator}) is ${num}, but found ${elements.length}`).assert(elements.length, num);
1196
1297
  }
1197
1298
 
1198
1299
  /**
1199
- * {{> ../webapi/seeNumberOfVisibleElements }}
1300
+ * {{> seeNumberOfVisibleElements }}
1301
+ *
1302
+ * {{ react }}
1200
1303
  */
1201
1304
  async seeNumberOfVisibleElements(locator, num) {
1202
1305
  const res = await this.grabNumberOfVisibleElements(locator);
@@ -1204,7 +1307,7 @@ class Puppeteer extends Helper {
1204
1307
  }
1205
1308
 
1206
1309
  /**
1207
- * {{> ../webapi/setCookie }}
1310
+ * {{> setCookie }}
1208
1311
  */
1209
1312
  async setCookie(cookie) {
1210
1313
  if (Array.isArray(cookie)) {
@@ -1214,7 +1317,7 @@ class Puppeteer extends Helper {
1214
1317
  }
1215
1318
 
1216
1319
  /**
1217
- * {{> ../webapi/seeCookie}}
1320
+ * {{> seeCookie }}
1218
1321
  *
1219
1322
  */
1220
1323
  async seeCookie(name) {
@@ -1223,7 +1326,7 @@ class Puppeteer extends Helper {
1223
1326
  }
1224
1327
 
1225
1328
  /**
1226
- * {{> ../webapi/dontSeeCookie}}
1329
+ * {{> dontSeeCookie }}
1227
1330
  */
1228
1331
  async dontSeeCookie(name) {
1229
1332
  const cookies = await this.page.cookies();
@@ -1231,7 +1334,7 @@ class Puppeteer extends Helper {
1231
1334
  }
1232
1335
 
1233
1336
  /**
1234
- * {{> ../webapi/grabCookie}}
1337
+ * {{> grabCookie }}
1235
1338
  *
1236
1339
  * Returns cookie in JSON format. If name not passed returns all cookies for this domain.
1237
1340
  */
@@ -1243,7 +1346,7 @@ class Puppeteer extends Helper {
1243
1346
  }
1244
1347
 
1245
1348
  /**
1246
- * {{> ../webapi/clearCookie}}
1349
+ * {{> clearCookie }}
1247
1350
  */
1248
1351
  async clearCookie(name) {
1249
1352
  const cookies = await this.page.cookies();
@@ -1256,7 +1359,7 @@ class Puppeteer extends Helper {
1256
1359
  }
1257
1360
 
1258
1361
  /**
1259
- * {{> ../webapi/executeScript }}
1362
+ * {{> executeScript }}
1260
1363
  *
1261
1364
  * If a function returns a Promise It will wait for it resolution.
1262
1365
  */
@@ -1269,7 +1372,7 @@ class Puppeteer extends Helper {
1269
1372
  }
1270
1373
 
1271
1374
  /**
1272
- * {{> ../webapi/executeAsyncScript }}
1375
+ * {{> executeAsyncScript }}
1273
1376
  *
1274
1377
  * Asynchronous scripts can also be executed with `executeScript` if a function returns a Promise.
1275
1378
  */
@@ -1290,7 +1393,8 @@ class Puppeteer extends Helper {
1290
1393
 
1291
1394
 
1292
1395
  /**
1293
- * {{> ../webapi/grabTextFrom }}
1396
+ * {{> grabTextFrom }}
1397
+ * {{ react }}
1294
1398
  */
1295
1399
  async grabTextFrom(locator) {
1296
1400
  const els = await this._locate(locator);
@@ -1304,7 +1408,7 @@ class Puppeteer extends Helper {
1304
1408
  }
1305
1409
 
1306
1410
  /**
1307
- * {{> ../webapi/grabValueFrom }}
1411
+ * {{> grabValueFrom }}
1308
1412
  */
1309
1413
  async grabValueFrom(locator) {
1310
1414
  const els = await findFields.call(this, locator);
@@ -1313,7 +1417,7 @@ class Puppeteer extends Helper {
1313
1417
  }
1314
1418
 
1315
1419
  /**
1316
- * {{> ../webapi/grabHTMLFrom }}
1420
+ * {{> grabHTMLFrom }}
1317
1421
  */
1318
1422
  async grabHTMLFrom(locator) {
1319
1423
  const els = await this._locate(locator);
@@ -1326,7 +1430,8 @@ class Puppeteer extends Helper {
1326
1430
  }
1327
1431
 
1328
1432
  /**
1329
- * {{> ../webapi/grabCssPropertyFrom }}
1433
+ * {{> grabCssPropertyFrom }}
1434
+ * {{ react }}
1330
1435
  */
1331
1436
  async grabCssPropertyFrom(locator, cssProperty) {
1332
1437
  const els = await this._locate(locator);
@@ -1340,7 +1445,8 @@ class Puppeteer extends Helper {
1340
1445
  }
1341
1446
 
1342
1447
  /**
1343
- * {{> ../webapi/seeCssPropertiesOnElements }}
1448
+ * {{> seeCssPropertiesOnElements }}
1449
+ * {{ react }}
1344
1450
  */
1345
1451
  async seeCssPropertiesOnElements(locator, cssProperties) {
1346
1452
  const res = await this._locate(locator);
@@ -1378,7 +1484,8 @@ class Puppeteer extends Helper {
1378
1484
  }
1379
1485
 
1380
1486
  /**
1381
- * {{> ../webapi/seeAttributesOnElements }}
1487
+ * {{> seeAttributesOnElements }}
1488
+ * {{ react }}
1382
1489
  */
1383
1490
  async seeAttributesOnElements(locator, attributes) {
1384
1491
  const res = await this._locate(locator);
@@ -1408,7 +1515,8 @@ class Puppeteer extends Helper {
1408
1515
  }
1409
1516
 
1410
1517
  /**
1411
- * {{> ../webapi/dragSlider }}
1518
+ * {{> dragSlider }}
1519
+ * {{ react }}
1412
1520
  */
1413
1521
  async dragSlider(locator, offsetX = 0) {
1414
1522
  const src = await this._locate(locator);
@@ -1429,7 +1537,8 @@ class Puppeteer extends Helper {
1429
1537
  }
1430
1538
 
1431
1539
  /**
1432
- * {{> ../webapi/grabAttributeFrom }}
1540
+ * {{> grabAttributeFrom }}
1541
+ * {{ react }}
1433
1542
  */
1434
1543
  async grabAttributeFrom(locator, attr) {
1435
1544
  const els = await this._locate(locator);
@@ -1445,7 +1554,7 @@ class Puppeteer extends Helper {
1445
1554
  }
1446
1555
 
1447
1556
  /**
1448
- * {{> ../webapi/saveScreenshot }}
1557
+ * {{> saveScreenshot }}
1449
1558
  */
1450
1559
  async saveScreenshot(fileName, fullPage) {
1451
1560
  const fullPageOption = fullPage || this.options.fullPageScreenshots;
@@ -1464,7 +1573,7 @@ class Puppeteer extends Helper {
1464
1573
  }
1465
1574
 
1466
1575
  /**
1467
- * {{> ../webapi/wait }}
1576
+ * {{> wait }}
1468
1577
  */
1469
1578
  async wait(sec) {
1470
1579
  return new Promise(((done) => {
@@ -1473,7 +1582,7 @@ class Puppeteer extends Helper {
1473
1582
  }
1474
1583
 
1475
1584
  /**
1476
- * {{> ../webapi/waitForEnabled }}
1585
+ * {{> waitForEnabled }}
1477
1586
  */
1478
1587
  async waitForEnabled(locator, sec) {
1479
1588
  const waitTimeout = sec ? sec * 1000 : this.options.waitForTimeout;
@@ -1503,7 +1612,7 @@ class Puppeteer extends Helper {
1503
1612
  }
1504
1613
 
1505
1614
  /**
1506
- * {{> ../webapi/waitForValue }}
1615
+ * {{> waitForValue }}
1507
1616
  */
1508
1617
  async waitForValue(field, value, sec) {
1509
1618
  const waitTimeout = sec ? sec * 1000 : this.options.waitForTimeout;
@@ -1534,7 +1643,8 @@ class Puppeteer extends Helper {
1534
1643
  }
1535
1644
 
1536
1645
  /**
1537
- * {{> ../webapi/waitNumberOfVisibleElements }}
1646
+ * {{> waitNumberOfVisibleElements }}
1647
+ * {{ react }}
1538
1648
  */
1539
1649
  async waitNumberOfVisibleElements(locator, num, sec) {
1540
1650
  const waitTimeout = sec ? sec * 1000 : this.options.waitForTimeout;
@@ -1564,7 +1674,8 @@ class Puppeteer extends Helper {
1564
1674
  }
1565
1675
 
1566
1676
  /**
1567
- * {{> ../webapi/waitForElement }}
1677
+ * {{> waitForElement }}
1678
+ * {{ react }}
1568
1679
  */
1569
1680
  async waitForElement(locator, sec) {
1570
1681
  const waitTimeout = sec ? sec * 1000 : this.options.waitForTimeout;
@@ -1583,7 +1694,9 @@ class Puppeteer extends Helper {
1583
1694
  }
1584
1695
 
1585
1696
  /**
1586
- * {{> ../webapi/waitForVisible }}
1697
+ * {{> waitForVisible }}
1698
+ *
1699
+ * This method accepts [React selectors](https://codecept.io/react).
1587
1700
  */
1588
1701
  async waitForVisible(locator, sec) {
1589
1702
  const waitTimeout = sec ? sec * 1000 : this.options.waitForTimeout;
@@ -1606,7 +1719,7 @@ class Puppeteer extends Helper {
1606
1719
  }
1607
1720
 
1608
1721
  /**
1609
- * {{> ../webapi/waitForInvisible }}
1722
+ * {{> waitForInvisible }}
1610
1723
  */
1611
1724
  async waitForInvisible(locator, sec) {
1612
1725
  const waitTimeout = sec ? sec * 1000 : this.options.waitForTimeout;
@@ -1630,7 +1743,7 @@ class Puppeteer extends Helper {
1630
1743
  }
1631
1744
 
1632
1745
  /**
1633
- * {{> ../webapi/waitToHide }}
1746
+ * {{> waitToHide }}
1634
1747
  */
1635
1748
  async waitToHide(locator, sec) {
1636
1749
  const waitTimeout = sec ? sec * 1000 : this.options.waitForTimeout;
@@ -1659,7 +1772,7 @@ class Puppeteer extends Helper {
1659
1772
  }
1660
1773
 
1661
1774
  /**
1662
- * {{> ../webapi/waitInUrl }}
1775
+ * {{> waitInUrl }}
1663
1776
  */
1664
1777
  async waitInUrl(urlPart, sec = null) {
1665
1778
  const waitTimeout = sec ? sec * 1000 : this.options.waitForTimeout;
@@ -1678,7 +1791,7 @@ class Puppeteer extends Helper {
1678
1791
  }
1679
1792
 
1680
1793
  /**
1681
- * {{> ../webapi/waitUrlEquals }}
1794
+ * {{> waitUrlEquals }}
1682
1795
  */
1683
1796
  async waitUrlEquals(urlPart, sec = null) {
1684
1797
  const waitTimeout = sec ? sec * 1000 : this.options.waitForTimeout;
@@ -1702,7 +1815,7 @@ class Puppeteer extends Helper {
1702
1815
  }
1703
1816
 
1704
1817
  /**
1705
- * {{> ../webapi/waitForText }}
1818
+ * {{> waitForText }}
1706
1819
  */
1707
1820
  async waitForText(text, sec = null, context = null) {
1708
1821
  const waitTimeout = sec ? sec * 1000 : this.options.waitForTimeout;
@@ -1744,8 +1857,8 @@ class Puppeteer extends Helper {
1744
1857
  * I.waitForRequest(request => request.url() === 'http://example.com' && request.method() === 'GET');
1745
1858
  * ```
1746
1859
  *
1747
- * @param {*} urlOrPredicate
1748
- * @param {*} sec
1860
+ * @param {string|function} urlOrPredicate
1861
+ * @param {number?} [sec=null] seconds to wait
1749
1862
  */
1750
1863
  async waitForRequest(urlOrPredicate, sec = null) {
1751
1864
  const timeout = sec ? sec * 1000 : this.options.waitForTimeout;
@@ -1760,8 +1873,8 @@ class Puppeteer extends Helper {
1760
1873
  * I.waitForResponse(request => request.url() === 'http://example.com' && request.method() === 'GET');
1761
1874
  * ```
1762
1875
  *
1763
- * @param {*} urlOrPredicate
1764
- * @param {*} sec
1876
+ * @param {string|function} urlOrPredicate
1877
+ * @param {number?} [sec=null] number of seconds to wait
1765
1878
  */
1766
1879
  async waitForResponse(urlOrPredicate, sec = null) {
1767
1880
  const timeout = sec ? sec * 1000 : this.options.waitForTimeout;
@@ -1769,7 +1882,7 @@ class Puppeteer extends Helper {
1769
1882
  }
1770
1883
 
1771
1884
  /**
1772
- * {{> ../webapi/switchTo }}
1885
+ * {{> switchTo }}
1773
1886
  */
1774
1887
  async switchTo(locator) {
1775
1888
  if (Number.isInteger(locator)) {
@@ -1813,7 +1926,7 @@ class Puppeteer extends Helper {
1813
1926
  }
1814
1927
 
1815
1928
  /**
1816
- * {{> ../webapi/waitForFunction }}
1929
+ * {{> waitForFunction }}
1817
1930
  */
1818
1931
  async waitForFunction(fn, argsOrSec = null, sec = null) {
1819
1932
  let args = [];
@@ -1842,7 +1955,7 @@ class Puppeteer extends Helper {
1842
1955
  }
1843
1956
 
1844
1957
  /**
1845
- * {{> ../webapi/waitUntil }}
1958
+ * {{> waitUntil }}
1846
1959
  */
1847
1960
  async waitUntil(fn, sec = null) {
1848
1961
  console.log('This method will remove in CodeceptJS 1.4; use `waitForFunction` instead!');
@@ -1859,7 +1972,7 @@ class Puppeteer extends Helper {
1859
1972
  }
1860
1973
 
1861
1974
  /**
1862
- * {{> ../webapi/waitForDetached }}
1975
+ * {{> waitForDetached }}
1863
1976
  */
1864
1977
  async waitForDetached(locator, sec) {
1865
1978
  const waitTimeout = sec ? sec * 1000 : this.options.waitForTimeout;
@@ -1887,6 +2000,13 @@ class Puppeteer extends Helper {
1887
2000
  async _waitForAction() {
1888
2001
  return this.wait(this.options.waitForAction / 1000);
1889
2002
  }
2003
+
2004
+ /**
2005
+ * {{> grabDataFromPerformanceTiming }}
2006
+ */
2007
+ async grabDataFromPerformanceTiming() {
2008
+ return perfTiming;
2009
+ }
1890
2010
  }
1891
2011
 
1892
2012
  module.exports = Puppeteer;
@@ -1911,9 +2031,9 @@ async function findFrame(context, name, url) {
1911
2031
  }
1912
2032
 
1913
2033
  async function findElements(matcher, locator) {
2034
+ if (locator.react) return findReact(matcher, locator);
1914
2035
  locator = new Locator(locator, 'css');
1915
2036
  if (!locator.isXPath()) return matcher.$$(locator.simplify());
1916
-
1917
2037
  return matcher.$x(locator.value);
1918
2038
  }
1919
2039
 
@@ -1940,6 +2060,7 @@ async function proceedClick(locator, context = null, options = {}) {
1940
2060
  }
1941
2061
 
1942
2062
  async function findClickable(matcher, locator) {
2063
+ if (locator.react) return findReact(matcher, locator);
1943
2064
  locator = new Locator(locator);
1944
2065
  if (!locator.isFuzzy()) return findElements.call(this, matcher, locator);
1945
2066
 
@@ -2181,7 +2302,11 @@ function $XPath(element, selector) {
2181
2302
  function targetCreatedHandler(page) {
2182
2303
  if (!page) return;
2183
2304
  this.withinLocator = null;
2184
- page.on('load', frame => this.context = page.$('body'));
2305
+ page.on('load', (frame) => {
2306
+ page.$('body')
2307
+ .catch(() => null)
2308
+ .then(context => this.context = context);
2309
+ });
2185
2310
  page.on('console', (msg) => {
2186
2311
  this.debugSection(`Browser:${ucfirst(msg.type())}`, (msg._text || '') + msg.args().join(' '));
2187
2312
  consoleLogStore.add(msg);