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,32 +505,56 @@ class Puppeteer extends Helper {
488
505
  this.context = await this.page.mainFrame().$('body');
489
506
  }
490
507
 
491
- /**
492
- * Opens a web page in a browser. Requires relative or absolute url.
493
- If url starts with `/`, opens a web page of a site defined in `url` config parameter.
508
+ _extractDataFromPerformanceTiming(timing, ...dataNames) {
509
+ const navigationStart = timing.navigationStart;
510
+
511
+ const extractedData = {};
512
+ dataNames.forEach((name) => {
513
+ extractedData[name] = timing[name] - navigationStart;
514
+ });
494
515
 
495
- ```js
496
- I.amOnPage('/'); // opens main page of website
497
- I.amOnPage('https://github.com'); // opens github
498
- I.amOnPage('/login'); // opens a login page
499
- ```
516
+ return extractedData;
517
+ }
500
518
 
501
- @param url url path or global url.
519
+ /**
520
+ * Opens a web page in a browser. Requires relative or absolute url.
521
+ * If url starts with `/`, opens a web page of a site defined in `url` config parameter.
522
+ *
523
+ * ```js
524
+ * I.amOnPage('/'); // opens main page of website
525
+ * I.amOnPage('https://github.com'); // opens github
526
+ * I.amOnPage('/login'); // opens a login page
527
+ * ```
528
+ *
529
+ * @param {string} url url path or global url.
530
+ * {--end--}
502
531
  */
