codeceptjs 3.5.15 → 3.6.0-beta.1.ai-healers

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 (139) hide show
  1. package/README.md +2 -2
  2. package/bin/codecept.js +66 -30
  3. package/docs/advanced.md +351 -0
  4. package/docs/ai.md +365 -0
  5. package/docs/api.md +323 -0
  6. package/docs/basics.md +979 -0
  7. package/docs/bdd.md +539 -0
  8. package/docs/best.md +237 -0
  9. package/docs/books.md +37 -0
  10. package/docs/bootstrap.md +135 -0
  11. package/docs/build/AI.js +124 -0
  12. package/docs/build/ApiDataFactory.js +410 -0
  13. package/docs/build/Appium.js +2027 -0
  14. package/docs/build/Expect.js +422 -0
  15. package/docs/build/FileSystem.js +228 -0
  16. package/docs/build/GraphQL.js +229 -0
  17. package/docs/build/GraphQLDataFactory.js +309 -0
  18. package/docs/build/JSONResponse.js +338 -0
  19. package/docs/build/Mochawesome.js +71 -0
  20. package/docs/build/Nightmare.js +2152 -0
  21. package/docs/build/Playwright.js +5110 -0
  22. package/docs/build/Protractor.js +2706 -0
  23. package/docs/build/Puppeteer.js +3905 -0
  24. package/docs/build/REST.js +344 -0
  25. package/docs/build/TestCafe.js +2125 -0
  26. package/docs/build/WebDriver.js +4240 -0
  27. package/docs/changelog.md +2572 -0
  28. package/docs/commands.md +266 -0
  29. package/docs/community-helpers.md +58 -0
  30. package/docs/configuration.md +157 -0
  31. package/docs/continuous-integration.md +22 -0
  32. package/docs/custom-helpers.md +306 -0
  33. package/docs/data.md +379 -0
  34. package/docs/detox.md +235 -0
  35. package/docs/docker.md +136 -0
  36. package/docs/email.md +183 -0
  37. package/docs/examples.md +149 -0
  38. package/docs/heal.md +186 -0
  39. package/docs/helpers/ApiDataFactory.md +266 -0
  40. package/docs/helpers/Appium.md +1374 -0
  41. package/docs/helpers/Detox.md +586 -0
  42. package/docs/helpers/Expect.md +275 -0
  43. package/docs/helpers/FileSystem.md +152 -0
  44. package/docs/helpers/GraphQL.md +151 -0
  45. package/docs/helpers/GraphQLDataFactory.md +226 -0
  46. package/docs/helpers/JSONResponse.md +254 -0
  47. package/docs/helpers/Mochawesome.md +8 -0
  48. package/docs/helpers/MockRequest.md +377 -0
  49. package/docs/helpers/Nightmare.md +1305 -0
  50. package/docs/helpers/OpenAI.md +70 -0
  51. package/docs/helpers/Playwright.md +2759 -0
  52. package/docs/helpers/Polly.md +44 -0
  53. package/docs/helpers/Protractor.md +1769 -0
  54. package/docs/helpers/Puppeteer-firefox.md +86 -0
  55. package/docs/helpers/Puppeteer.md +2317 -0
  56. package/docs/helpers/REST.md +218 -0
  57. package/docs/helpers/TestCafe.md +1321 -0
  58. package/docs/helpers/WebDriver.md +2547 -0
  59. package/docs/hooks.md +340 -0
  60. package/docs/index.md +111 -0
  61. package/docs/installation.md +75 -0
  62. package/docs/internal-api.md +266 -0
  63. package/docs/locators.md +339 -0
  64. package/docs/mobile-react-native-locators.md +67 -0
  65. package/docs/mobile.md +338 -0
  66. package/docs/pageobjects.md +291 -0
  67. package/docs/parallel.md +400 -0
  68. package/docs/playwright.md +632 -0
  69. package/docs/plugins.md +1247 -0
  70. package/docs/puppeteer.md +316 -0
  71. package/docs/quickstart.md +162 -0
  72. package/docs/react.md +70 -0
  73. package/docs/reports.md +392 -0
  74. package/docs/secrets.md +36 -0
  75. package/docs/shadow.md +68 -0
  76. package/docs/shared/keys.mustache +31 -0
  77. package/docs/shared/react.mustache +1 -0
  78. package/docs/testcafe.md +174 -0
  79. package/docs/translation.md +247 -0
  80. package/docs/tutorial.md +271 -0
  81. package/docs/typescript.md +180 -0
  82. package/docs/ui.md +59 -0
  83. package/docs/videos.md +28 -0
  84. package/docs/visual.md +202 -0
  85. package/docs/vue.md +143 -0
  86. package/docs/webdriver.md +701 -0
  87. package/docs/wiki/Books-&-Posts.md +27 -0
  88. package/docs/wiki/Community-Helpers-&-Plugins.md +53 -0
  89. package/docs/wiki/Converting-Playwright-to-Istanbul-Coverage.md +61 -0
  90. package/docs/wiki/Examples.md +145 -0
  91. package/docs/wiki/Google-Summer-of-Code-(GSoC)-2020.md +68 -0
  92. package/docs/wiki/Home.md +16 -0
  93. package/docs/wiki/Migration-to-Appium-v2---CodeceptJS.md +83 -0
  94. package/docs/wiki/Release-Process.md +24 -0
  95. package/docs/wiki/Roadmap.md +23 -0
  96. package/docs/wiki/Tests.md +1393 -0
  97. package/docs/wiki/Upgrading-to-CodeceptJS-3.md +153 -0
  98. package/docs/wiki/Videos.md +19 -0
  99. package/lib/actor.js +3 -6
  100. package/lib/ai.js +152 -80
  101. package/lib/cli.js +1 -0
  102. package/lib/command/dryRun.js +13 -44
  103. package/lib/command/generate.js +34 -0
  104. package/lib/command/run-workers.js +3 -0
  105. package/lib/command/run.js +3 -0
  106. package/lib/container.js +2 -0
  107. package/lib/heal.js +172 -0
  108. package/lib/helper/AI.js +124 -0
  109. package/lib/helper/Appium.js +12 -36
  110. package/lib/helper/Expect.js +8 -11
  111. package/lib/helper/JSONResponse.js +8 -8
  112. package/lib/helper/Playwright.js +240 -100
  113. package/lib/helper/Puppeteer.js +68 -182
  114. package/lib/helper/REST.js +1 -4
  115. package/lib/helper/WebDriver.js +10 -324
  116. package/lib/index.js +3 -0
  117. package/lib/listener/steps.js +0 -2
  118. package/lib/locator.js +4 -13
  119. package/lib/plugin/coverage.js +99 -112
  120. package/lib/plugin/heal.js +26 -117
  121. package/lib/recorder.js +11 -5
  122. package/lib/step.js +1 -3
  123. package/lib/store.js +2 -0
  124. package/lib/template/heal.js +39 -0
  125. package/package.json +35 -47
  126. package/typings/index.d.ts +0 -17
  127. package/typings/promiseBasedTypes.d.ts +57 -340
  128. package/typings/types.d.ts +73 -433
  129. package/docs/webapi/dontSeeTraffic.mustache +0 -13
  130. package/docs/webapi/flushNetworkTraffics.mustache +0 -5
  131. package/docs/webapi/grabRecordedNetworkTraffics.mustache +0 -10
  132. package/docs/webapi/seeTraffic.mustache +0 -36
  133. package/docs/webapi/startRecordingTraffic.mustache +0 -8
  134. package/docs/webapi/stopRecordingTraffic.mustache +0 -5
  135. package/docs/webapi/waitForCookie.mustache +0 -9
  136. package/lib/helper/MockServer.js +0 -221
  137. package/lib/helper/errors/ElementAssertion.js +0 -38
  138. package/lib/helper/networkTraffics/utils.js +0 -137
  139. /package/{lib/helper → docs/build}/OpenAI.js +0 -0
@@ -5,7 +5,6 @@ const path = require('path');
5
5
 
6
6
  const Helper = require('@codeceptjs/helper');
7
7
  const { v4: uuidv4 } = require('uuid');
8
- const promiseRetry = require('promise-retry');
9
8
  const Locator = require('../locator');
10
9
  const recorder = require('../recorder');
11
10
  const store = require('../store');
@@ -40,9 +39,6 @@ const findReact = require('./extras/React');
40
39
  const { highlightElement } = require('./scripts/highlightElement');
41
40
  const { blurElement } = require('./scripts/blurElement');
42
41
  const { focusElement } = require('./scripts/focusElement');
43
- const {
44
- dontSeeElementError, seeElementError, dontSeeElementInDOMError, seeElementInDOMError,
45
- } = require('./errors/ElementAssertion');
46
42
 
47
43
  let puppeteer;
48
44
  let perfTiming;
@@ -78,7 +74,7 @@ const consoleLogStore = new Console();
78
74
  * @prop {string} [browser=chrome] - can be changed to `firefox` when using [puppeteer-firefox](https://codecept.io/helpers/Puppeteer-firefox).
79
75
  * @prop {object} [chrome] - pass additional [Puppeteer run options](https://github.com/GoogleChrome/puppeteer/blob/master/docs/api.md#puppeteerlaunchoptions).
80
76
  * @prop {boolean} [highlightElement] - highlight the interacting elements. Default: false. Note: only activate under verbose mode (--verbose).
81
- */
77
+ */
82
78
  const config = {};
83
79
 
84
80
  /**
@@ -369,7 +365,7 @@ class Puppeteer extends Helper {
369
365
  this.debugSection('Incognito Tab', 'opened');
370
366
  this.activeSessionName = name;
371
367
 
372
- const bc = await this.browser.createBrowserContext();
368
+ const bc = await this.browser.createIncognitoBrowserContext();
373
369
  await bc.newPage();
374
370
 
375
371
  // Create a new page inside context.
@@ -486,7 +482,7 @@ class Puppeteer extends Helper {
486
482
  if (!page) return;
487
483
  page.setDefaultNavigationTimeout(this.options.getPageTimeout);
488
484
  this.context = await this.page.$('body');
489
- if (this.options.browser === 'chrome') {
485
+ if (this.config.browser === 'chrome') {
490
486
  await page.bringToFront();
491
487
  }
492
488
  }
@@ -599,7 +595,14 @@ class Puppeteer extends Helper {
599
595
  }
600
596
 
601
597
  async _evaluateHandeInContext(...args) {
602
- const context = await this._getContext();
598
+ let context = await this._getContext();
599
+
600
+ if (context.constructor.name === 'Frame') {
601
+ // Currently there is no evalateHandle for the Frame object
602
+ // https://github.com/GoogleChrome/puppeteer/issues/1051
603
+ context = await context.executionContext();
604
+ }
605
+
603
606
  return context.evaluateHandle(...args);
604
607
  }
605
608
 
@@ -651,9 +654,9 @@ class Puppeteer extends Helper {
651
654
  url = this.options.url + url;
652
655
  }
653
656
 
654
- if (this.options.basicAuth && (this.isAuthenticated !== true)) {
657
+ if (this.config.basicAuth && (this.isAuthenticated !== true)) {
655
658
  if (url.includes(this.options.url)) {
656
- await this.page.authenticate(this.options.basicAuth);
659
+ await this.page.authenticate(this.config.basicAuth);
657
660
  this.isAuthenticated = true;
658
661
  }
659
662
  }
@@ -877,8 +880,7 @@ class Puppeteer extends Helper {
877
880
  * {{ react }}
878
881
  */
879
882
  async _locate(locator) {
880
- const context = await this.context;
881
- return findElements.call(this, context, locator);
883
+ return findElements(await this.context, locator);
882
884
  }
883
885
 
884
886
  /**
@@ -904,7 +906,7 @@ class Puppeteer extends Helper {
904
906
  * ```
905
907
  */
906
908
  async _locateClickable(locator) {
907
- const context = await this.context;
909
+ const context = await this._getContext();
908
910
  return findClickable.call(this, context, locator);
909
911
  }
910
912
 
@@ -1036,11 +1038,8 @@ class Puppeteer extends Helper {
1036
1038
  els = (await Promise.all(els.map(el => el.boundingBox() && el))).filter(v => v);
1037
1039
  // Puppeteer visibility was ignored? | Remove when Puppeteer is fixed
1038
1040
  els = await Promise.all(els.map(async el => (await el.evaluate(node => window.getComputedStyle(node).visibility !== 'hidden' && window.getComputedStyle(node).display !== 'none')) && el));
1039
- try {
1040
- return empty('visible elements').negate(els.filter(v => v).fill('ELEMENT'));
1041
- } catch (e) {
1042
- dontSeeElementError(locator);
1043
- }
1041
+
1042
+ return empty('visible elements').negate(els.filter(v => v).fill('ELEMENT'));
1044
1043
  }
1045
1044
 
1046
1045
  /**
@@ -1052,11 +1051,8 @@ class Puppeteer extends Helper {
1052
1051
  els = (await Promise.all(els.map(el => el.boundingBox() && el))).filter(v => v);
1053
1052
  // Puppeteer visibility was ignored? | Remove when Puppeteer is fixed
1054
1053
  els = await Promise.all(els.map(async el => (await el.evaluate(node => window.getComputedStyle(node).visibility !== 'hidden' && window.getComputedStyle(node).display !== 'none')) && el));
1055
- try {
1056
- return empty('visible elements').assert(els.filter(v => v).fill('ELEMENT'));
1057
- } catch (e) {
1058
- seeElementError(locator);
1059
- }
1054
+
1055
+ return empty('visible elements').assert(els.filter(v => v).fill('ELEMENT'));
1060
1056
  }
1061
1057
 
1062
1058
  /**
@@ -1064,11 +1060,7 @@ class Puppeteer extends Helper {
1064
1060
  */
1065
1061
  async seeElementInDOM(locator) {
1066
1062
  const els = await this._locate(locator);
1067
- try {
1068
- return empty('elements on page').negate(els.filter(v => v).fill('ELEMENT'));
1069
- } catch (e) {
1070
- dontSeeElementInDOMError(locator);
1071
- }
1063
+ return empty('elements on page').negate(els.filter(v => v).fill('ELEMENT'));
1072
1064
  }
1073
1065
 
1074
1066
  /**
@@ -1076,11 +1068,7 @@ class Puppeteer extends Helper {
1076
1068
  */
1077
1069
  async dontSeeElementInDOM(locator) {
1078
1070
  const els = await this._locate(locator);
1079
- try {
1080
- return empty('elements on a page').assert(els.filter(v => v).fill('ELEMENT'));
1081
- } catch (e) {
1082
- seeElementInDOMError(locator);
1083
- }
1071
+ return empty('elements on a page').assert(els.filter(v => v).fill('ELEMENT'));
1084
1072
  }
1085
1073
 
1086
1074
  /**
@@ -1630,38 +1618,6 @@ class Puppeteer extends Helper {
1630
1618
  if (cookie[0]) return cookie[0];
1631
1619
  }
1632
1620
 
1633
- /**
1634
- * {{> waitForCookie }}
1635
- */
1636
- async waitForCookie(name, sec) {
1637
- // by default, we will retry 3 times
1638
- let retries = 3;
1639
- const waitTimeout = sec ? sec * 1000 : this.options.waitForTimeout;
1640
-
1641
- if (sec) {
1642
- retries = sec;
1643
- } else {
1644
- retries = Math.ceil(waitTimeout / 1000) - 1;
1645
- }
1646
-
1647
- return promiseRetry(async (retry, number) => {
1648
- const _grabCookie = async (name) => {
1649
- const cookies = await this.page.cookies();
1650
- const cookie = cookies.filter(c => c.name === name);
1651
- if (cookie.length === 0) throw Error(`Cookie ${name} is not found after ${retries}s`);
1652
- };
1653
-
1654
- this.debugSection('Wait for cookie: ', name);
1655
- if (number > 1) this.debugSection('Retrying... Attempt #', number);
1656
-
1657
- try {
1658
- await _grabCookie(name);
1659
- } catch (e) {
1660
- retry(e);
1661
- }
1662
- }, { retries, maxTimeout: 1000 });
1663
- }
1664
-
1665
1621
  /**
1666
1622
  * {{> clearCookie }}
1667
1623
  */
@@ -1681,8 +1637,8 @@ class Puppeteer extends Helper {
1681
1637
  * {{> executeScript }}
1682
1638
  */
1683
1639
  async executeScript(...args) {
1684
- let context = await this._getContext();
1685
- if (this.context && this.context.constructor.name === 'CdpFrame') {
1640
+ let context = this.page;
1641
+ if (this.context && this.context.constructor.name === 'Frame') {
1686
1642
  context = this.context; // switching to iframe context
1687
1643
  }
1688
1644
  return context.evaluate.apply(context, args);
@@ -1763,7 +1719,7 @@ class Puppeteer extends Helper {
1763
1719
  */
1764
1720
  async grabHTMLFromAll(locator) {
1765
1721
  const els = await this._locate(locator);
1766
- const values = await Promise.all(els.map(el => el.evaluate(element => element.innerHTML, el)));
1722
+ const values = await Promise.all(els.map(el => el.executionContext().evaluate(element => element.innerHTML, el)));
1767
1723
  return values;
1768
1724
  }
1769
1725
 
@@ -1786,7 +1742,7 @@ class Puppeteer extends Helper {
1786
1742
  */
1787
1743
  async grabCssPropertyFromAll(locator, cssProperty) {
1788
1744
  const els = await this._locate(locator);
1789
- const res = await Promise.all(els.map(el => el.evaluate(el => JSON.parse(JSON.stringify(getComputedStyle(el))), el)));
1745
+ const res = await Promise.all(els.map(el => el.executionContext().evaluate(el => JSON.parse(JSON.stringify(getComputedStyle(el))), el)));
1790
1746
  const cssValues = res.map(props => props[toCamelCase(cssProperty)]);
1791
1747
 
1792
1748
  return cssValues;
@@ -1848,34 +1804,33 @@ class Puppeteer extends Helper {
1848
1804
  * {{ react }}
1849
1805
  */
1850
1806
  async seeAttributesOnElements(locator, attributes) {
1851
- const elements = await this._locate(locator);
1852
- assertElementExists(elements, locator);
1853
-
1854
- const expectedAttributes = Object.entries(attributes);
1807
+ const res = await this._locate(locator);
1808
+ assertElementExists(res, locator);
1855
1809
 
1856
- const valuesPromises = elements.map(async (element) => {
1857
- const elementAttributes = {};
1858
- await Promise.all(expectedAttributes.map(async ([attribute, expectedValue]) => {
1859
- const actualValue = await element.evaluate((el, attr) => el[attr] || el.getAttribute(attr), attribute);
1860
- elementAttributes[attribute] = actualValue;
1861
- }));
1862
- return elementAttributes;
1810
+ const elemAmount = res.length;
1811
+ const commands = [];
1812
+ res.forEach((el) => {
1813
+ Object.keys(attributes).forEach((prop) => {
1814
+ commands.push(el
1815
+ .executionContext()
1816
+ .evaluateHandle((el, attr) => el[attr] || el.getAttribute(attr), el, prop)
1817
+ .then(el => el.jsonValue()));
1818
+ });
1863
1819
  });
1864
-
1865
- const actualAttributes = await Promise.all(valuesPromises);
1866
-
1867
- const matchingElements = actualAttributes.filter((attrs) => expectedAttributes.every(([attribute, expectedValue]) => {
1868
- const actualValue = attrs[attribute];
1869
- if (!actualValue) return false;
1870
- if (actualValue.toString().match(new RegExp(expectedValue.toString()))) return true;
1871
- return expectedValue === actualValue;
1872
- }));
1873
-
1874
- const elementsCount = elements.length;
1875
- const matchingCount = matchingElements.length;
1876
-
1877
- return equals(`all elements (${(new Locator(locator))}) to have attributes ${JSON.stringify(attributes)}`)
1878
- .assert(matchingCount, elementsCount);
1820
+ let attrs = await Promise.all(commands);
1821
+ const values = Object.keys(attributes).map(key => attributes[key]);
1822
+ if (!Array.isArray(attrs)) attrs = [attrs];
1823
+ let chunked = chunkArray(attrs, values.length);
1824
+ chunked = chunked.filter((val) => {
1825
+ for (let i = 0; i < val.length; ++i) {
1826
+ const _actual = Number.isNaN(val[i]) || (typeof values[i]) === 'string' ? val[i] : Number.parseInt(values[i], 10);
1827
+ const _expected = Number.isNaN(values[i]) || (typeof values[i]) === 'string' ? values[i] : Number.parseInt(values[i], 10);
1828
+ // if the attribute doesn't exist, returns false as well
1829
+ if (!_actual || !_actual.includes(_expected)) return false;
1830
+ }
1831
+ return true;
1832
+ });
1833
+ return equals(`all elements (${(new Locator(locator))}) to have attributes ${JSON.stringify(attributes)}`).assert(chunked.length, elemAmount);
1879
1834
  }
1880
1835
 
1881
1836
  /**
@@ -2131,7 +2086,7 @@ class Puppeteer extends Helper {
2131
2086
  if (locator.isCSS()) {
2132
2087
  waiter = context.waitForSelector(locator.simplify(), { timeout: waitTimeout });
2133
2088
  } else {
2134
- waiter = _waitForElement.call(this, locator, { timeout: waitTimeout });
2089
+ waiter = context.waitForXPath(locator.value, { timeout: waitTimeout });
2135
2090
  }
2136
2091
  return waiter.catch((err) => {
2137
2092
  throw new Error(`element (${locator.toString()}) still not present on page after ${waitTimeout / 1000} sec\n${err.message}`);
@@ -2152,7 +2107,7 @@ class Puppeteer extends Helper {
2152
2107
  if (locator.isCSS()) {
2153
2108
  waiter = context.waitForSelector(locator.simplify(), { timeout: waitTimeout, visible: true });
2154
2109
  } else {
2155
- waiter = _waitForElement.call(this, locator, { timeout: waitTimeout, visible: true });
2110
+ waiter = context.waitForXPath(locator.value, { timeout: waitTimeout, visible: true });
2156
2111
  }
2157
2112
  return waiter.catch((err) => {
2158
2113
  throw new Error(`element (${locator.toString()}) still not visible after ${waitTimeout / 1000} sec\n${err.message}`);
@@ -2171,7 +2126,7 @@ class Puppeteer extends Helper {
2171
2126
  if (locator.isCSS()) {
2172
2127
  waiter = context.waitForSelector(locator.simplify(), { timeout: waitTimeout, hidden: true });
2173
2128
  } else {
2174
- waiter = _waitForElement.call(this, locator, { timeout: waitTimeout, hidden: true });
2129
+ waiter = context.waitForXPath(locator.value, { timeout: waitTimeout, hidden: true });
2175
2130
  }
2176
2131
  return waiter.catch((err) => {
2177
2132
  throw new Error(`element (${locator.toString()}) still visible after ${waitTimeout / 1000} sec\n${err.message}`);
@@ -2189,7 +2144,7 @@ class Puppeteer extends Helper {
2189
2144
  if (locator.isCSS()) {
2190
2145
  waiter = context.waitForSelector(locator.simplify(), { timeout: waitTimeout, hidden: true });
2191
2146
  } else {
2192
- waiter = _waitForElement.call(this, locator, { timeout: waitTimeout, hidden: true });
2147
+ waiter = context.waitForXPath(locator.value, { timeout: waitTimeout, hidden: true });
2193
2148
  }
2194
2149
  return waiter.catch((err) => {
2195
2150
  throw new Error(`element (${locator.toString()}) still not hidden after ${waitTimeout / 1000} sec\n${err.message}`);
@@ -2215,7 +2170,7 @@ class Puppeteer extends Helper {
2215
2170
  }
2216
2171
 
2217
2172
  async _getContext() {
2218
- if (this.context && this.context.constructor.name === 'CdpFrame') {
2173
+ if (this.context && this.context.constructor.name === 'Frame') {
2219
2174
  return this.context;
2220
2175
  }
2221
2176
  return this.page;
@@ -2338,37 +2293,35 @@ class Puppeteer extends Helper {
2338
2293
  async switchTo(locator) {
2339
2294
  if (Number.isInteger(locator)) {
2340
2295
  // Select by frame index of current context
2341
- let frames = [];
2296
+
2297
+ let childFrames = null;
2342
2298
  if (this.context && typeof this.context.childFrames === 'function') {
2343
- frames = await this.context.childFrames();
2299
+ childFrames = this.context.childFrames();
2344
2300
  } else {
2345
- frames = await this.page.mainFrame().childFrames();
2301
+ childFrames = this.page.mainFrame().childFrames();
2346
2302
  }
2347
2303
 
2348
- if (locator >= 0 && locator < frames.length) {
2349
- this.context = frames[locator];
2304
+ if (locator >= 0 && locator < childFrames.length) {
2305
+ this.context = childFrames[locator];
2350
2306
  } else {
2351
- throw new Error('Frame index out of bounds');
2307
+ throw new Error('Element #invalidIframeSelector was not found by text|CSS|XPath');
2352
2308
  }
2353
2309
  return;
2354
2310
  }
2355
-
2356
2311
  if (!locator) {
2357
- this.context = await this.page.mainFrame();
2312
+ this.context = await this.page.mainFrame().$('body');
2358
2313
  return;
2359
2314
  }
2360
2315
 
2361
- // Select iframe by selector
2316
+ // iframe by selector
2362
2317
  const els = await this._locate(locator);
2363
2318
  assertElementExists(els, locator);
2364
-
2365
- const iframeElement = els[0];
2366
- const contentFrame = await iframeElement.contentFrame();
2319
+ const contentFrame = await els[0].contentFrame();
2367
2320
 
2368
2321
  if (contentFrame) {
2369
2322
  this.context = contentFrame;
2370
2323
  } else {
2371
- throw new Error('Element "#invalidIframeSelector" was not found by text|CSS|XPath');
2324
+ this.context = els[0];
2372
2325
  }
2373
2326
  }
2374
2327
 
@@ -2464,7 +2417,7 @@ class Puppeteer extends Helper {
2464
2417
  module.exports = Puppeteer;
2465
2418
 
2466
2419
  async function findElements(matcher, locator) {
2467
- if (locator.react) return findReactElements.call(this, locator);
2420
+ if (locator.react) return findReact(matcher.executionContext(), locator);
2468
2421
  locator = new Locator(locator, 'css');
2469
2422
  if (!locator.isXPath()) return matcher.$$(locator.simplify());
2470
2423
  // puppeteer version < 19.4.0 is no longer supported. This one is backward support.
@@ -2501,7 +2454,7 @@ async function proceedClick(locator, context = null, options = {}) {
2501
2454
  }
2502
2455
 
2503
2456
  async function findClickable(matcher, locator) {
2504
- if (locator.react) return findReactElements.call(this, locator);
2457
+ if (locator.react) return findReact(matcher.executionContext(), locator);
2505
2458
  locator = new Locator(locator);
2506
2459
  if (!locator.isFuzzy()) return findElements.call(this, matcher, locator);
2507
2460
 
@@ -2850,70 +2803,3 @@ function highlightActiveElement(element, context) {
2850
2803
  highlightElement(element, context);
2851
2804
  }
2852
2805
  }
2853
-
2854
- function _waitForElement(locator, options) {
2855
- try {
2856
- return this.context.waitForXPath(locator.value, options);
2857
- } catch (e) {
2858
- return this.context.waitForSelector(`::-p-xpath(${locator.value})`, options);
2859
- }
2860
- }
2861
-
2862
- async function findReactElements(locator, props = {}, state = {}) {
2863
- const resqScript = await fs.promises.readFile(require.resolve('resq'), 'utf-8');
2864
- await this.page.evaluate(resqScript.toString());
2865
-
2866
- await this.page.evaluate(() => window.resq.waitToLoadReact());
2867
- const arrayHandle = await this.page.evaluateHandle((obj) => {
2868
- const { selector, props, state } = obj;
2869
- let elements = window.resq.resq$$(selector);
2870
- if (Object.keys(props).length) {
2871
- elements = elements.byProps(props);
2872
- }
2873
- if (Object.keys(state).length) {
2874
- elements = elements.byState(state);
2875
- }
2876
-
2877
- if (!elements.length) {
2878
- return [];
2879
- }
2880
-
2881
- // resq returns an array of HTMLElements if the React component is a fragment
2882
- // this avoids having nested arrays of nodes which the driver does not understand
2883
- // [[div, div], [div, div]] => [div, div, div, div]
2884
- let nodes = [];
2885
-
2886
- elements.forEach((element) => {
2887
- let { node, isFragment } = element;
2888
-
2889
- if (!node) {
2890
- isFragment = true;
2891
- node = element.children;
2892
- }
2893
-
2894
- if (isFragment) {
2895
- nodes = nodes.concat(node);
2896
- } else {
2897
- nodes.push(node);
2898
- }
2899
- });
2900
-
2901
- return [...nodes];
2902
- }, {
2903
- selector: locator.react,
2904
- props: locator.props || {},
2905
- state: locator.state || {},
2906
- });
2907
-
2908
- const properties = await arrayHandle.getProperties();
2909
- const result = [];
2910
- for (const property of properties.values()) {
2911
- const elementHandle = property.asElement();
2912
- if (elementHandle) {
2913
- result.push(elementHandle);
2914
- }
2915
- }
2916
-
2917
- await arrayHandle.dispose();
2918
- return result;
2919
- }
@@ -72,12 +72,9 @@ class REST extends Helper {
72
72
  this.options.maxBodyLength = maxContentLength;
73
73
  }
74
74
 
75
- // override defaults with config
76
- this._setConfig(config);
77
-
75
+ this.options = { ...this.options, ...config };
78
76
  this.headers = { ...this.options.defaultHeaders };
79
77
  this.axios = axios.create();
80
- // @ts-ignore
81
78
  this.axios.defaults.headers = this.options.defaultHeaders;
82
79
  }
83
80