503
532
  async amOnPage(url) {
504
533
  if (!(/^\w+\:\/\//.test(url))) {
505
534
  url = this.options.url + url;
506
535
  }
507
536
  await this.page.goto(url, { waitUntil: this.options.waitForNavigation });
537
+
538
+ const performanceTiming = JSON.parse(await this.page.evaluate(() => JSON.stringify(window.performance.timing)));
539
+
540
+ perfTiming = this._extractDataFromPerformanceTiming(
541
+ performanceTiming,
542
+ 'responseEnd',
543
+ 'domInteractive',
544
+ 'domContentLoadedEventEnd',
545
+ 'loadEventEnd',
546
+ );
547
+
508
548
  return this._waitForAction();
509
549
  }
510
550
 
511
551
  /**
512
552
  * Resize the current window to provided width and height.
513
- First parameter can be set to `maximize`.
514
-
515
- @param width width in pixels or `maximize`.
516
- @param height height in pixels.
553
+ * First parameter can be set to `maximize`.
554
+ *
555
+ * @param {number} width width in pixels or `maximize`.
556
+ * @param {number} height height in pixels.
557
+ * {--end--}
517
558
  *
518
559
  * Unlike other drivers Puppeteer changes the size of a viewport, not the window!
519
560
  * Puppeteer does not control the window of a browser so it can't adjust its real size.
@@ -536,6 +577,8 @@ First parameter can be set to `maximize`.
536
577
  * 'X-Sent-By': 'CodeceptJS',
537
578
  * });
538
579
  * ```
580
+ *
581
+ * @param {object} customHeaders headers to set
539
582
  */
540
583
  async haveRequestHeaders(customHeaders) {
541
584
  if (!customHeaders) {
@@ -546,16 +589,18 @@ First parameter can be set to `maximize`.
546
589
 
547
590
  /**
548
591
  * Moves cursor to element matched by locator.
549
- Extra shift can be set with offsetX and offsetY options.
550
-
551
- ```js
552
- I.moveCursorTo('.tooltip');
553
- I.moveCursorTo('#submit', 5,5);
554
- ```
555
-
556
- @param locator located by CSS|XPath|strict locator.
557
- @param offsetX (optional) X-axis offset.
558
- @param offsetY (optional) Y-axis offset.
592
+ * Extra shift can be set with offsetX and offsetY options.
593
+ *
594
+ * ```js
595
+ * I.moveCursorTo('.tooltip');
596
+ * I.moveCursorTo('#submit', 5,5);
597
+ * ```
598
+ *
599
+ * @param {string|object} locator located by CSS|XPath|strict locator.
600
+ * @param {number} offsetX (optional, `0` by default) X-axis offset.
601
+ * @param {number} offsetY (optional, `0` by default) Y-axis offset.
602
+ * {--end--}
603
+ * {{ react }}
559
604
  */
560
605
  async moveCursorTo(locator, offsetX = 0, offsetY = 0) {
561
606
  const els = await this._locate(locator);
@@ -569,13 +614,14 @@ I.moveCursorTo('#submit', 5,5);
569
614
 
570
615
  /**
571
616
  * Drag an item to a destination element.
572
-
573
- ```js
574
- I.dragAndDrop('#dragHandle', '#container');
575
- ```
576
-
577
- @param srcElement located by CSS|XPath|strict locator.
578
- @param destElement located by CSS|XPath|strict locator.
617
+ *
618
+ * ```js
619
+ * I.dragAndDrop('#dragHandle', '#container');
620
+ * ```
621
+ *
622
+ * @param {string|object} srcElement located by CSS|XPath|strict locator.
623
+ * @param {string|object} destElement located by CSS|XPath|strict locator.
624
+ * {--end--}
579
625
  */
580
626
  async dragAndDrop(srcElement, destElement) {
581
627
  return proceedDragAndDrop.call(this, srcElement, destElement);
@@ -583,11 +629,11 @@ I.dragAndDrop('#dragHandle', '#container');
583
629
 
584
630
  /**
585
631
  * Reload the current page.
586
-
587
- ```js
588
- I.refreshPage();
589
- ```
590
-
632
+ *
633
+ * ```js
634
+ * I.refreshPage();
635
+ * ```
636
+ * {--end--}
591
637
  */
592
638
  async refreshPage() {
593
639
  return this.page.reload({ timeout: this.options.getPageTimeout, waitUntil: this.options.waitForNavigation });
@@ -595,26 +641,28 @@ I.refreshPage();
595
641
 
596
642
  /**
597
643
  * Scroll page to the top.
598
-
599
- ```js
600
- I.scrollPageToTop();
601
- ```
644
+ *
645
+ * ```js
646
+ * I.scrollPageToTop();
647
+ * ```
648
+ * {--end--}
602
649
  */
603
650
  scrollPageToTop() {
604
- return this.page.evaluate(() => {
651
+ return this.executeScript(() => {
605
652
  window.scrollTo(0, 0);
606
653
  });
607
654
  }
608
655
 
609
656
  /**
610
657
  * Scroll page to the bottom.
611
-
612
- ```js
613
- I.scrollPageToBottom();
614
- ```
658
+ *
659
+ * ```js
660
+ * I.scrollPageToBottom();
661
+ * ```
662
+ * {--end--}
615
663
  */
616
664
  scrollPageToBottom() {
617
- return this.page.evaluate(() => {
665
+ return this.executeScript(() => {
618
666
  const body = document.body;
619
667
  const html = document.documentElement;
620
668
  window.scrollTo(0, Math.max(
@@ -626,16 +674,17 @@ I.scrollPageToBottom();
626
674
 
627
675
  /**
628
676
  * Scrolls to element matched by locator.
629
- Extra shift can be set with offsetX and offsetY options.
630
-
631
- ```js
632
- I.scrollTo('footer');
633
- I.scrollTo('#submit', 5, 5);
634
- ```
635
-
636
- @param locator located by CSS|XPath|strict locator.
637
- @param offsetX (optional) X-axis offset.
638
- @param offsetY (optional) Y-axis offset.
677
+ * Extra shift can be set with offsetX and offsetY options.
678
+ *
679
+ * ```js
680
+ * I.scrollTo('footer');
681
+ * I.scrollTo('#submit', 5, 5);
682
+ * ```
683
+ *
684
+ * @param {string|object} locator located by CSS|XPath|strict locator.
685
+ * @param {number} offsetX (optional, `0` by default) X-axis offset.
686
+ * @param {number} offsetY (optional, `0` by default) Y-axis offset.
687
+ * {--end--}
639
688
  */
640
689
  async scrollTo(locator, offsetX = 0, offsetY = 0) {
641
690
  if (typeof locator === 'number' && typeof offsetX === 'number') {
@@ -643,27 +692,28 @@ I.scrollTo('#submit', 5, 5);
643
692
  offsetX = locator;
644
693
  locator = null;
645
694
  }
646
- let x = 0;
647
- let y = 0;
695
+
648
696
  if (locator) {
649
697
  const els = await this._locate(locator);
650
698
  assertElementExists(els, locator, 'Element');
651
699
  await els[0]._scrollIntoViewIfNeeded();
652
700
  const elementCoordinates = await els[0]._clickablePoint();
653
- x = elementCoordinates.x;
654
- y = elementCoordinates.y;
701
+ await this.executeScript((x, y) => window.scrollBy(x, y), elementCoordinates.x + offsetX, elementCoordinates.y + offsetY);
702
+ } else {
703
+ await this.executeScript((x, y) => window.scrollTo(x, y), offsetX, offsetY);
655
704
  }
656
-
657
- await this.page.evaluate((x, y) => {
658
- window.scrollTo(x, y);
659
- }, x + offsetX, y + offsetY);
660
705
  return this._waitForAction();
661
706
  }
662
707
 
663
708
  /**
664
709
  * Checks that title contains text.
665
-
666
- @param text text value to check.
710
+ *
711
+ * ```js
712
+ * I.seeInTitle('Home Page');
713
+ * ```
714
+ *
715
+ * @param {string} text text value to check.
716
+ * {--end--}
667
717
  */
668
718
  async seeInTitle(text) {
669
719
  const title = await this.page.title();
@@ -672,11 +722,14 @@ I.scrollTo('#submit', 5, 5);
672
722
 
673
723
  /**
674
724
  * Retrieves a page scroll position and returns it to test.
675
- Resumes test execution, so **should be used inside an async function with `await`** operator.
676
-
677
- ```js
678
- let { x, y } = await I.grabPageScrollPosition();
679
- ```
725
+ * Resumes test execution, so **should be used inside an async function with `await`** operator.
726
+ *
727
+ * ```js
728
+ * let { x, y } = await I.grabPageScrollPosition();
729
+ * ```
730
+ *
731
+ * @returns {Promise<object>} scroll position
732
+ * {--end--}
680
733
  */
681
734
  async grabPageScrollPosition() {
682
735
  /* eslint-disable comma-dangle */
@@ -704,8 +757,13 @@ let { x, y } = await I.grabPageScrollPosition();
704
757
 
705
758
  /**
706
759
  * Checks that title does not contain text.
707
-
708
- @param text text value to check.
760
+ *
761
+ * ```js
762
+ * I.dontSeeInTitle('Error');
763
+ * ```
764
+ *
765
+ * @param {string} text value to check.
766
+ * {--end--}
709
767
  */
710
768
  async dontSeeInTitle(text) {
711
769
  const title = await this.page.title();
@@ -714,11 +772,14 @@ let { x, y } = await I.grabPageScrollPosition();
714
772
 
715
773
  /**
716
774
  * Retrieves a page title and returns it to test.
717
- Resumes test execution, so **should be used inside async with `await`** operator.
718
-
719
- ```js
720
- let title = await I.grabTitle();
721
- ```
775
+ * Resumes test execution, so **should be used inside async with `await`** operator.
776
+ *
777
+ * ```js
778
+ * let title = await I.grabTitle();
779
+ * ```
780
+ *
781
+ * @returns {Promise<string>} title
782
+ * {--end--}
722
783
  */
723
784
  async grabTitle() {
724
785
  return this.page.title();
@@ -731,6 +792,8 @@ let title = await I.grabTitle();
731
792
  * ```js
732
793
  * const elements = await this.helpers['Puppeteer']._locate({name: 'password'});
733
794
  * ```
795
+ *
796
+ * {{ react }}
734
797
  */
735
798
  async _locate(locator) {
736
799
  return findElements(await this.context, locator);
@@ -781,6 +844,8 @@ let title = await I.grabTitle();
781
844
  * I.switchToNextTab();
782
845
  * I.switchToNextTab(2);
783
846
  * ```
847
+ *
848
+ * @param {number} [num=1]
784
849
  */
785
850
  async switchToNextTab(num = 1) {
786
851
  const pages = await this.browser.pages();
@@ -803,6 +868,7 @@ let title = await I.grabTitle();
803
868
  * I.switchToPreviousTab();
804
869
  * I.switchToPreviousTab(2);
805
870
  * ```
871
+ * @param {number} [num=1]
806
872
  */
807
873
  async switchToPreviousTab(num = 1) {
808
874
  const pages = await this.browser.pages();
@@ -865,10 +931,13 @@ let title = await I.grabTitle();
865
931
 
866
932
  /**
867
933
  * Grab number of open tabs.
868
-
869
- ```js
870
- I.grabNumberOfOpenTabs();
871
- ```
934
+ *
935
+ * ```js
936
+ * let tabs = await I.grabNumberOfOpenTabs();
937
+ * ```
938
+ *
939
+ * @returns {Promise<number>} number of open tabs
940
+ * {--end--}
872
941
  */
873
942
  async grabNumberOfOpenTabs() {
874
943
  const pages = await this.browser.pages();
@@ -877,14 +946,15 @@ I.grabNumberOfOpenTabs();
877
946
 
878
947
  /**
879
948
  * Checks that a given Element is visible
880
- Element is located by CSS or XPath.
881
-
882
- ```js
883
- I.seeElement('#modal');
884
- ```
885
- @param locator located by CSS|XPath|strict locator.
949
+ * Element is located by CSS or XPath.
950
+ *
951
+ * ```js
952
+ * I.seeElement('#modal');
953
+ * ```
954
+ * @param {string|object} locator located by CSS|XPath|strict locator.
955
+ * {--end--}
956
+ * {{ react }}
886
957
  */
887
-
888
958
  async seeElement(locator) {
889
959
  let els = await this._locate(locator);
890
960
  els = await Promise.all(els.map(el => el.boundingBox()));
@@ -893,8 +963,14 @@ I.seeElement('#modal');
893
963
 
894
964
  /**
895
965
  * Opposite to `seeElement`. Checks that element is not visible (or in DOM)
896
-
897
- @param locator located by CSS|XPath|Strict locator.
966
+ *
967
+ * ```js
968
+ * I.dontSeeElement('.modal'); // modal is not shown
969
+ * ```
970
+ *
971
+ * @param {string|object} locator located by CSS|XPath|Strict locator.
972
+ * {--end--}
973
+ * {{ react }}
898
974
  */
899
975
  async dontSeeElement(locator) {
900
976
  let els = await this._locate(locator);
@@ -904,12 +980,13 @@ I.seeElement('#modal');
904
980
 
905
981
  /**
906
982
  * Checks that a given Element is present in the DOM
907
- Element is located by CSS or XPath.
908
-
909
- ```js
910
- I.seeElementInDOM('#modal');
911
- ```
912
- @param locator located by CSS|XPath|strict locator.
983
+ * Element is located by CSS or XPath.
984
+ *
985
+ * ```js
986
+ * I.seeElementInDOM('#modal');
987
+ * ```
988
+ * @param {string|object} locator element located by CSS|XPath|strict locator.
989
+ * {--end--}
913
990
  */
914
991
  async seeElementInDOM(locator) {
915
992
  const els = await this._locate(locator);
@@ -918,8 +995,13 @@ I.seeElementInDOM('#modal');
918
995
 
919
996
  /**
920
997
  * Opposite to `seeElementInDOM`. Checks that element is not on page.
921
-
922
- @param locator located by CSS|XPath|Strict locator.
998
+ *
999
+ * ```js
1000
+ * I.dontSeeElementInDOM('.nav'); // checks that element is not on page visible or not
1001
+ * ```
1002
+ *
1003
+ * @param {string|object} locator located by CSS|XPath|Strict locator.
1004
+ * {--end--}
923
1005
  */
924
1006
  async dontSeeElementInDOM(locator) {
925
1007
  const els = await this._locate(locator);
@@ -928,29 +1010,32 @@ I.seeElementInDOM('#modal');
928
1010
 
929
1011
  /**
930
1012
  * Perform a click on a link or a button, given by a locator.
931
- If a fuzzy locator is given, the page will be searched for a button, link, or image matching the locator string.
932
- For buttons, the "value" attribute, "name" attribute, and inner text are searched. For links, the link text is searched.
933
- For images, the "alt" attribute and inner text of any parent links are searched.
934
-
935
- The second parameter is a context (CSS or XPath locator) to narrow the search.
936
-
937
- ```js
938
- // simple link
939
- I.click('Logout');
940
- // button of form
941
- I.click('Submit');
942
- // CSS button
943
- I.click('#form input[type=submit]');
944
- // XPath
945
- I.click('//form/*[@type=submit]');
946
- // link in context
947
- I.click('Logout', '#nav');
948
- // using strict locator
949
- I.click({css: 'nav a.login'});
950
- ```
951
-
952
- @param locator clickable link or button located by text, or any element located by CSS|XPath|strict locator.
953
- @param context (optional) element to search in CSS|XPath|Strict locator.
1013
+ * If a fuzzy locator is given, the page will be searched for a button, link, or image matching the locator string.
1014
+ * For buttons, the "value" attribute, "name" attribute, and inner text are searched. For links, the link text is searched.
1015
+ * For images, the "alt" attribute and inner text of any parent links are searched.
1016
+ *
1017
+ * The second parameter is a context (CSS or XPath locator) to narrow the search.
1018
+ *
1019
+ * ```js
1020
+ * // simple link
1021
+ * I.click('Logout');
1022
+ * // button of form
1023
+ * I.click('Submit');
1024
+ * // CSS button
1025
+ * I.click('#form input[type=submit]');
1026
+ * // XPath
1027
+ * I.click('//form/*[@type=submit]');
1028
+ * // link in context
1029
+ * I.click('Logout', '#nav');
1030
+ * // using strict locator
1031
+ * I.click({css: 'nav a.login'});
1032
+ * ```
1033
+ *
1034
+ * @param {string|object} locator clickable link or button located by text, or any element located by CSS|XPath|strict locator.
1035
+ * @param {string|object} context (optional, `null` by default) element to search in CSS|XPath|Strict locator.
1036
+ * {--end--}
1037
+ *
1038
+ * {{ react }}
954
1039
  */
955
1040
  async click(locator, context = null) {
956
1041
  return proceedClick.call(this, locator, context);
@@ -958,45 +1043,74 @@ I.click({css: 'nav a.login'});
958
1043
 
959
1044
  /**
960
1045
  * Performs a click on a link and waits for navigation before moving on.
961
-
962
- ```js
963
- I.click('Logout', '#nav');
964
- ```
965
- @param locator clickable link or button located by text, or any element located by CSS|XPath|strict locator
966
- @param context (optional) element to search in CSS|XPath|Strict locator
1046
+ *
1047
+ * ```js
1048
+ * I.clickLink('Logout', '#nav');
1049
+ * ```
1050
+ * @param {string|object} locator clickable link or button located by text, or any element located by CSS|XPath|strict locator
1051
+ * @param {string|object} context (optional, `null` by default) element to search in CSS|XPath|Strict locator
1052
+ * {--end--}
1053
+ *
1054
+ * {{ react }}
967
1055
  */
968
1056
  async clickLink(locator, context = null) {
969
1057
  return proceedClick.call(this, locator, context, { waitForNavigation: true });
970
1058
  }
971
1059
 
972
1060
  /**
973
- * Performs a download file on an element matched by link|button|CSS or XPath.
974
- File is downloaded by default to output folder.
975
- If no custom file name is provided, the default name will be used
976
-
977
-
978
- ```js
979
- I.downloadFile('td[class="text-right file-link"] a', 'thisIsCustomName');
980
- ```
1061
+ * Sets a directory to where save files. Allows to test file downloads.
1062
+ * Should be used with [FileSystem helper](https://codecept.io/helpers/FileSystem) to check that file were downloaded correctly.
1063
+ *
1064
+ * By default files are saved to `output/downloads`.
1065
+ * This directory is cleaned on every `handleDownloads` call, to ensure no old files are kept.
1066
+ *
1067
+ * ```js
1068
+ * I.handleDownloads();
1069
+ * I.click('Download Avatar');
1070
+ * I.amInPath('output/downloads');
1071
+ * I.seeFile('avatar.jpg');
1072
+ *
1073
+ * ```
1074
+ *
1075
+ * @param {string} [downloadPath='downloads'] change this parameter to set another directory for saving
1076
+ */
1077
+ async handleDownloads(downloadPath = 'downloads') {
1078
+ downloadPath = path.join(global.output_dir, downloadPath);
1079
+ if (!fs.existsSync(downloadPath)) {
1080
+ fs.mkdirSync(downloadPath, '0777');
1081
+ }
1082
+ fsExtra.emptyDirSync(downloadPath);
1083
+ return this.page._client.send('Page.setDownloadBehavior', { behavior: 'allow', downloadPath });
1084
+ }
981
1085
 
982
- @param locator clickable link or button located by CSS|XPath locator.
983
- @param string custom file name.
1086
+ /**
1087
+ * This method is **depreacted**.
1088
+ *
1089
+ * Please use `handleDownloads()` instead.
984
1090
  */
985
1091
  async downloadFile(locator, customName) {
986
1092
  let fileName;
987
1093
  await this.page.setRequestInterception(true);
988
- this.click(locator);
989
1094
 
990
1095
  const xRequest = await new Promise((resolve) => {
991
1096
  this.page.on('request', (request) => {
1097
+ console.log('rq', request, customName);
992
1098
  const grabbedFileName = request.url().split('/')[request.url().split('/').length - 1];
993
1099
  const fileExtension = request.url().split('/')[request.url().split('/').length - 1].split('.')[1];
1100
+ console.log('nm', customName, fileExtension);
1101
+ if (customName && path.extname(customName) !== fileExtension) {
1102
+ console.log('bypassing a request');
1103
+ request.continue();
1104
+ return;
1105
+ }
994
1106
  customName ? fileName = `${customName}.${fileExtension}` : fileName = grabbedFileName;
995
1107
  request.abort();
996
1108
  resolve(request);
997
1109
  });
998
1110
  });
999
1111
 
1112
+ await this.click(locator);
1113
+
1000
1114
  const options = {
1001
1115
  encoding: null,
1002
1116
  method: xRequest._method,
@@ -1009,14 +1123,21 @@ I.downloadFile('td[class="text-right file-link"] a', 'thisIsCustomName');
1009
1123
  options.headers.Cookie = cookies.map(ck => `${ck.name}=${ck.value}`).join(';');
1010
1124
 
1011
1125
  const response = await axios({
1012
- method: options.method, url: options.uri, headers: options.headers, responseType: 'arraybuffer',
1126
+ method: options.method,
1127
+ url: options.uri,
1128
+ headers: options.headers,
1129
+ responseType: 'arraybuffer',
1130
+ onDownloadProgress(e) {
1131
+ console.log('+', e);
1132
+ },
1013
1133
  });
1014
1134
 
1015
1135
  const outputFile = path.join(`${global.output_dir}/${fileName}`);
1016
1136
 
1017
1137
  try {
1018
- return new Promise((resolve, reject) => {
1138
+ await new Promise((resolve, reject) => {
1019
1139
  const wstream = fs.createWriteStream(outputFile);
1140
+ console.log(response);
1020
1141
  wstream.write(response.data);
1021
1142
  wstream.end();
1022
1143
  this.debug(`File is downloaded in ${outputFile}`);
@@ -1030,17 +1151,20 @@ I.downloadFile('td[class="text-right file-link"] a', 'thisIsCustomName');
1030
1151
 
1031
1152
  /**
1032
1153
  * Performs a double-click on an element matched by link|button|label|CSS or XPath.
1033
- Context can be specified as second parameter to narrow search.
1034
-
1035
- ```js
1036
- I.doubleClick('Edit');
1037
- I.doubleClick('Edit', '.actions');
1038
- I.doubleClick({css: 'button.accept'});
1039
- I.doubleClick('.btn.edit');
1040
- ```
1041
-
1042
- @param locator clickable link or button located by text, or any element located by CSS|XPath|strict locator.
1043
- @param context (optional) element to search in CSS|XPath|Strict locator.
1154
+ * Context can be specified as second parameter to narrow search.
1155
+ *
1156
+ * ```js
1157
+ * I.doubleClick('Edit');
1158
+ * I.doubleClick('Edit', '.actions');
1159
+ * I.doubleClick({css: 'button.accept'});
1160
+ * I.doubleClick('.btn.edit');
1161
+ * ```
1162
+ *
1163
+ * @param {string|object} locator clickable link or button located by text, or any element located by CSS|XPath|strict locator.
1164
+ * @param {string|object} context (optional, `null` by default) element to search in CSS|XPath|Strict locator.
1165
+ * {--end--}
1166
+ *
1167
+ * {{ react }}
1044
1168
  */
1045
1169
  async doubleClick(locator, context = null) {
1046
1170
  return proceedClick.call(this, locator, context, { clickCount: 2 });
@@ -1048,19 +1172,21 @@ I.doubleClick('.btn.edit');
1048
1172
 
1049
1173
  /**
1050
1174
  * Performs right click on a clickable element matched by semantic locator, CSS or XPath.
1051
-
1052
- ```js
1053
- // right click element with id el
1054
- I.rightClick('#el');
1055
- // right click link or button with text "Click me"
1056
- I.rightClick('Click me');
1057
- // right click button with text "Click me" inside .context
1058
- I.rightClick('Click me', '.context');
1059
- ```
1060
-
1061
- @param locator clickable element located by CSS|XPath|strict locator.
1062
- @param context (optional) element located by CSS|XPath|strict locator.
1063
-
1175
+ *
1176
+ * ```js
1177
+ * // right click element with id el
1178
+ * I.rightClick('#el');
1179
+ * // right click link or button with text "Click me"
1180
+ * I.rightClick('Click me');
1181
+ * // right click button with text "Click me" inside .context
1182
+ * I.rightClick('Click me', '.context');
1183
+ * ```
1184
+ *
1185
+ * @param {string|object} locator clickable element located by CSS|XPath|strict locator.
1186
+ * @param {string|object} context (optional, `null` by default) element located by CSS|XPath|strict locator.
1187
+ * {--end--}
1188
+ *
1189
+ * {{ react }}
1064
1190
  */
1065
1191
  async rightClick(locator, context = null) {
1066
1192
  return proceedClick.call(this, locator, context, { button: 'right' });
@@ -1068,17 +1194,18 @@ I.rightClick('Click me', '.context');
1068
1194
 
1069
1195
  /**
1070
1196
  * Selects a checkbox or radio button.
1071
- Element is located by label or name or CSS or XPath.
1072
-
1073
- The second parameter is a context (CSS or XPath locator) to narrow the search.
1074
-
1075
- ```js
1076
- I.checkOption('#agree');
1077
- I.checkOption('I Agree to Terms and Conditions');
1078
- I.checkOption('agree', '//form');
1079
- ```
1080
- @param field checkbox located by label | name | CSS | XPath | strict locator.
1081
- @param context (optional) element located by CSS | XPath | strict locator.
1197
+ * Element is located by label or name or CSS or XPath.
1198
+ *
1199
+ * The second parameter is a context (CSS or XPath locator) to narrow the search.
1200
+ *
1201
+ * ```js
1202
+ * I.checkOption('#agree');
1203
+ * I.checkOption('I Agree to Terms and Conditions');
1204
+ * I.checkOption('agree', '//form');
1205
+ * ```
1206
+ * @param {string|object} field checkbox located by label | name | CSS | XPath | strict locator.
1207
+ * @param {string} context (optional, `null` by default) element located by CSS | XPath | strict locator.
1208
+ * {--end--}
1082
1209
  */
1083
1210
  async checkOption(field, context = null) {
1084
1211
  const elm = await this._locateCheckable(field, context);
@@ -1093,17 +1220,18 @@ I.checkOption('agree', '//form');
1093
1220
 
1094
1221
  /**
1095
1222
  * Unselects a checkbox or radio button.
1096
- Element is located by label or name or CSS or XPath.
1097
-
1098
- The second parameter is a context (CSS or XPath locator) to narrow the search.
1099
-
1100
- ```js
1101
- I.uncheckOption('#agree');
1102
- I.uncheckOption('I Agree to Terms and Conditions');
1103
- I.uncheckOption('agree', '//form');
1104
- ```
1105
- @param field checkbox located by label | name | CSS | XPath | strict locator.
1106
- @param context (optional) element located by CSS | XPath | strict locator.
1223
+ * Element is located by label or name or CSS or XPath.
1224
+ *
1225
+ * The second parameter is a context (CSS or XPath locator) to narrow the search.
1226
+ *
1227
+ * ```js
1228
+ * I.uncheckOption('#agree');
1229
+ * I.uncheckOption('I Agree to Terms and Conditions');
1230
+ * I.uncheckOption('agree', '//form');
1231
+ * ```
1232
+ * @param {string|object} field checkbox located by label | name | CSS | XPath | strict locator.
1233
+ * @param {string} context (optional, `null` by default) element located by CSS | XPath | strict locator.
1234
+ * {--end--}
1107
1235
  */
1108
1236
  async uncheckOption(field, context = null) {
1109
1237
  const elm = await this._locateCheckable(field, context);
@@ -1118,13 +1246,15 @@ I.uncheckOption('agree', '//form');
1118
1246
 
1119
1247
  /**
1120
1248
  * Verifies that the specified checkbox is checked.
1121
-
1122
- ```js
1123
- I.seeCheckboxIsChecked('Agree');
1124
- I.seeCheckboxIsChecked('#agree'); // I suppose user agreed to terms
1125
- I.seeCheckboxIsChecked({css: '#signup_form input[type=checkbox]'});
1126
- ```
1127
- @param field located by label|name|CSS|XPath|strict locator.
1249
+ *
1250
+ * ```js
1251
+ * I.seeCheckboxIsChecked('Agree');
1252
+ * I.seeCheckboxIsChecked('#agree'); // I suppose user agreed to terms
1253
+ * I.seeCheckboxIsChecked({css: '#signup_form input[type=checkbox]'});
1254
+ * ```
1255
+ *
1256
+ * @param {string|object} field located by label|name|CSS|XPath|strict locator.
1257
+ * {--end--}
1128
1258
  */
1129
1259
  async seeCheckboxIsChecked(field) {
1130
1260
  return proceedIsChecked.call(this, 'assert', field);
@@ -1132,8 +1262,15 @@ I.seeCheckboxIsChecked({css: '#signup_form input[type=checkbox]'});
1132
1262
 
1133
1263
  /**
1134
1264
  * Verifies that the specified checkbox is not checked.
1135
-
1136
- @param field located by label|name|CSS|XPath|strict locator.
1265
+ *
1266
+ * ```js
1267
+ * I.dontSeeeCheckboxIsChedcked('#agree'); // located by ID
1268
+ * I.dontSeeeCheckboxIsChedcked('I agree to terms'); // located by label
1269
+ * I.dontSeeeCheckboxIsChedcked('agree'); // located by name
1270
+ * ```
1271
+ *
1272
+ * @param {string|object} field located by label|name|CSS|XPath|strict locator.
1273
+ * {--end--}
1137
1274
  */
1138
1275
  async dontSeeCheckboxIsChecked(field) {
1139
1276
  return proceedIsChecked.call(this, 'negate', field);
@@ -1141,16 +1278,19 @@ I.seeCheckboxIsChecked({css: '#signup_form input[type=checkbox]'});
1141
1278
 
1142
1279
  /**
1143
1280
  * Presses a key on a focused element.
1144
- Special keys like 'Enter', 'Control', [etc](https://code.google.com/p/selenium/wiki/JsonWireProtocol#/session/:sessionId/element/:id/value)
1145
- will be replaced with corresponding unicode.
1146
- If modifier key is used (Control, Command, Alt, Shift) in array, it will be released afterwards.
1147
-
1148
- ```js
1149
- I.pressKey('Enter');
1150
- I.pressKey(['Control','a']);
1151
- ```
1152
-
1153
- @param key key or array of keys to press.
1281
+ * Special keys like 'Enter', 'Control', [etc](https://code.google.com/p/selenium/wiki/JsonWireProtocol#/session/:sessionId/element/:id/value)
1282
+ * will be replaced with corresponding unicode.
1283
+ * If modifier key is used (Control, Command, Alt, Shift) in array, it will be released afterwards.
1284
+ *
1285
+ * ```js
1286
+ * I.pressKey('Enter');
1287
+ * I.pressKey(['Control','a']);
1288
+ * ```
1289
+ *
1290
+ * @param {string|array} key key or array of keys to press.
1291
+ * {--end--}
1292
+ *
1293
+ * {{ keys }}
1154
1294
  */
1155
1295
  async pressKey(key) {
1156
1296
  let modifier;
@@ -1167,21 +1307,22 @@ I.pressKey(['Control','a']);
1167
1307
 
1168
1308
  /**
1169
1309
  * Fills a text field or textarea, after clearing its value, with the given string.
1170
- Field is located by name, label, CSS, or XPath.
1171
-
1172
- ```js
1173
- // by label
1174
- I.fillField('Email', 'hello@world.com');
1175
- // by name
1176
- I.fillField('password', secret('123456'));
1177
- // by CSS
1178
- I.fillField('form#login input[name=username]', 'John');
1179
- // or by strict locator
1180
- I.fillField({css: 'form#login input[name=username]'}, 'John');
1181
- ```
1182
- @param field located by label|name|CSS|XPath|strict locator.
1183
- @param value text value to fill.
1184
-
1310
+ * Field is located by name, label, CSS, or XPath.
1311
+ *
1312
+ * ```js
1313
+ * // by label
1314
+ * I.fillField('Email', 'hello@world.com');
1315
+ * // by name
1316
+ * I.fillField('password', secret('123456'));
1317
+ * // by CSS
1318
+ * I.fillField('form#login input[name=username]', 'John');
1319
+ * // or by strict locator
1320
+ * I.fillField({css: 'form#login input[name=username]'}, 'John');
1321
+ * ```
1322
+ * @param {string|object} field located by label|name|CSS|XPath|strict locator.
1323
+ * @param {string} value text value to fill.
1324
+ * {--end--}
1325
+ * {{ react }}
1185
1326
  */
1186
1327
  async fillField(field, value) {
1187
1328
  const els = await findFields.call(this, field);
@@ -1195,20 +1336,21 @@ I.fillField({css: 'form#login input[name=username]'}, 'John');
1195
1336
  } else if (editable) {
1196
1337
  await this._evaluateHandeInContext(el => el.innerHTML = '', el);
1197
1338
  }
1198
- await el.type(value.toString(), { delay: 10 });
1339
+ await el.type(value.toString(), { delay: this.options.pressKeyDelay });
1199
1340
  return this._waitForAction();
1200
1341
  }
1201
1342
 
1202
1343
 
1203
1344
  /**
1204
1345
  * Clears a `<textarea>` or text `<input>` element's value.
1205
-
1206
- ```js
1207
- I.clearField('Email');
1208
- I.clearField('user[email]');
1209
- I.clearField('#email');
1210
- ```
1211
- @param field located by label|name|CSS|XPath|strict locator.
1346
+ *
1347
+ * ```js
1348
+ * I.clearField('Email');
1349
+ * I.clearField('user[email]');
1350
+ * I.clearField('#email');
1351
+ * ```
1352
+ * @param {string|object} editable field located by label|name|CSS|XPath|strict locator.
1353
+ * {--end--}
1212
1354
  */
1213
1355
  async clearField(field) {
1214
1356
  return this.fillField(field, '');
@@ -1216,34 +1358,38 @@ I.clearField('#email');
1216
1358
 
1217
1359
  /**
1218
1360
  * Appends text to a input field or textarea.
1219
- Field is located by name, label, CSS or XPath
1220
-
1221
- ```js
1222
- I.appendField('#myTextField', 'appended');
1223
- ```
1224
- @param field located by label|name|CSS|XPath|strict locator
1225
- @param value text value to append.
1361
+ * Field is located by name, label, CSS or XPath
1362
+ *
1363
+ * ```js
1364
+ * I.appendField('#myTextField', 'appended');
1365
+ * ```
1366
+ * @param {string|object} field located by label|name|CSS|XPath|strict locator
1367
+ * @param {string} value text value to append.
1368
+ * {--end--}
1369
+ *
1370
+ * {{ react }}
1226
1371
  */
1227
1372
  async appendField(field, value) {
1228
1373
  const els = await findFields.call(this, field);
1229
1374
  assertElementExists(els, field, 'Field');
1230
1375
  await els[0].press('End');
1231
- await els[0].type(value, { delay: 10 });
1376
+ await els[0].type(value, { delay: this.options.pressKeyDelay });
1232
1377
  return this._waitForAction();
1233
1378
  }
1234
1379
 
1235
1380
  /**
1236
1381
  * Checks that the given input field or textarea equals to given value.
1237
- For fuzzy locators, fields are matched by label text, the "name" attribute, CSS, and XPath.
1238
-
1239
- ```js
1240
- I.seeInField('Username', 'davert');
1241
- I.seeInField({css: 'form textarea'},'Type your comment here');
1242
- I.seeInField('form input[type=hidden]','hidden_value');
1243
- I.seeInField('#searchform input','Search');
1244
- ```
1245
- @param field located by label|name|CSS|XPath|strict locator.
1246
- @param value value to check.
1382
+ * For fuzzy locators, fields are matched by label text, the "name" attribute, CSS, and XPath.
1383
+ *
1384
+ * ```js
1385
+ * I.seeInField('Username', 'davert');
1386
+ * I.seeInField({css: 'form textarea'},'Type your comment here');
1387
+ * I.seeInField('form input[type=hidden]','hidden_value');
1388
+ * I.seeInField('#searchform input','Search');
1389
+ * ```
1390
+ * @param {string|object} field located by label|name|CSS|XPath|strict locator.
1391
+ * @param {string} value value to check.
1392
+ * {--end--}
1247
1393
  */
1248
1394
  async seeInField(field, value) {
1249
1395
  return proceedSeeInField.call(this, 'assert', field, value);
@@ -1251,10 +1397,16 @@ I.seeInField('#searchform input','Search');
1251
1397
 
1252
1398
  /**
1253
1399
  * Checks that value of input field or textare doesn't equal to given value
1254
- Opposite to `seeInField`.
1255
-
1256
- @param field located by label|name|CSS|XPath|strict locator.
1257
- @param value value to check.
1400
+ * Opposite to `seeInField`.
1401
+ *
1402
+ * ```js
1403
+ * I.dontSeeInField('email', 'user@user.com'); // field by name
1404
+ * I.dontSeeInField({ css: 'form input.email' }, 'user@user.com'); // field by CSS
1405
+ * ```
1406
+ *
1407
+ * @param {string|object} field located by label|name|CSS|XPath|strict locator.
1408
+ * @param {string} value value to check.
1409
+ * {--end--}
1258
1410
  */
1259
1411
  async dontSeeInField(field, value) {
1260
1412
  return proceedSeeInField.call(this, 'negate', field, value);
@@ -1263,17 +1415,17 @@ Opposite to `seeInField`.
1263
1415
 
1264
1416
  /**
1265
1417
  * Attaches a file to element located by label, name, CSS or XPath
1266
- Path to file is relative current codecept directory (where codecept.json or codecept.conf.js is located).
1267
- File will be uploaded to remote system (if tests are running remotely).
1268
-
1269
- ```js
1270
- I.attachFile('Avatar', 'data/avatar.jpg');
1271
- I.attachFile('form input[name=avatar]', 'data/avatar.jpg');
1272
- ```
1273
-
1274
- @param locator field located by label|name|CSS|XPath|strict locator.
1275
- @param pathToFile local file path relative to codecept.json config file.
1276
- *
1418
+ * Path to file is relative current codecept directory (where codecept.json or codecept.conf.js is located).
1419
+ * File will be uploaded to remote system (if tests are running remotely).
1420
+ *
1421
+ * ```js
1422
+ * I.attachFile('Avatar', 'data/avatar.jpg');
1423
+ * I.attachFile('form input[name=avatar]', 'data/avatar.jpg');
1424
+ * ```
1425
+ *
1426
+ * @param {string|object} locator field located by label|name|CSS|XPath|strict locator.
1427
+ * @param {string} pathToFile local file path relative to codecept.json config file.
1428
+ * {--end--}
1277
1429
  */
1278
1430
  async attachFile(locator, pathToFile) {
1279
1431
  const file = path.join(global.codecept_dir, pathToFile);
@@ -1289,25 +1441,26 @@ I.attachFile('form input[name=avatar]', 'data/avatar.jpg');
1289
1441
 
1290
1442
  /**
1291
1443
  * Selects an option in a drop-down select.
1292
- Field is searched by label | name | CSS | XPath.
1293
- Option is selected by visible text or by value.
1294
-
1295
- ```js
1296
- I.selectOption('Choose Plan', 'Monthly'); // select by label
1297
- I.selectOption('subscription', 'Monthly'); // match option by text
1298
- I.selectOption('subscription', '0'); // or by value
1299
- I.selectOption('//form/select[@name=account]','Premium');
1300
- I.selectOption('form select[name=account]', 'Premium');
1301
- I.selectOption({css: 'form select[name=account]'}, 'Premium');
1302
- ```
1303
-
1304
- Provide an array for the second argument to select multiple options.
1305
-
1306
- ```js
1307
- I.selectOption('Which OS do you use?', ['Android', 'iOS']);
1308
- ```
1309
- @param select field located by label|name|CSS|XPath|strict locator.
1310
- @param option visible text or value of option.
1444
+ * Field is searched by label | name | CSS | XPath.
1445
+ * Option is selected by visible text or by value.
1446
+ *
1447
+ * ```js
1448
+ * I.selectOption('Choose Plan', 'Monthly'); // select by label
1449
+ * I.selectOption('subscription', 'Monthly'); // match option by text
1450
+ * I.selectOption('subscription', '0'); // or by value
1451
+ * I.selectOption('//form/select[@name=account]','Premium');
1452
+ * I.selectOption('form select[name=account]', 'Premium');
1453
+ * I.selectOption({css: 'form select[name=account]'}, 'Premium');
1454
+ * ```
1455
+ *
1456
+ * Provide an array for the second argument to select multiple options.
1457
+ *
1458
+ * ```js
1459
+ * I.selectOption('Which OS do you use?', ['Android', 'iOS']);
1460
+ * ```
1461
+ * @param {string|object} select field located by label|name|CSS|XPath|strict locator.
1462
+ * @param {string|array} option visible text or value of option.
1463
+ * {--end--}
1311
1464
  */
1312
1465
  async selectOption(select, option) {
1313
1466
  const els = await findFields.call(this, select);
@@ -1340,12 +1493,15 @@ I.selectOption('Which OS do you use?', ['Android', 'iOS']);
1340
1493
 
1341
1494
  /**
1342
1495
  * Grab number of visible elements by locator.
1343
-
1344
- ```js
1345
- I.grabNumberOfVisibleElements('p');
1346
- ```
1347
-
1348
- @param locator located by CSS|XPath|strict locator.
1496
+ *
1497
+ * ```js
1498
+ * let numOfElements = await I.grabNumberOfVisibleElements('p');
1499
+ * ```
1500
+ *
1501
+ * @param {string|object} locator located by CSS|XPath|strict locator.
1502
+ * @returns {Promise<number>} number of visible elements
1503
+ * {--end--}
1504
+ * {{ react }}
1349
1505
  */
1350
1506
  async grabNumberOfVisibleElements(locator) {
1351
1507
  let els = await this._locate(locator);
@@ -1355,12 +1511,13 @@ I.grabNumberOfVisibleElements('p');
1355
1511
 
1356
1512
  /**
1357
1513
  * Checks that current url contains a provided fragment.
1358
-
1359
- ```js
1360
- I.seeInCurrentUrl('/register'); // we are on registration page
1361
- ```
1362
-
1363
- @param url value to check.
1514
+ *
1515
+ * ```js
1516
+ * I.seeInCurrentUrl('/register'); // we are on registration page
1517
+ * ```
1518
+ *
1519
+ * @param {string} url a fragment to check
1520
+ * {--end--}
1364
1521
  */
1365
1522
  async seeInCurrentUrl(url) {
1366
1523
  stringIncludes('url').assert(url, await this._getPageUrl());
@@ -1368,8 +1525,9 @@ I.seeInCurrentUrl('/register'); // we are on registration page
1368
1525
 
1369
1526
  /**
1370
1527
  * Checks that current url does not contain a provided fragment.
1371
-
1372
- @param url value to check.
1528
+ *
1529
+ * @param {string} url value to check.
1530
+ * {--end--}
1373
1531
  */
1374
1532
  async dontSeeInCurrentUrl(url) {
1375
1533
  stringIncludes('url').negate(url, await this._getPageUrl());
@@ -1377,15 +1535,16 @@ I.seeInCurrentUrl('/register'); // we are on registration page
1377
1535
 
1378
1536
  /**
1379
1537
  * Checks that current url is equal to provided one.
1380
- If a relative url provided, a configured url will be prepended to it.
1381
- So both examples will work:
1382
-
1383
- ```js
1384
- I.seeCurrentUrlEquals('/register');
1385
- I.seeCurrentUrlEquals('http://my.site.com/register');
1386
- ```
1387
-
1388
- @param url value to check.
1538
+ * If a relative url provided, a configured url will be prepended to it.
1539
+ * So both examples will work:
1540
+ *
1541
+ * ```js
1542
+ * I.seeCurrentUrlEquals('/register');
1543
+ * I.seeCurrentUrlEquals('http://my.site.com/register');
1544
+ * ```
1545
+ *
1546
+ * @param {string} url value to check.
1547
+ * {--end--}
1389
1548
  */
1390
1549
  async seeCurrentUrlEquals(url) {
1391
1550
  urlEquals(this.options.url).assert(url, await this._getPageUrl());
@@ -1393,9 +1552,15 @@ I.seeCurrentUrlEquals('http://my.site.com/register');
1393
1552
 
1394
1553
  /**
1395
1554
  * Checks that current url is not equal to provided one.
1396
- If a relative url provided, a configured url will be prepended to it.
1397
-
1398
- @param url value to check.
1555
+ * If a relative url provided, a configured url will be prepended to it.
1556
+ *
1557
+ * ```js
1558
+ * I.dontSeeCurrentUrlEquals('/login'); // relative url are ok
1559
+ * I.dontSeeCurrentUrlEquals('http://mysite.com/login'); // absolute urls are also ok
1560
+ * ```
1561
+ *
1562
+ * @param {string} url value to check.
1563
+ * {--end--}
1399
1564
  */
1400
1565
  async dontSeeCurrentUrlEquals(url) {
1401
1566
  urlEquals(this.options.url).negate(url, await this._getPageUrl());
@@ -1403,15 +1568,18 @@ If a relative url provided, a configured url will be prepended to it.
1403
1568
 
1404
1569
  /**
1405
1570
  * Checks that a page contains a visible text.
1406
- Use context parameter to narrow down the search.
1407
-
1408
- ```js
1409
- I.see('Welcome'); // text welcome on a page
1410
- I.see('Welcome', '.content'); // text inside .content div
1411
- I.see('Register', {css: 'form.register'}); // use strict locator
1412
- ```
1413
- @param text expected on page.
1414
- @param context (optional) element located by CSS|Xpath|strict locator in which to search for text.
1571
+ * Use context parameter to narrow down the search.
1572
+ *
1573
+ * ```js
1574
+ * I.see('Welcome'); // text welcome on a page
1575
+ * I.see('Welcome', '.content'); // text inside .content div
1576
+ * I.see('Register', {css: 'form.register'}); // use strict locator
1577
+ * ```
1578
+ * @param {string} text expected on page.
1579
+ * @param {string|object} context (optional, `null` by default) element located by CSS|Xpath|strict locator in which to search for text.
1580
+ * {--end--}
1581
+ *
1582
+ * {{ react }}
1415
1583
  */
1416
1584
  async see(text, context = null) {
1417
1585
  return proceedSee.call(this, 'assert', text, context);
@@ -1430,13 +1598,19 @@ I.see('Register', {css: 'form.register'}); // use strict locator
1430
1598
 
1431
1599
  /**
1432
1600
  * Opposite to `see`. Checks that a text is not present on a page.
1433
- Use context parameter to narrow down the search.
1434
-
1435
- ```js
1436
- I.dontSee('Login'); // assume we are already logged in
1437
- ```
1438
- @param text is not present.
1439
- @param context (optional) element located by CSS|XPath|strict locator in which to perfrom search.
1601
+ * Use context parameter to narrow down the search.
1602
+ *
1603
+ * ```js
1604
+ * I.dontSee('Login'); // assume we are already logged in.
1605
+ * I.dontSee('Login', '.nav'); // no login inside .nav element
1606
+ * ```
1607
+ *
1608
+ * @param {string} text which is not present.
1609
+ * @param {string|object} context (optional) element located by CSS|XPath|strict locator in which to perfrom search.
1610
+ *
1611
+ * {--end--}
1612
+ *
1613
+ * {{ react }}
1440
1614
  */
1441
1615
  async dontSee(text, context = null) {
1442
1616
  return proceedSee.call(this, 'negate', text, context);
@@ -1444,11 +1618,14 @@ I.dontSee('Login'); // assume we are already logged in
1444
1618
 
1445
1619
  /**
1446
1620
  * Retrieves page source and returns it to test.
1447
- Resumes test execution, so should be used inside an async function.
1448
-
1449
- ```js
1450
- let pageSource = await I.grabSource();
1451
- ```
1621
+ * Resumes test execution, so should be used inside an async function.
1622
+ *
1623
+ * ```js
1624
+ * let pageSource = await I.grabSource();
1625
+ * ```
1626
+ *
1627
+ * @returns {Promise<string>} source code
1628
+ * {--end--}
1452
1629
  */
1453
1630
  async grabSource() {
1454
1631
  return this.page.content();
@@ -1470,12 +1647,15 @@ let pageSource = await I.grabSource();
1470
1647
 
1471
1648
  /**
1472
1649
  * Get current URL from browser.
1473
- Resumes test execution, so should be used inside an async function.
1474
-
1475
- ```js
1476
- let url = await I.grabCurrentUrl();
1477
- console.log(`Current URL is [${url}]`);
1478
- ```
1650
+ * Resumes test execution, so should be used inside an async function.
1651
+ *
1652
+ * ```js
1653
+ * let url = await I.grabCurrentUrl();
1654
+ * console.log(`Current URL is [${url}]`);
1655
+ * ```
1656
+ *
1657
+ * @returns {Promise<string>} current URL
1658
+ * {--end--}
1479
1659
  */
1480
1660
  async grabCurrentUrl() {
1481
1661
  return this._getPageUrl();
@@ -1483,11 +1663,12 @@ console.log(`Current URL is [${url}]`);
1483
1663
 
1484
1664
  /**
1485
1665
  * Checks that the current page contains the given string in its raw source code.
1486
-
1487
- ```js
1488
- I.seeInSource('<h1>Green eggs &amp; ham</h1>');
1489
- ```
1490
- @param text value to check.
1666
+ *
1667
+ * ```js
1668
+ * I.seeInSource('<h1>Green eggs &amp; ham</h1>');
1669
+ * ```
1670
+ * @param {string} text value to check.
1671
+ * {--end--}
1491
1672
  */
1492
1673
  async seeInSource(text) {
1493
1674
  const source = await this.page.content();
@@ -1496,9 +1677,13 @@ I.seeInSource('<h1>Green eggs &amp; ham</h1>');
1496
1677
 
1497
1678
  /**
1498
1679
  * Checks that the current page does not contains the given string in its raw source code.
1499
-
1500
- @param text value to check.
1501
-
1680
+ *
1681
+ * ```js
1682
+ * I.dontSeeInSource('<!--'); // no comments in source
1683
+ * ```
1684
+ *
1685
+ * @param {string} value to check.
1686
+ * {--end--}
1502
1687
  */
1503
1688
  async dontSeeInSource(text) {
1504
1689
  const source = await this.page.content();
@@ -1507,28 +1692,38 @@ I.seeInSource('<h1>Green eggs &amp; ham</h1>');
1507
1692
 
1508
1693
 
1509
1694
  /**
1510
- * asserts that an element appears a given number of times in the DOM
1695
+ * Asserts that an element appears a given number of times in the DOM.
1511
1696
  * Element is located by label or name or CSS or XPath.
1512
- *
1697
+ *
1698
+ *
1513
1699
  * ```js
1514
1700
  * I.seeNumberOfElements('#submitBtn', 1);
1515
1701
  * ```
1702
+ *
1703
+ * @param {string|object} locator element located by CSS|XPath|strict locator.
1704
+ * @param {number} num number of elements.
1705
+ * {--end--}
1706
+ *
1707
+ * {{ react }}
1516
1708
  */
1517
- async seeNumberOfElements(selector, num) {
1518
- const elements = await this._locate(selector);
1519
- return equals(`expected number of elements (${selector}) is ${num}, but found ${elements.length}`).assert(elements.length, num);
1709
+ async seeNumberOfElements(locator, num) {
1710
+ const elements = await this._locate(locator);
1711
+ return equals(`expected number of elements (${locator}) is ${num}, but found ${elements.length}`).assert(elements.length, num);
1520
1712
  }
1521
1713
 
1522
1714
  /**
1523
1715
  * Asserts that an element is visible a given number of times.
1524
- Element is located by CSS or XPath.
1525
-
1526
- ```js
1527
- I.seeNumberOfVisibleElements('.buttons', 3);
1528
- ```
1529
-
1530
- @param locator element located by CSS|XPath|strict locator.
1531
- @param num number of elements.
1716
+ * Element is located by CSS or XPath.
1717
+ *
1718
+ * ```js
1719
+ * I.seeNumberOfVisibleElements('.buttons', 3);
1720
+ * ```
1721
+ *
1722
+ * @param {string|object} locator element located by CSS|XPath|strict locator.
1723
+ * @param {number} num number of elements.
1724
+ * {--end--}
1725
+ *
1726
+ * {{ react }}
1532
1727
  */
1533
1728
  async seeNumberOfVisibleElements(locator, num) {
1534
1729
  const res = await this.grabNumberOfVisibleElements(locator);
@@ -1537,12 +1732,13 @@ I.seeNumberOfVisibleElements('.buttons', 3);
1537
1732
 
1538
1733
  /**
1539
1734
  * Sets a cookie.
1540
-
1541
- ```js
1542
- I.setCookie({name: 'auth', value: true});
1543
- ```
1544
-
1545
- @param cookie cookie JSON object.
1735
+ *
1736
+ * ```js
1737
+ * I.setCookie({name: 'auth', value: true});
1738
+ * ```
1739
+ *
1740
+ * @param {object} cookie a cookie object.
1741
+ * {--end--}
1546
1742
  */
1547
1743
  async setCookie(cookie) {
1548
1744
  if (Array.isArray(cookie)) {
@@ -1553,12 +1749,13 @@ I.setCookie({name: 'auth', value: true});
1553
1749
 
1554
1750
  /**
1555
1751
  * Checks that cookie with given name exists.
1556
-
1557
- ```js
1558
- I.seeCookie('Auth');
1559
- ```
1560
-
1561
- @param name cookie name.
1752
+ *
1753
+ * ```js
1754
+ * I.seeCookie('Auth');
1755
+ * ```
1756
+ *
1757
+ * @param {string} name cookie name.
1758
+ * {--end--}
1562
1759
  *
1563
1760
  */
1564
1761
  async seeCookie(name) {
@@ -1568,8 +1765,13 @@ I.seeCookie('Auth');
1568
1765
 
1569
1766
  /**
1570
1767
  * Checks that cookie with given name does not exist.
1571
-
1572
- @param name cookie name.
1768
+ *
1769
+ * ```js
1770
+ * I.dontSeeCookie('auth'); // no auth cookie
1771
+ * ```
1772
+ *
1773
+ * @param {string} name cookie name.
1774
+ * {--end--}
1573
1775
  */
1574
1776
  async dontSeeCookie(name) {
1575
1777
  const cookies = await this.page.cookies();
@@ -1578,15 +1780,17 @@ I.seeCookie('Auth');
1578
1780
 
1579
1781
  /**
1580
1782
  * Gets a cookie object by name.
1581
- If none provided gets all cookies.
1582
- * Resumes test execution, so **should be used inside async with `await`** operator.
1583
-
1584
- ```js
1585
- let cookie = await I.grabCookie('auth');
1586
- assert(cookie.value, '123456');
1587
- ```
1588
-
1589
- @param name (optional) cookie name.
1783
+ * If none provided gets all cookies.
1784
+ * * Resumes test execution, so **should be used inside async with `await`** operator.
1785
+ *
1786
+ * ```js
1787
+ * let cookie = await I.grabCookie('auth');
1788
+ * assert(cookie.value, '123456');
1789
+ * ```
1790
+ *
1791
+ * @param [name=null] cookie name.
1792
+ * @returns {Promise<string>} attribute value
1793
+ * {--end--}
1590
1794
  *
1591
1795
  * Returns cookie in JSON format. If name not passed returns all cookies for this domain.
1592
1796
  */
@@ -1599,14 +1803,15 @@ assert(cookie.value, '123456');
1599
1803
 
1600
1804
  /**
1601
1805
  * Clears a cookie by name,
1602
- if none provided clears all cookies.
1603
-
1604
- ```js
1605
- I.clearCookie();
1606
- I.clearCookie('test');
1607
- ```
1608
-
1609
- @param cookie (optional) cookie name.
1806
+ * if none provided clears all cookies.
1807
+ *
1808
+ * ```js
1809
+ * I.clearCookie();
1810
+ * I.clearCookie('test');
1811
+ * ```
1812
+ *
1813
+ * @param {string} cookie (optional, `null` by default) cookie name
1814
+ * {--end--}
1610
1815
  */
1611
1816
  async clearCookie(name) {
1612
1817
  const cookies = await this.page.cookies();
@@ -1620,30 +1825,31 @@ I.clearCookie('test');
1620
1825
 
1621
1826
  /**
1622
1827
  * Executes sync script on a page.
1623
- Pass arguments to function as additional parameters.
1624
- Will return execution result to a test.
1625
- In this case you should use async function and await to receive results.
1626
-
1627
- Example with jQuery DatePicker:
1628
-
1629
- ```js
1630
- // change date of jQuery DatePicker
1631
- I.executeScript(function() {
1632
- // now we are inside browser context
1633
- $('date').datetimepicker('setDate', new Date());
1634
- });
1635
- ```
1636
- Can return values. Don't forget to use `await` to get them.
1637
-
1638
- ```js
1639
- let date = await I.executeScript(function(el) {
1640
- // only basic types can be returned
1641
- return $(el).datetimepicker('getDate').toString();
1642
- }, '#date'); // passing jquery selector
1643
- ```
1644
-
1645
- @param fn function to be executed in browser context.
1646
- @param ...args args to be passed to function.
1828
+ * Pass arguments to function as additional parameters.
1829
+ * Will return execution result to a test.
1830
+ * In this case you should use async function and await to receive results.
1831
+ *
1832
+ * Example with jQuery DatePicker:
1833
+ *
1834
+ * ```js
1835
+ * // change date of jQuery DatePicker
1836
+ * I.executeScript(function() {
1837
+ * // now we are inside browser context
1838
+ * $('date').datetimepicker('setDate', new Date());
1839
+ * });
1840
+ * ```
1841
+ * Can return values. Don't forget to use `await` to get them.
1842
+ *
1843
+ * ```js
1844
+ * let date = await I.executeScript(function(el) {
1845
+ * // only basic types can be returned
1846
+ * return $(el).datetimepicker('getDate').toString();
1847
+ * }, '#date'); // passing jquery selector
1848
+ * ```
1849
+ *
1850
+ * @param {string|function} fn function to be executed in browser context.
1851
+ * @param ...args args to be passed to function.
1852
+ * {--end--}
1647
1853
  *
1648
1854
  * If a function returns a Promise It will wait for it resolution.
1649
1855
  */
@@ -1657,28 +1863,29 @@ let date = await I.executeScript(function(el) {
1657
1863
 
1658
1864
  /**
1659
1865
  * Executes async script on page.
1660
- Provided function should execute a passed callback (as first argument) to signal it is finished.
1661
-
1662
- Example: In Vue.js to make components completely rendered we are waiting for [nextTick](https://vuejs.org/v2/api/#Vue-nextTick).
1663
-
1664
- ```js
1665
- I.executeAsyncScript(function(done) {
1666
- Vue.nextTick(done); // waiting for next tick
1667
- });
1668
- ```
1669
-
1670
- By passing value to `done()` function you can return values.
1671
- Additional arguments can be passed as well, while `done` function is always last parameter in arguments list.
1672
-
1673
- ```js
1674
- let val = await I.executeAsyncScript(function(url, done) {
1675
- // in browser context
1676
- $.ajax(url, { success: (data) => done(data); }
1677
- }, 'http://ajax.callback.url/');
1678
- ```
1679
-
1680
- @param fn function to be executed in browser context.
1681
- @param ...args args to be passed to function.
1866
+ * Provided function should execute a passed callback (as first argument) to signal it is finished.
1867
+ *
1868
+ * Example: In Vue.js to make components completely rendered we are waiting for [nextTick](https://vuejs.org/v2/api/#Vue-nextTick).
1869
+ *
1870
+ * ```js
1871
+ * I.executeAsyncScript(function(done) {
1872
+ * Vue.nextTick(done); // waiting for next tick
1873
+ * });
1874
+ * ```
1875
+ *
1876
+ * By passing value to `done()` function you can return values.
1877
+ * Additional arguments can be passed as well, while `done` function is always last parameter in arguments list.
1878
+ *
1879
+ * ```js
1880
+ * let val = await I.executeAsyncScript(function(url, done) {
1881
+ * // in browser context
1882
+ * $.ajax(url, { success: (data) => done(data); }
1883
+ * }, 'http://ajax.callback.url/');
1884
+ * ```
1885
+ *
1886
+ * @param {string|function} fn function to be executed in browser context.
1887
+ * @param ...args args to be passed to function.
1888
+ * {--end--}
1682
1889
  *
1683
1890
  * Asynchronous scripts can also be executed with `executeScript` if a function returns a Promise.
1684
1891
  */
@@ -1700,14 +1907,17 @@ let val = await I.executeAsyncScript(function(url, done) {
1700
1907
 
1701
1908
  /**
1702
1909
  * Retrieves a text from an element located by CSS or XPath and returns it to test.
1703
- Resumes test execution, so **should be used inside async with `await`** operator.
1704
-
1705
- ```js
1706
- let pin = await I.grabTextFrom('#pin');
1707
- ```
1708
- If multiple elements found returns an array of texts.
1709
-
1710
- @param locator element located by CSS|XPath|strict locator.
1910
+ * Resumes test execution, so **should be used inside async with `await`** operator.
1911
+ *
1912
+ * ```js
1913
+ * let pin = await I.grabTextFrom('#pin');
1914
+ * ```
1915
+ * If multiple elements found returns an array of texts.
1916
+ *
1917
+ * @param locator element located by CSS|XPath|strict locator.
1918
+ * @returns {Promise<string>} attribute value
1919
+ * {--end--}
1920
+ * {{ react }}
1711
1921
  */
1712
1922
  async grabTextFrom(locator) {
1713
1923
  const els = await this._locate(locator);
@@ -1722,12 +1932,14 @@ If multiple elements found returns an array of texts.
1722
1932
 
1723
1933
  /**
1724
1934
  * Retrieves a value from a form element located by CSS or XPath and returns it to test.
1725
- Resumes test execution, so **should be used inside async function with `await`** operator.
1726
-
1727
- ```js
1728
- let email = await I.grabValueFrom('input[name=email]');
1729
- ```
1730
- @param locator field located by label|name|CSS|XPath|strict locator.
1935
+ * Resumes test execution, so **should be used inside async function with `await`** operator.
1936
+ *
1937
+ * ```js
1938
+ * let email = await I.grabValueFrom('input[name=email]');
1939
+ * ```
1940
+ * @param {string|object} locator field located by label|name|CSS|XPath|strict locator.
1941
+ * @returns {Promise<string>} attribute value
1942
+ * {--end--}
1731
1943
  */
1732
1944
  async grabValueFrom(locator) {
1733
1945
  const els = await findFields.call(this, locator);
@@ -1737,14 +1949,16 @@ let email = await I.grabValueFrom('input[name=email]');
1737
1949
 
1738
1950
  /**
1739
1951
  * Retrieves the innerHTML from an element located by CSS or XPath and returns it to test.
1740
- Resumes test execution, so **should be used inside async function with `await`** operator.
1741
- If more than one element is found - an array of HTMLs returned.
1742
-
1743
- ```js
1744
- let postHTML = await I.grabHTMLFrom('#post');
1745
- ```
1746
-
1747
- @param locator element located by CSS|XPath|strict locator.
1952
+ * Resumes test execution, so **should be used inside async function with `await`** operator.
1953
+ * If more than one element is found - an array of HTMLs returned.
1954
+ *
1955
+ * ```js
1956
+ * let postHTML = await I.grabHTMLFrom('#post');
1957
+ * ```
1958
+ *
1959
+ * @param locator element located by CSS|XPath|strict locator.
1960
+ * @returns {Promise<string>} HTML code for an element
1961
+ * {--end--}
1748
1962
  */
1749
1963
  async grabHTMLFrom(locator) {
1750
1964
  const els = await this._locate(locator);
@@ -1758,14 +1972,17 @@ let postHTML = await I.grabHTMLFrom('#post');
1758
1972
 
1759
1973
  /**
1760
1974
  * Grab CSS property for given locator
1761
- Resumes test execution, so **should be used inside an async function with `await`** operator.
1762
-
1763
- ```js
1764
- const value = await I.grabCssPropertyFrom('h3', 'font-weight');
1765
- ```
1766
-
1767
- @param locator element located by CSS|XPath|strict locator.
1768
- @param cssProperty CSS property name.
1975
+ * Resumes test execution, so **should be used inside an async function with `await`** operator.
1976
+ *
1977
+ * ```js
1978
+ * const value = await I.grabCssPropertyFrom('h3', 'font-weight');
1979
+ * ```
1980
+ *
1981
+ * @param {string|object} locator element located by CSS|XPath|strict locator.
1982
+ * @param {string} cssProperty CSS property name.
1983
+ * @returns {Promise<string>} CSS value
1984
+ * {--end--}
1985
+ * {{ react }}
1769
1986
  */
1770
1987
  async grabCssPropertyFrom(locator, cssProperty) {
1771
1988
  const els = await this._locate(locator);
@@ -1780,13 +1997,15 @@ const value = await I.grabCssPropertyFrom('h3', 'font-weight');
1780
1997
 
1781
1998
  /**
1782
1999
  * Checks that all elements with given locator have given CSS properties.
1783
-
1784
- ```js
1785
- I.seeCssPropertiesOnElements('h3', { 'font-weight': "bold"});
1786
- ```
1787
-
1788
- @param locator located by CSS|XPath|strict locator.
1789
- @param cssProperties object with CSS properties and their values to check.
2000
+ *
2001
+ * ```js
2002
+ * I.seeCssPropertiesOnElements('h3', { 'font-weight': "bold"});
2003
+ * ```
2004
+ *
2005
+ * @param {string|object} locator located by CSS|XPath|strict locator.
2006
+ * @param {object} cssProperties object with CSS properties and their values to check.
2007
+ * {--end--}
2008
+ * {{ react }}
1790
2009
  */
1791
2010
  async seeCssPropertiesOnElements(locator, cssProperties) {
1792
2011
  const res = await this._locate(locator);
@@ -1825,13 +2044,15 @@ I.seeCssPropertiesOnElements('h3', { 'font-weight': "bold"});
1825
2044
 
1826
2045
  /**
1827
2046
  * Checks that all elements with given locator have given attributes.
1828
-
1829
- ```js
1830
- I.seeAttributesOnElements('//form', {'method': "post"});
1831
- ```
1832
-
1833
- @param locator located by CSS|XPath|strict locator.
1834
- @param attributes object with attributes and their values to check.
2047
+ *
2048
+ * ```js
2049
+ * I.seeAttributesOnElements('//form', { method: "post"});
2050
+ * ```
2051
+ *
2052
+ * @param {string|object} locator located by CSS|XPath|strict locator.
2053
+ * @param {object} attributes attributes and their values to check.
2054
+ * {--end--}
2055
+ * {{ react }}
1835
2056
  */
1836
2057
  async seeAttributesOnElements(locator, attributes) {
1837
2058
  const res = await this._locate(locator);
@@ -1862,15 +2083,17 @@ I.seeAttributesOnElements('//form', {'method': "post"});
1862
2083
 
1863
2084
  /**
1864
2085
  * Drag the scrubber of a slider to a given position
1865
- For fuzzy locators, fields are matched by label text, the "name" attribute, CSS, and XPath.
1866
-
1867
- ```js
1868
- I.dragSlider('#slider', 30);
1869
- I.dragSlider('#slider', -70);
1870
- ```
1871
-
1872
- @param locator located by label|name|CSS|XPath|strict locator.
1873
- @param offsetX position to drag.
2086
+ * For fuzzy locators, fields are matched by label text, the "name" attribute, CSS, and XPath.
2087
+ *
2088
+ * ```js
2089
+ * I.dragSlider('#slider', 30);
2090
+ * I.dragSlider('#slider', -70);
2091
+ * ```
2092
+ *
2093
+ * @param {string|object} locator located by label|name|CSS|XPath|strict locator.
2094
+ * @param {number} offsetX position to drag.
2095
+ * {--end--}
2096
+ * {{ react }}
1874
2097
  */
1875
2098
  async dragSlider(locator, offsetX = 0) {
1876
2099
  const src = await this._locate(locator);
@@ -1892,14 +2115,17 @@ I.dragSlider('#slider', -70);
1892
2115
 
1893
2116
  /**
1894
2117
  * Retrieves an attribute from an element located by CSS or XPath and returns it to test.
1895
- An array as a result will be returned if there are more than one matched element.
1896
- Resumes test execution, so **should be used inside async with `await`** operator.
1897
-
1898
- ```js
1899
- let hint = await I.grabAttributeFrom('#tooltip', 'title');
1900
- ```
1901
- @param locator element located by CSS|XPath|strict locator.
1902
- @param attr attribute name.
2118
+ * An array as a result will be returned if there are more than one matched element.
2119
+ * Resumes test execution, so **should be used inside async with `await`** operator.
2120
+ *
2121
+ * ```js
2122
+ * let hint = await I.grabAttributeFrom('#tooltip', 'title');
2123
+ * ```
2124
+ * @param {string|object} locator element located by CSS|XPath|strict locator.
2125
+ * @param {string} attr attribute name.
2126
+ * @returns {Promise<string>} attribute value
2127
+ * {--end--}
2128
+ * {{ react }}
1903
2129
  */
1904
2130
  async grabAttributeFrom(locator, attr) {
1905
2131
  const els = await this._locate(locator);
@@ -1916,16 +2142,17 @@ let hint = await I.grabAttributeFrom('#tooltip', 'title');
1916
2142
 
1917
2143
  /**
1918
2144
  * Saves a screenshot to ouput folder (set in codecept.json or codecept.conf.js).
1919
- Filename is relative to output folder.
1920
- Optionally resize the window to the full available page `scrollHeight` and `scrollWidth` to capture the entire page by passing `true` in as the second argument.
1921
-
1922
- ```js
1923
- I.saveScreenshot('debug.png');
1924
- I.saveScreenshot('debug.png', true) //resizes to available scrollHeight and scrollWidth before taking screenshot
1925
- ```
1926
-
1927
- @param fileName file name to save.
1928
- @param fullPage (optional) flag to enable fullscreen screenshot mode.
2145
+ * Filename is relative to output folder.
2146
+ * Optionally resize the window to the full available page `scrollHeight` and `scrollWidth` to capture the entire page by passing `true` in as the second argument.
2147
+ *
2148
+ * ```js
2149
+ * I.saveScreenshot('debug.png');
2150
+ * I.saveScreenshot('debug.png', true) //resizes to available scrollHeight and scrollWidth before taking screenshot
2151
+ * ```
2152
+ *
2153
+ * @param {string} fileName file name to save.
2154
+ * @param {boolean} fullPage (optional, `false` by default) flag to enable fullscreen screenshot mode.
2155
+ * {--end--}
1929
2156
  */
1930
2157
  async saveScreenshot(fileName, fullPage) {
1931
2158
  const fullPageOption = fullPage || this.options.fullPageScreenshots;
@@ -1945,13 +2172,13 @@ I.saveScreenshot('debug.png', true) //resizes to available scrollHeight and scro
1945
2172
 
1946
2173
  /**
1947
2174
  * Pauses execution for a number of seconds.
1948
-
1949
- ```js
1950
- I.wait(2); // wait 2 secs
1951
- ```
1952
-
1953
- @param sec number of second to wait.
1954
- @param sec time in seconds to wait.
2175
+ *
2176
+ * ```js
2177
+ * I.wait(2); // wait 2 secs
2178
+ * ```
2179
+ *
2180
+ * @param {number} sec number of second to wait.
2181
+ * {--end--}
1955
2182
  */
1956
2183
  async wait(sec) {
1957
2184
  return new Promise(((done) => {
@@ -1961,10 +2188,11 @@ I.wait(2); // wait 2 secs
1961
2188
 
1962
2189
  /**
1963
2190
  * Waits for element to become enabled (by default waits for 1sec).
1964
- Element can be located by CSS or XPath.
1965
-
1966
- @param locator element located by CSS|XPath|strict locator.
1967
- @param sec (optional) time in seconds to wait, 1 by default.
2191
+ * Element can be located by CSS or XPath.
2192
+ *
2193
+ * @param {string|object} locator element located by CSS|XPath|strict locator.
2194
+ * @param sec (optional) time in seconds to wait, 1 by default.
2195
+ * {--end--}
1968
2196
  */
1969
2197
  async waitForEnabled(locator, sec) {
1970
2198
  const waitTimeout = sec ? sec * 1000 : this.options.waitForTimeout;
@@ -1995,14 +2223,15 @@ Element can be located by CSS or XPath.
1995
2223
 
1996
2224
  /**
1997
2225
  * Waits for the specified value to be in value attribute.
1998
-
1999
- ```js
2000
- I.waitForValue('//input', "GoodValue");
2001
- ```
2002
-
2003
- @param field input field.
2004
- @param value expected value.
2005
- @param sec (optional) time in seconds to wait, 1 sec by default.
2226
+ *
2227
+ * ```js
2228
+ * I.waitForValue('//input', "GoodValue");
2229
+ * ```
2230
+ *
2231
+ * @param {string|object} field input field.
2232
+ * @param {string }value expected value.
2233
+ * @param {number} sec (optional, `1` by default) time in seconds to wait
2234
+ * {--end--}
2006
2235
  */
2007
2236
  async waitForValue(field, value, sec) {
2008
2237
  const waitTimeout = sec ? sec * 1000 : this.options.waitForTimeout;
@@ -2034,14 +2263,16 @@ I.waitForValue('//input', "GoodValue");
2034
2263
 
2035
2264
  /**
2036
2265
  * Waits for a specified number of elements on the page.
2037
-
2038
- ```js
2039
- I.waitNumberOfVisibleElements('a', 3);
2040
- ```
2041
-
2042
- @param locator element located by CSS|XPath|strict locator.
2043
- @param num number of elements.
2044
- @param sec (optional) time in seconds to wait.
2266
+ *
2267
+ * ```js
2268
+ * I.waitNumberOfVisibleElements('a', 3);
2269
+ * ```
2270
+ *
2271
+ * @param {string|object} locator element located by CSS|XPath|strict locator.
2272
+ * @param {number} num number of elements.
2273
+ * @param {number} sec (optional, `1` by default) time in seconds to wait
2274
+ * {--end--}
2275
+ * {{ react }}
2045
2276
  */
2046
2277
  async waitNumberOfVisibleElements(locator, num, sec) {
2047
2278
  const waitTimeout = sec ? sec * 1000 : this.options.waitForTimeout;
@@ -2072,15 +2303,17 @@ I.waitNumberOfVisibleElements('a', 3);
2072
2303
 
2073
2304
  /**
2074
2305
  * Waits for element to be present on page (by default waits for 1sec).
2075
- Element can be located by CSS or XPath.
2076
-
2077
- ```js
2078
- I.waitForElement('.btn.continue');
2079
- I.waitForElement('.btn.continue', 5); // wait for 5 secs
2080
- ```
2081
-
2082
- @param locator element located by CSS|XPath|strict locator.
2083
- @param sec (optional) time in seconds to wait, 1 by default.
2306
+ * Element can be located by CSS or XPath.
2307
+ *
2308
+ * ```js
2309
+ * I.waitForElement('.btn.continue');
2310
+ * I.waitForElement('.btn.continue', 5); // wait for 5 secs
2311
+ * ```
2312
+ *
2313
+ * @param {string|object} locator element located by CSS|XPath|strict locator.
2314
+ * @param {number} sec (optional, `1` by default) time in seconds to wait
2315
+ * {--end--}
2316
+ * {{ react }}
2084
2317
  */
2085
2318
  async waitForElement(locator, sec) {
2086
2319
  const waitTimeout = sec ? sec * 1000 : this.options.waitForTimeout;
@@ -2100,14 +2333,17 @@ I.waitForElement('.btn.continue', 5); // wait for 5 secs
2100
2333
 
2101
2334
  /**
2102
2335
  * Waits for an element to become visible on a page (by default waits for 1sec).
2103
- Element can be located by CSS or XPath.
2104
-
2105
- ```
2106
- I.waitForVisible('#popup');
2107
- ```
2108
-
2109
- @param locator element located by CSS|XPath|strict locator.
2110
- @param sec (optional) time in seconds to wait, 1 by default.
2336
+ * Element can be located by CSS or XPath.
2337
+ *
2338
+ * ```js
2339
+ * I.waitForVisible('#popup');
2340
+ * ```
2341
+ *
2342
+ * @param {string|object} locator element located by CSS|XPath|strict locator.
2343
+ * @param {number} sec (optional, `1` by default) time in seconds to wait
2344
+ * {--end--}
2345
+ *
2346
+ * This method accepts [React selectors](https://codecept.io/react).
2111
2347
  */
2112
2348
  async waitForVisible(locator, sec) {
2113
2349
  const waitTimeout = sec ? sec * 1000 : this.options.waitForTimeout;
@@ -2131,14 +2367,15 @@ I.waitForVisible('#popup');
2131
2367
 
2132
2368
  /**
2133
2369
  * Waits for an element to be removed or become invisible on a page (by default waits for 1sec).
2134
- Element can be located by CSS or XPath.
2135
-
2136
- ```
2137
- I.waitForInvisible('#popup');
2138
- ```
2139
-
2140
- @param locator element located by CSS|XPath|strict locator.
2141
- @param sec (optional) time in seconds to wait, 1 by default.
2370
+ * Element can be located by CSS or XPath.
2371
+ *
2372
+ * ```js
2373
+ * I.waitForInvisible('#popup');
2374
+ * ```
2375
+ *
2376
+ * @param {string|object} locator element located by CSS|XPath|strict locator.
2377
+ * @param {number} sec (optional, `1` by default) time in seconds to wait
2378
+ * {--end--}
2142
2379
  */
2143
2380
  async waitForInvisible(locator, sec) {
2144
2381
  const waitTimeout = sec ? sec * 1000 : this.options.waitForTimeout;
@@ -2163,14 +2400,15 @@ I.waitForInvisible('#popup');
2163
2400
 
2164
2401
  /**
2165
2402
  * Waits for an element to hide (by default waits for 1sec).
2166
- Element can be located by CSS or XPath.
2167
-
2168
- ```
2169
- I.waitToHide('#popup');
2170
- ```
2171
-
2172
- @param locator element located by CSS|XPath|strict locator.
2173
- @param sec (optional) time in seconds to wait, 1 by default.
2403
+ * Element can be located by CSS or XPath.
2404
+ *
2405
+ * ```js
2406
+ * I.waitToHide('#popup');
2407
+ * ```
2408
+ *
2409
+ * @param {string|object} locator element located by CSS|XPath|strict locator.
2410
+ * @param {number} sec (optional, `1` by default) time in seconds to wait
2411
+ * {--end--}
2174
2412
  */
2175
2413
  async waitToHide(locator, sec) {
2176
2414
  const waitTimeout = sec ? sec * 1000 : this.options.waitForTimeout;
@@ -2200,13 +2438,14 @@ I.waitToHide('#popup');
2200
2438
 
2201
2439
  /**
2202
2440
  * Waiting for the part of the URL to match the expected. Useful for SPA to understand that page was changed.
2203
-
2204
- ```js
2205
- I.waitInUrl('/info', 2);
2206
- ```
2207
-
2208
- @param urlPart value to check.
2209
- @param sec (optional) time in seconds to wait.
2441
+ *
2442
+ * ```js
2443
+ * I.waitInUrl('/info', 2);
2444
+ * ```
2445
+ *
2446
+ * @param {string} urlPart value to check.
2447
+ * @param {number} sec (optional, `1` by default) time in seconds to wait
2448
+ * {--end--}
2210
2449
  */
2211
2450
  async waitInUrl(urlPart, sec = null) {
2212
2451
  const waitTimeout = sec ? sec * 1000 : this.options.waitForTimeout;
@@ -2226,14 +2465,15 @@ I.waitInUrl('/info', 2);
2226
2465
 
2227
2466
  /**
2228
2467
  * Waits for the entire URL to match the expected
2229
-
2230
- ```js
2231
- I.waitUrlEquals('/info', 2);
2232
- I.waitUrlEquals('http://127.0.0.1:8000/info');
2233
- ```
2234
-
2235
- @param urlPart value to check.
2236
- @param sec (optional) time in seconds to wait.
2468
+ *
2469
+ * ```js
2470
+ * I.waitUrlEquals('/info', 2);
2471
+ * I.waitUrlEquals('http://127.0.0.1:8000/info');
2472
+ * ```
2473
+ *
2474
+ * @param {string} urlPart value to check.
2475
+ * @param {number} sec (optional, `1` by default) time in seconds to wait
2476
+ * {--end--}
2237
2477
  */
2238
2478
  async waitUrlEquals(urlPart, sec = null) {
2239
2479
  const waitTimeout = sec ? sec * 1000 : this.options.waitForTimeout;
@@ -2258,17 +2498,18 @@ I.waitUrlEquals('http://127.0.0.1:8000/info');
2258
2498
 
2259
2499
  /**
2260
2500
  * Waits for a text to appear (by default waits for 1sec).
2261
- Element can be located by CSS or XPath.
2262
- Narrow down search results by providing context.
2263
-
2264
- ```js
2265
- I.waitForText('Thank you, form has been submitted');
2266
- I.waitForText('Thank you, form has been submitted', 5, '#modal');
2267
- ```
2268
-
2269
- @param text to wait for.
2270
- @param sec (optional) time in seconds to wait.
2271
- @param context (optional) element located by CSS|XPath|strict locator.
2501
+ * Element can be located by CSS or XPath.
2502
+ * Narrow down search results by providing context.
2503
+ *
2504
+ * ```js
2505
+ * I.waitForText('Thank you, form has been submitted');
2506
+ * I.waitForText('Thank you, form has been submitted', 5, '#modal');
2507
+ * ```
2508
+ *
2509
+ * @param {string }text to wait for.
2510
+ * @param {number} sec (optional, `1` by default) time in seconds to wait
2511
+ * @param {string|object} context (optional) element located by CSS|XPath|strict locator.
2512
+ * {--end--}
2272
2513
  */
2273
2514
  async waitForText(text, sec = null, context = null) {
2274
2515
  const waitTimeout = sec ? sec * 1000 : this.options.waitForTimeout;
@@ -2310,8 +2551,8 @@ I.waitForText('Thank you, form has been submitted', 5, '#modal');
2310
2551
  * I.waitForRequest(request => request.url() === 'http://example.com' && request.method() === 'GET');
2311
2552
  * ```
2312
2553
  *
2313
- * @param {*} urlOrPredicate
2314
- * @param {*} sec
2554
+ * @param {string|function} urlOrPredicate
2555
+ * @param {number?} [sec=null] seconds to wait
2315
2556
  */
2316
2557
  async waitForRequest(urlOrPredicate, sec = null) {
2317
2558
  const timeout = sec ? sec * 1000 : this.options.waitForTimeout;
@@ -2326,8 +2567,8 @@ I.waitForText('Thank you, form has been submitted', 5, '#modal');
2326
2567
  * I.waitForResponse(request => request.url() === 'http://example.com' && request.method() === 'GET');
2327
2568
  * ```
2328
2569
  *
2329
- * @param {*} urlOrPredicate
2330
- * @param {*} sec
2570
+ * @param {string|function} urlOrPredicate
2571
+ * @param {number?} [sec=null] number of seconds to wait
2331
2572
  */
2332
2573
  async waitForResponse(urlOrPredicate, sec = null) {
2333
2574
  const timeout = sec ? sec * 1000 : this.options.waitForTimeout;
@@ -2336,8 +2577,14 @@ I.waitForText('Thank you, form has been submitted', 5, '#modal');
2336
2577
 
2337
2578
  /**
2338
2579
  * Switches frame or in case of null locator reverts to parent.
2339
-
2340
- @param locator element located by CSS|XPath|strict locator.
2580
+ *
2581
+ * ```js
2582
+ * I.switchTo('iframe'); // switch to first iframe
2583
+ * I.switchTo(); // switch back to main page
2584
+ * ```
2585
+ *
2586
+ * @param {string|object} locator (optional, `null` by default) element located by CSS|XPath|strict locator.
2587
+ * {--end--}
2341
2588
  */
2342
2589
  async switchTo(locator) {
2343
2590
  if (Number.isInteger(locator)) {
@@ -2382,21 +2629,22 @@ I.waitForText('Thank you, form has been submitted', 5, '#modal');
2382
2629
 
2383
2630
  /**
2384
2631
  * Waits for a function to return true (waits for 1 sec by default).
2385
- Running in browser context.
2386
-
2387
- ```js
2388
- I.waitForFunction(fn[, [args[, timeout]])
2389
- ```
2390
-
2391
- ```js
2392
- I.waitForFunction(() => window.requests == 0);
2393
- I.waitForFunction(() => window.requests == 0, 5); // waits for 5 sec
2394
- I.waitForFunction((count) => window.requests == count, [3], 5) // pass args and wait for 5 sec
2395
- ```
2396
-
2397
- @param fn to be executed in browser context.
2398
- @param argsOrSec (optional) arguments for function or seconds.
2399
- @param sec (optional) time in seconds to wait, 1 by default.
2632
+ * Running in browser context.
2633
+ *
2634
+ * ```js
2635
+ * I.waitForFunction(fn[, [args[, timeout]])
2636
+ * ```
2637
+ *
2638
+ * ```js
2639
+ * I.waitForFunction(() => window.requests == 0);
2640
+ * I.waitForFunction(() => window.requests == 0, 5); // waits for 5 sec
2641
+ * I.waitForFunction((count) => window.requests == count, [3], 5) // pass args and wait for 5 sec
2642
+ * ```
2643
+ *
2644
+ * @param {string|function} fn to be executed in browser context.
2645
+ * @param {array|number} argsOrSec (optional, `1` by default) arguments for function or seconds.
2646
+ * @param {number} sec (optional, `1` by default) time in seconds to wait
2647
+ * {--end--}
2400
2648
  */
2401
2649
  async waitForFunction(fn, argsOrSec = null, sec = null) {
2402
2650
  let args = [];
@@ -2426,15 +2674,16 @@ I.waitForFunction((count) => window.requests == count, [3], 5) // pass args and
2426
2674
 
2427
2675
  /**
2428
2676
  * Waits for a function to return true (waits for 1sec by default).
2429
-
2430
- ```js
2431
- I.waitUntil(() => window.requests == 0);
2432
- I.waitUntil(() => window.requests == 0, 5);
2433
- ```
2434
-
2435
- @param fn function which is executed in browser context.
2436
- @param sec (optional) time in seconds to wait, 1 by default.
2437
- @param timeoutMsg (optional) message to show in case of timeout fail.
2677
+ *
2678
+ * ```js
2679
+ * I.waitUntil(() => window.requests == 0);
2680
+ * I.waitUntil(() => window.requests == 0, 5);
2681
+ * ```
2682
+ *
2683
+ * @param {function|string} fn function which is executed in browser context.
2684
+ * @param {number} sec (optional, `1` by default) time in seconds to wait
2685
+ * @param {string} [timeoutMsg=''] message to show in case of timeout fail.
2686
+ * {--end--}
2438
2687
  */
2439
2688
  async waitUntil(fn, sec = null) {
2440
2689
  console.log('This method will remove in CodeceptJS 1.4; use `waitForFunction` instead!');
@@ -2452,14 +2701,15 @@ I.waitUntil(() => window.requests == 0, 5);
2452
2701
 
2453
2702
  /**
2454
2703
  * Waits for an element to become not attached to the DOM on a page (by default waits for 1sec).
2455
- Element can be located by CSS or XPath.
2456
-
2457
- ```
2458
- I.waitForDetached('#popup');
2459
- ```
2460
-
2461
- @param locator element located by CSS|XPath|strict locator.
2462
- @param sec (optional) time in seconds to wait, 1 by default.
2704
+ * Element can be located by CSS or XPath.
2705
+ *
2706
+ * ```js
2707
+ * I.waitForDetached('#popup');
2708
+ * ```
2709
+ *
2710
+ * @param {string|object} locator element located by CSS|XPath|strict locator.
2711
+ * @param {number} sec (optional, `1` by default) time in seconds to wait
2712
+ * {--end--}
2463
2713
  */
2464
2714
  async waitForDetached(locator, sec) {
2465
2715
  const waitTimeout = sec ? sec * 1000 : this.options.waitForTimeout;
@@ -2487,6 +2737,32 @@ I.waitForDetached('#popup');
2487
2737
  async _waitForAction() {
2488
2738
  return this.wait(this.options.waitForAction / 1000);
2489
2739
  }
2740
+
2741
+ /**
2742
+ * Grab the data from performance timing using Navigation Timing API.
2743
+ * The returned data will contain following things in ms:
2744
+ * - responseEnd,
2745
+ * - domInteractive,
2746
+ * - domContentLoadedEventEnd,
2747
+ * - loadEventEnd
2748
+ * Resumes test execution, so **should be used inside an async function with `await`** operator.
2749
+ *
2750
+ * ```js
2751
+ * await I.amOnPage('https://example.com');
2752
+ * let data = await I.grabDataFromPerformanceTiming();
2753
+ * //Returned data
2754
+ * { // all results are in [ms]
2755
+ * responseEnd: 23,
2756
+ * domInteractive: 44,
2757
+ * domContentLoadedEventEnd: 196,
2758
+ * loadEventEnd: 241
2759
+ * }
2760
+ * ```
2761
+ * {--end--}
2762
+ */
2763
+ async grabDataFromPerformanceTiming() {
2764
+ return perfTiming;
2765
+ }
2490
2766
  }
2491
2767
 
2492
2768
  module.exports = Puppeteer;
@@ -2511,9 +2787,9 @@ async function findFrame(context, name, url) {
2511
2787
  }
2512
2788
 
2513
2789
  async function findElements(matcher, locator) {
2790
+ if (locator.react) return findReact(matcher, locator);
2514
2791
  locator = new Locator(locator, 'css');
2515
2792
  if (!locator.isXPath()) return matcher.$$(locator.simplify());
2516
-
2517
2793
  return matcher.$x(locator.value);
2518
2794
  }
2519
2795
 
@@ -2540,6 +2816,7 @@ async function proceedClick(locator, context = null, options = {}) {
2540
2816
  }
2541
2817
 
2542
2818
  async function findClickable(matcher, locator) {
2819
+ if (locator.react) return findReact(matcher, locator);
2543
2820
  locator = new Locator(locator);
2544
2821
  if (!locator.isFuzzy()) return findElements.call(this, matcher, locator);
2545
2822
 
@@ -2781,7 +3058,11 @@ function $XPath(element, selector) {
2781
3058
  function targetCreatedHandler(page) {
2782
3059
  if (!page) return;
2783
3060
  this.withinLocator = null;
2784
- page.on('load', frame => this.context = page.$('body'));
3061
+ page.on('load', (frame) => {
3062
+ page.$('body')
3063
+ .catch(() => null)
3064
+ .then(context => this.context = context);
3065
+ });
2785
3066
  page.on('console', (msg) => {
2786
3067
  this.debugSection(`Browser:${ucfirst(msg.type())}`, (msg._text || '') + msg.args().join(' '));
2787
3068
  consoleLogStore.add(msg);