codeceptjs 3.5.0 → 3.5.1-2.beta.7

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 (273) hide show
  1. package/README.md +24 -25
  2. package/lib/actor.js +6 -3
  3. package/lib/ai.js +12 -3
  4. package/lib/cli.js +12 -2
  5. package/lib/codecept.js +4 -0
  6. package/lib/colorUtils.js +10 -0
  7. package/lib/command/definitions.js +2 -7
  8. package/lib/command/dryRun.js +2 -1
  9. package/lib/command/info.js +24 -0
  10. package/lib/command/init.js +51 -5
  11. package/lib/command/run-multiple/collection.js +17 -5
  12. package/lib/command/run-multiple.js +4 -2
  13. package/lib/command/run-workers.js +66 -4
  14. package/lib/command/run.js +7 -0
  15. package/lib/command/workers/runTests.js +39 -0
  16. package/lib/data/context.js +14 -6
  17. package/lib/event.js +4 -0
  18. package/lib/helper/ApiDataFactory.js +2 -1
  19. package/lib/helper/Appium.js +73 -24
  20. package/lib/helper/Expect.js +422 -0
  21. package/lib/helper/FileSystem.js +1 -1
  22. package/lib/helper/GraphQL.js +25 -0
  23. package/lib/helper/Nightmare.js +9 -4
  24. package/lib/helper/OpenAI.js +14 -10
  25. package/lib/helper/Playwright.js +1205 -288
  26. package/lib/helper/Protractor.js +11 -6
  27. package/lib/helper/Puppeteer.js +173 -61
  28. package/lib/helper/TestCafe.js +44 -9
  29. package/lib/helper/WebDriver.js +231 -82
  30. package/lib/helper/errors/ElementNotFound.js +2 -1
  31. package/lib/helper/extras/PlaywrightReactVueLocator.js +38 -0
  32. package/lib/helper/scripts/blurElement.js +17 -0
  33. package/lib/helper/scripts/focusElement.js +17 -0
  34. package/lib/helper/scripts/highlightElement.js +2 -2
  35. package/lib/html.js +3 -3
  36. package/lib/interfaces/bdd.js +1 -1
  37. package/lib/interfaces/gherkin.js +37 -3
  38. package/lib/interfaces/scenarioConfig.js +1 -0
  39. package/lib/locator.js +17 -4
  40. package/lib/mochaFactory.js +2 -1
  41. package/lib/output.js +1 -1
  42. package/lib/pause.js +12 -9
  43. package/lib/plugin/autoLogin.js +45 -10
  44. package/lib/plugin/heal.js +47 -17
  45. package/lib/plugin/retryFailedStep.js +10 -1
  46. package/lib/plugin/retryTo.js +2 -4
  47. package/lib/plugin/selenoid.js +6 -1
  48. package/lib/plugin/standardActingHelpers.js +0 -2
  49. package/lib/plugin/stepByStepReport.js +2 -2
  50. package/lib/plugin/tryTo.js +5 -7
  51. package/lib/plugin/wdio.js +0 -1
  52. package/lib/recorder.js +20 -9
  53. package/lib/session.js +1 -1
  54. package/lib/step.js +30 -11
  55. package/lib/ui.js +1 -0
  56. package/lib/utils.js +18 -1
  57. package/lib/workers.js +28 -3
  58. package/package.json +108 -98
  59. package/translations/de-DE.js +5 -0
  60. package/translations/fr-FR.js +14 -1
  61. package/translations/it-IT.js +1 -0
  62. package/translations/ja-JP.js +5 -0
  63. package/translations/pl-PL.js +5 -0
  64. package/translations/pt-BR.js +1 -0
  65. package/translations/ru-RU.js +1 -0
  66. package/translations/zh-CN.js +5 -0
  67. package/translations/zh-TW.js +5 -0
  68. package/typings/index.d.ts +8 -6
  69. package/typings/promiseBasedTypes.d.ts +784 -822
  70. package/typings/types.d.ts +1214 -727
  71. package/CHANGELOG.md +0 -2492
  72. package/docs/advanced.md +0 -351
  73. package/docs/ai.md +0 -246
  74. package/docs/api.md +0 -323
  75. package/docs/basics.md +0 -980
  76. package/docs/bdd.md +0 -535
  77. package/docs/best.md +0 -237
  78. package/docs/books.md +0 -37
  79. package/docs/bootstrap.md +0 -135
  80. package/docs/build/ApiDataFactory.js +0 -409
  81. package/docs/build/Appium.js +0 -1978
  82. package/docs/build/FileSystem.js +0 -228
  83. package/docs/build/GraphQL.js +0 -204
  84. package/docs/build/GraphQLDataFactory.js +0 -309
  85. package/docs/build/JSONResponse.js +0 -338
  86. package/docs/build/Mochawesome.js +0 -71
  87. package/docs/build/Nightmare.js +0 -2147
  88. package/docs/build/OpenAI.js +0 -122
  89. package/docs/build/Playwright.js +0 -4134
  90. package/docs/build/Polly.js +0 -42
  91. package/docs/build/Protractor.js +0 -2701
  92. package/docs/build/Puppeteer.js +0 -3743
  93. package/docs/build/REST.js +0 -344
  94. package/docs/build/SeleniumWebdriver.js +0 -76
  95. package/docs/build/TestCafe.js +0 -2059
  96. package/docs/build/WebDriver.js +0 -4042
  97. package/docs/changelog.md +0 -2501
  98. package/docs/commands.md +0 -254
  99. package/docs/community-helpers.md +0 -58
  100. package/docs/configuration.md +0 -157
  101. package/docs/continuous-integration.md +0 -22
  102. package/docs/custom-helpers.md +0 -306
  103. package/docs/data.md +0 -375
  104. package/docs/detox.md +0 -235
  105. package/docs/docker.md +0 -137
  106. package/docs/email.md +0 -183
  107. package/docs/examples.md +0 -149
  108. package/docs/helpers/ApiDataFactory.md +0 -266
  109. package/docs/helpers/Appium.md +0 -1317
  110. package/docs/helpers/Detox.md +0 -586
  111. package/docs/helpers/FileSystem.md +0 -152
  112. package/docs/helpers/GraphQL.md +0 -130
  113. package/docs/helpers/GraphQLDataFactory.md +0 -226
  114. package/docs/helpers/JSONResponse.md +0 -254
  115. package/docs/helpers/Mochawesome.md +0 -8
  116. package/docs/helpers/MockRequest.md +0 -377
  117. package/docs/helpers/Nightmare.md +0 -1258
  118. package/docs/helpers/OpenAI.md +0 -70
  119. package/docs/helpers/Playwright.md +0 -2250
  120. package/docs/helpers/Polly.md +0 -44
  121. package/docs/helpers/Puppeteer-firefox.md +0 -86
  122. package/docs/helpers/Puppeteer.md +0 -2147
  123. package/docs/helpers/REST.md +0 -218
  124. package/docs/helpers/TestCafe.md +0 -1224
  125. package/docs/helpers/WebDriver.md +0 -2325
  126. package/docs/hooks.md +0 -340
  127. package/docs/index.md +0 -111
  128. package/docs/installation.md +0 -75
  129. package/docs/internal-api.md +0 -265
  130. package/docs/locators.md +0 -331
  131. package/docs/mobile-react-native-locators.md +0 -67
  132. package/docs/mobile.md +0 -344
  133. package/docs/nightmare.md +0 -223
  134. package/docs/pageobjects.md +0 -291
  135. package/docs/parallel.md +0 -288
  136. package/docs/playwright.md +0 -609
  137. package/docs/plugins.md +0 -1225
  138. package/docs/puppeteer.md +0 -316
  139. package/docs/quickstart.md +0 -163
  140. package/docs/react.md +0 -69
  141. package/docs/reports.md +0 -392
  142. package/docs/secrets.md +0 -36
  143. package/docs/shadow.md +0 -68
  144. package/docs/shared/keys.mustache +0 -31
  145. package/docs/shared/react.mustache +0 -1
  146. package/docs/testcafe.md +0 -174
  147. package/docs/translation.md +0 -247
  148. package/docs/tutorial.md +0 -271
  149. package/docs/typescript.md +0 -180
  150. package/docs/ui.md +0 -59
  151. package/docs/videos.md +0 -28
  152. package/docs/visual.md +0 -202
  153. package/docs/vue.md +0 -121
  154. package/docs/webapi/amOnPage.mustache +0 -11
  155. package/docs/webapi/appendField.mustache +0 -11
  156. package/docs/webapi/attachFile.mustache +0 -12
  157. package/docs/webapi/checkOption.mustache +0 -13
  158. package/docs/webapi/clearCookie.mustache +0 -10
  159. package/docs/webapi/clearField.mustache +0 -9
  160. package/docs/webapi/click.mustache +0 -25
  161. package/docs/webapi/clickLink.mustache +0 -8
  162. package/docs/webapi/closeCurrentTab.mustache +0 -7
  163. package/docs/webapi/closeOtherTabs.mustache +0 -8
  164. package/docs/webapi/dontSee.mustache +0 -11
  165. package/docs/webapi/dontSeeCheckboxIsChecked.mustache +0 -10
  166. package/docs/webapi/dontSeeCookie.mustache +0 -8
  167. package/docs/webapi/dontSeeCurrentUrlEquals.mustache +0 -10
  168. package/docs/webapi/dontSeeElement.mustache +0 -8
  169. package/docs/webapi/dontSeeElementInDOM.mustache +0 -8
  170. package/docs/webapi/dontSeeInCurrentUrl.mustache +0 -4
  171. package/docs/webapi/dontSeeInField.mustache +0 -11
  172. package/docs/webapi/dontSeeInSource.mustache +0 -8
  173. package/docs/webapi/dontSeeInTitle.mustache +0 -8
  174. package/docs/webapi/doubleClick.mustache +0 -13
  175. package/docs/webapi/downloadFile.mustache +0 -12
  176. package/docs/webapi/dragAndDrop.mustache +0 -9
  177. package/docs/webapi/dragSlider.mustache +0 -11
  178. package/docs/webapi/executeAsyncScript.mustache +0 -24
  179. package/docs/webapi/executeScript.mustache +0 -26
  180. package/docs/webapi/fillField.mustache +0 -16
  181. package/docs/webapi/forceClick.mustache +0 -28
  182. package/docs/webapi/forceRightClick.mustache +0 -18
  183. package/docs/webapi/grabAllWindowHandles.mustache +0 -7
  184. package/docs/webapi/grabAttributeFrom.mustache +0 -10
  185. package/docs/webapi/grabAttributeFromAll.mustache +0 -9
  186. package/docs/webapi/grabBrowserLogs.mustache +0 -9
  187. package/docs/webapi/grabCookie.mustache +0 -11
  188. package/docs/webapi/grabCssPropertyFrom.mustache +0 -11
  189. package/docs/webapi/grabCssPropertyFromAll.mustache +0 -10
  190. package/docs/webapi/grabCurrentUrl.mustache +0 -9
  191. package/docs/webapi/grabCurrentWindowHandle.mustache +0 -6
  192. package/docs/webapi/grabDataFromPerformanceTiming.mustache +0 -20
  193. package/docs/webapi/grabElementBoundingRect.mustache +0 -20
  194. package/docs/webapi/grabGeoLocation.mustache +0 -8
  195. package/docs/webapi/grabHTMLFrom.mustache +0 -10
  196. package/docs/webapi/grabHTMLFromAll.mustache +0 -9
  197. package/docs/webapi/grabNumberOfOpenTabs.mustache +0 -8
  198. package/docs/webapi/grabNumberOfVisibleElements.mustache +0 -9
  199. package/docs/webapi/grabPageScrollPosition.mustache +0 -8
  200. package/docs/webapi/grabPopupText.mustache +0 -5
  201. package/docs/webapi/grabSource.mustache +0 -8
  202. package/docs/webapi/grabTextFrom.mustache +0 -10
  203. package/docs/webapi/grabTextFromAll.mustache +0 -9
  204. package/docs/webapi/grabTitle.mustache +0 -8
  205. package/docs/webapi/grabValueFrom.mustache +0 -9
  206. package/docs/webapi/grabValueFromAll.mustache +0 -8
  207. package/docs/webapi/moveCursorTo.mustache +0 -12
  208. package/docs/webapi/openNewTab.mustache +0 -7
  209. package/docs/webapi/pressKey.mustache +0 -12
  210. package/docs/webapi/pressKeyDown.mustache +0 -12
  211. package/docs/webapi/pressKeyUp.mustache +0 -12
  212. package/docs/webapi/pressKeyWithKeyNormalization.mustache +0 -60
  213. package/docs/webapi/refreshPage.mustache +0 -6
  214. package/docs/webapi/resizeWindow.mustache +0 -6
  215. package/docs/webapi/rightClick.mustache +0 -14
  216. package/docs/webapi/saveElementScreenshot.mustache +0 -10
  217. package/docs/webapi/saveScreenshot.mustache +0 -12
  218. package/docs/webapi/say.mustache +0 -10
  219. package/docs/webapi/scrollIntoView.mustache +0 -11
  220. package/docs/webapi/scrollPageToBottom.mustache +0 -6
  221. package/docs/webapi/scrollPageToTop.mustache +0 -6
  222. package/docs/webapi/scrollTo.mustache +0 -12
  223. package/docs/webapi/see.mustache +0 -11
  224. package/docs/webapi/seeAttributesOnElements.mustache +0 -9
  225. package/docs/webapi/seeCheckboxIsChecked.mustache +0 -10
  226. package/docs/webapi/seeCookie.mustache +0 -8
  227. package/docs/webapi/seeCssPropertiesOnElements.mustache +0 -9
  228. package/docs/webapi/seeCurrentUrlEquals.mustache +0 -11
  229. package/docs/webapi/seeElement.mustache +0 -8
  230. package/docs/webapi/seeElementInDOM.mustache +0 -8
  231. package/docs/webapi/seeInCurrentUrl.mustache +0 -8
  232. package/docs/webapi/seeInField.mustache +0 -12
  233. package/docs/webapi/seeInPopup.mustache +0 -8
  234. package/docs/webapi/seeInSource.mustache +0 -7
  235. package/docs/webapi/seeInTitle.mustache +0 -8
  236. package/docs/webapi/seeNumberOfElements.mustache +0 -11
  237. package/docs/webapi/seeNumberOfVisibleElements.mustache +0 -10
  238. package/docs/webapi/seeTextEquals.mustache +0 -9
  239. package/docs/webapi/seeTitleEquals.mustache +0 -8
  240. package/docs/webapi/selectOption.mustache +0 -21
  241. package/docs/webapi/setCookie.mustache +0 -16
  242. package/docs/webapi/setGeoLocation.mustache +0 -12
  243. package/docs/webapi/switchTo.mustache +0 -9
  244. package/docs/webapi/switchToNextTab.mustache +0 -10
  245. package/docs/webapi/switchToPreviousTab.mustache +0 -10
  246. package/docs/webapi/type.mustache +0 -21
  247. package/docs/webapi/uncheckOption.mustache +0 -13
  248. package/docs/webapi/wait.mustache +0 -8
  249. package/docs/webapi/waitForClickable.mustache +0 -11
  250. package/docs/webapi/waitForDetached.mustache +0 -10
  251. package/docs/webapi/waitForElement.mustache +0 -11
  252. package/docs/webapi/waitForEnabled.mustache +0 -6
  253. package/docs/webapi/waitForFunction.mustache +0 -17
  254. package/docs/webapi/waitForInvisible.mustache +0 -10
  255. package/docs/webapi/waitForText.mustache +0 -13
  256. package/docs/webapi/waitForValue.mustache +0 -10
  257. package/docs/webapi/waitForVisible.mustache +0 -10
  258. package/docs/webapi/waitInUrl.mustache +0 -9
  259. package/docs/webapi/waitNumberOfVisibleElements.mustache +0 -10
  260. package/docs/webapi/waitToHide.mustache +0 -10
  261. package/docs/webapi/waitUrlEquals.mustache +0 -10
  262. package/docs/webdriver.md +0 -657
  263. package/docs/wiki/Books-&-Posts.md +0 -27
  264. package/docs/wiki/Community-Helpers-&-Plugins.md +0 -49
  265. package/docs/wiki/Converting-Playwright-to-Istanbul-Coverage.md +0 -29
  266. package/docs/wiki/Examples.md +0 -139
  267. package/docs/wiki/Google-Summer-of-Code-(GSoC)-2020.md +0 -68
  268. package/docs/wiki/Home.md +0 -16
  269. package/docs/wiki/Release-Process.md +0 -24
  270. package/docs/wiki/Roadmap.md +0 -23
  271. package/docs/wiki/Tests.md +0 -1393
  272. package/docs/wiki/Upgrading-to-CodeceptJS-3.md +0 -153
  273. package/docs/wiki/Videos.md +0 -19
@@ -631,14 +631,16 @@ class Protractor extends Helper {
631
631
  * {{> seeInField }}
632
632
  */
633
633
  async seeInField(field, value) {
634
- return proceedSeeInField.call(this, 'assert', field, value);
634
+ const _value = (typeof value === 'boolean') ? value : value.toString();
635
+ return proceedSeeInField.call(this, 'assert', field, _value);
635
636
  }
636
637
 
637
638
  /**
638
639
  * {{> dontSeeInField }}
639
640
  */
640
641
  async dontSeeInField(field, value) {
641
- return proceedSeeInField.call(this, 'negate', field, value);
642
+ const _value = (typeof value === 'boolean') ? value : value.toString();
643
+ return proceedSeeInField.call(this, 'negate', field, _value);
642
644
  }
643
645
 
644
646
  /**
@@ -1053,7 +1055,7 @@ class Protractor extends Helper {
1053
1055
  const stream = fs.createWriteStream(outputFile);
1054
1056
  stream.write(Buffer.from(png, 'base64'));
1055
1057
  stream.end();
1056
- return new Promise(resolve => stream.on('finish', resolve));
1058
+ return new Promise(resolve => stream.on('finish', resolve)); // eslint-disable-line no-promise-executor-return
1057
1059
  };
1058
1060
 
1059
1061
  const res = await this._locate(locator);
@@ -1076,7 +1078,7 @@ class Protractor extends Helper {
1076
1078
  const stream = fs.createWriteStream(outputFile);
1077
1079
  stream.write(Buffer.from(png, 'base64'));
1078
1080
  stream.end();
1079
- return new Promise(resolve => stream.on('finish', resolve));
1081
+ return new Promise(resolve => stream.on('finish', resolve)); // eslint-disable-line no-promise-executor-return
1080
1082
  };
1081
1083
 
1082
1084
  if (!fullPage) {
@@ -1613,8 +1615,11 @@ class Protractor extends Helper {
1613
1615
  const body = document.body;
1614
1616
  const html = document.documentElement;
1615
1617
  window.scrollTo(0, Math.max(
1616
- body.scrollHeight, body.offsetHeight,
1617
- html.clientHeight, html.scrollHeight, html.offsetHeight
1618
+ body.scrollHeight,
1619
+ body.offsetHeight,
1620
+ html.clientHeight,
1621
+ html.scrollHeight,
1622
+ html.offsetHeight
1618
1623
  ));
1619
1624
  });
1620
1625
  /* eslint-enable */
@@ -4,6 +4,7 @@ const fsExtra = require('fs-extra');
4
4
  const path = require('path');
5
5
 
6
6
  const Helper = require('@codeceptjs/helper');
7
+ const { v4: uuidv4 } = require('uuid');
7
8
  const Locator = require('../locator');
8
9
  const recorder = require('../recorder');
9
10
  const store = require('../store');
@@ -19,11 +20,12 @@ const {
19
20
  fileExists,
20
21
  chunkArray,
21
22
  toCamelCase,
23
+ clearString,
22
24
  convertCssPropertiesToCamelCase,
23
25
  screenshotOutputFolder,
24
26
  getNormalizedKeyAttributeValue,
25
27
  isModifierKey,
26
- requireWithFallback,
28
+ requireWithFallback, normalizeSpacesInString,
27
29
  } = require('../utils');
28
30
  const {
29
31
  isColorProperty,
@@ -35,6 +37,8 @@ const Popup = require('./extras/Popup');
35
37
  const Console = require('./extras/Console');
36
38
  const findReact = require('./extras/React');
37
39
  const { highlightElement } = require('./scripts/highlightElement');
40
+ const { blurElement } = require('./scripts/blurElement');
41
+ const { focusElement } = require('./scripts/focusElement');
38
42
 
39
43
  let puppeteer;
40
44
  let perfTiming;
@@ -55,6 +59,8 @@ const consoleLogStore = new Console();
55
59
  * @prop {boolean} [disableScreenshots=false] - don't save screenshot on failure.
56
60
  * @prop {boolean} [fullPageScreenshots=false] - make full page screenshots on failure.
57
61
  * @prop {boolean} [uniqueScreenshotNames=false] - option to prevent screenshot override if you have scenarios with the same name in different suites.
62
+ * @prop {boolean} [trace=false] - record [tracing information](https://pptr.dev/api/puppeteer.tracing) with screenshots.
63
+ * @prop {boolean} [keepTraceForPassedTests=false] - save trace for passed tests.
58
64
  * @prop {boolean} [keepBrowserState=false] - keep browser state between tests when `restart` is set to false.
59
65
  * @prop {boolean} [keepCookies=false] - keep cookies between tests when `restart` is set to false.
60
66
  * @prop {number} [waitForAction=100] - how long to wait after click, doubleClick or PressKey actions in ms. Default: 100.
@@ -67,7 +73,7 @@ const consoleLogStore = new Console();
67
73
  * @prop {boolean} [manualStart=false] - do not start browser before a test, start it manually inside a helper with `this.helpers["Puppeteer"]._startBrowser()`.
68
74
  * @prop {string} [browser=chrome] - can be changed to `firefox` when using [puppeteer-firefox](https://codecept.io/helpers/Puppeteer-firefox).
69
75
  * @prop {object} [chrome] - pass additional [Puppeteer run options](https://github.com/GoogleChrome/puppeteer/blob/master/docs/api.md#puppeteerlaunchoptions).
70
- * @prop {boolean} [highlightElement] - highlight the interacting elements
76
+ * @prop {boolean} [highlightElement] - highlight the interacting elements. Default: false. Note: only activate under verbose mode (--verbose).
71
77
  */
72
78
  const config = {};
73
79
 
@@ -90,6 +96,14 @@ const config = {};
90
96
  *
91
97
  * <!-- configuration -->
92
98
  *
99
+ * #### Trace Recording Customization
100
+ *
101
+ * Trace recording provides complete information on test execution and includes screenshots, and network requests logged during run.
102
+ * Traces will be saved to `output/trace`
103
+ *
104
+ * * `trace`: enables trace recording for failed tests; trace are saved into `output/trace` folder
105
+ * * `keepTraceForPassedTests`: - save trace for passed tests
106
+ *
93
107
  * #### Example #1: Wait for 0 network connections.
94
108
  *
95
109
  * ```js
@@ -229,6 +243,7 @@ class Puppeteer extends Helper {
229
243
  keepBrowserState: false,
230
244
  show: false,
231
245
  defaultPopupAction: 'accept',
246
+ highlightElement: false,
232
247
  };
233
248
 
234
249
  return Object.assign(defaults, config);
@@ -244,6 +259,7 @@ class Puppeteer extends Helper {
244
259
  headless: !this.options.show,
245
260
  ...this._getOptions(config),
246
261
  };
262
+ if (this.puppeteerOptions.headless) this.puppeteerOptions.headless = 'new';
247
263
  this.isRemoteBrowser = !!this.puppeteerOptions.browserWSEndpoint;
248
264
  popupStore.defaultAction = this.options.defaultPopupAction;
249
265
  }
@@ -278,10 +294,11 @@ class Puppeteer extends Helper {
278
294
  }
279
295
  }
280
296
 
281
- async _before() {
297
+ async _before(test) {
282
298
  this.sessionPages = {};
299
+ this.currentRunningTest = test;
283
300
  recorder.retry({
284
- retries: 3,
301
+ retries: process.env.FAILED_STEP_RETRIES || 3,
285
302
  when: err => {
286
303
  if (!err || typeof (err.message) !== 'string') {
287
304
  return false;
@@ -601,8 +618,8 @@ class Puppeteer extends Helper {
601
618
  return this.switchTo(null)
602
619
  .then(() => frame.reduce((p, frameLocator) => p.then(() => this.switchTo(frameLocator)), Promise.resolve()));
603
620
  }
604
- await this.switchTo(locator);
605
- this.withinLocator = new Locator(locator);
621
+ await this.switchTo(frame);
622
+ this.withinLocator = new Locator(frame);
606
623
  return;
607
624
  }
608
625
 
@@ -644,6 +661,14 @@ class Puppeteer extends Helper {
644
661
  }
645
662
  }
646
663
 
664
+ if (this.options.trace) {
665
+ const fileName = `${`${global.output_dir}${path.sep}trace${path.sep}${uuidv4()}_${clearString(this.currentRunningTest.title)}`.slice(0, 245)}.json`;
666
+ const dir = path.dirname(fileName);
667
+ if (!fileExists(dir)) fs.mkdirSync(dir);
668
+ await this.page.tracing.start({ screenshots: true, path: fileName });
669
+ this.currentRunningTest.artifacts.trace = fileName;
670
+ }
671
+
647
672
  await this.page.goto(url, { waitUntil: this.options.waitForNavigation });
648
673
 
649
674
  const performanceTiming = JSON.parse(await this.page.evaluate(() => JSON.stringify(window.performance.timing)));
@@ -660,11 +685,13 @@ class Puppeteer extends Helper {
660
685
  }
661
686
 
662
687
  /**
663
- * {{> resizeWindow }}
664
688
  *
665
689
  * Unlike other drivers Puppeteer changes the size of a viewport, not the window!
666
- * Puppeteer does not control the window of a browser so it can't adjust its real size.
690
+ * Puppeteer does not control the window of a browser, so it can't adjust its real size.
667
691
  * It also can't maximize a window.
692
+ *
693
+ * {{> resizeWindow }}
694
+ *
668
695
  */
669
696
  async resizeWindow(width, height) {
670
697
  if (width === 'maximize') {
@@ -679,14 +706,14 @@ class Puppeteer extends Helper {
679
706
  * Set headers for all next requests
680
707
  *
681
708
  * ```js
682
- * I.haveRequestHeaders({
709
+ * I.setPuppeteerRequestHeaders({
683
710
  * 'X-Sent-By': 'CodeceptJS',
684
711
  * });
685
712
  * ```
686
713
  *
687
714
  * @param {object} customHeaders headers to set
688
715
  */
689
- async haveRequestHeaders(customHeaders) {
716
+ async setPuppeteerRequestHeaders(customHeaders) {
690
717
  if (!customHeaders) {
691
718
  throw new Error('Cannot send empty headers.');
692
719
  }
@@ -707,6 +734,32 @@ class Puppeteer extends Helper {
707
734
  return this._waitForAction();
708
735
  }
709
736
 
737
+ /**
738
+ * {{> focus }}
739
+ *
740
+ */
741
+ async focus(locator) {
742
+ const els = await this._locate(locator);
743
+ assertElementExists(els, locator, 'Element to focus');
744
+ const el = els[0];
745
+
746
+ await el.click();
747
+ await el.focus();
748
+ return this._waitForAction();
749
+ }
750
+
751
+ /**
752
+ * {{> blur }}
753
+ *
754
+ */
755
+ async blur(locator) {
756
+ const els = await this._locate(locator);
757
+ assertElementExists(els, locator, 'Element to blur');
758
+
759
+ await blurElement(els[0], this.page);
760
+ return this._waitForAction();
761
+ }
762
+
710
763
  /**
711
764
  * {{> dragAndDrop }}
712
765
  */
@@ -738,8 +791,11 @@ class Puppeteer extends Helper {
738
791
  const body = document.body;
739
792
  const html = document.documentElement;
740
793
  window.scrollTo(0, Math.max(
741
- body.scrollHeight, body.offsetHeight,
742
- html.clientHeight, html.scrollHeight, html.offsetHeight,
794
+ body.scrollHeight,
795
+ body.offsetHeight,
796
+ html.clientHeight,
797
+ html.scrollHeight,
798
+ html.offsetHeight,
743
799
  ));
744
800
  });
745
801
  }
@@ -828,7 +884,7 @@ class Puppeteer extends Helper {
828
884
  }
829
885
 
830
886
  /**
831
- * Find a checkbox by providing human readable text:
887
+ * Find a checkbox by providing human-readable text:
832
888
  * NOTE: Assumes the checkable element exists
833
889
  *
834
890
  * ```js
@@ -843,7 +899,7 @@ class Puppeteer extends Helper {
843
899
  }
844
900
 
845
901
  /**
846
- * Find a clickable element by providing human readable text:
902
+ * Find a clickable element by providing human-readable text:
847
903
  *
848
904
  * ```js
849
905
  * this.helpers['Puppeteer']._locateClickable('Next page').then // ...
@@ -855,7 +911,7 @@ class Puppeteer extends Helper {
855
911
  }
856
912
 
857
913
  /**
858
- * Find field elements by providing human readable text:
914
+ * Find field elements by providing human-readable text:
859
915
  *
860
916
  * ```js
861
917
  * this.helpers['Puppeteer']._locateFields('Your email').then // ...
@@ -865,6 +921,14 @@ class Puppeteer extends Helper {
865
921
  return findFields.call(this, locator);
866
922
  }
867
923
 
924
+ /**
925
+ * {{> grabWebElements }}
926
+ *
927
+ */
928
+ async grabWebElements(locator) {
929
+ return this._locate(locator);
930
+ }
931
+
868
932
  /**
869
933
  * Switch focus to a particular tab by its number. It waits tabs loading and then switch tab
870
934
  *
@@ -1059,7 +1123,7 @@ class Puppeteer extends Helper {
1059
1123
  * Sets a directory to where save files. Allows to test file downloads.
1060
1124
  * Should be used with [FileSystem helper](https://codecept.io/helpers/FileSystem) to check that file were downloaded correctly.
1061
1125
  *
1062
- * By default files are saved to `output/downloads`.
1126
+ * By default, files are saved to `output/downloads`.
1063
1127
  * This directory is cleaned on every `handleDownloads` call, to ensure no old files are kept.
1064
1128
  *
1065
1129
  * ```js
@@ -1231,9 +1295,9 @@ class Puppeteer extends Helper {
1231
1295
  }
1232
1296
 
1233
1297
  /**
1234
- * {{> pressKeyWithKeyNormalization }}
1235
- *
1236
1298
  * _Note:_ Shortcuts like `'Meta'` + `'A'` do not work on macOS ([GoogleChrome/puppeteer#1313](https://github.com/GoogleChrome/puppeteer/issues/1313)).
1299
+ *
1300
+ * {{> pressKeyWithKeyNormalization }}
1237
1301
  */
1238
1302
  async pressKey(key) {
1239
1303
  const modifiers = [];
@@ -1291,7 +1355,7 @@ class Puppeteer extends Helper {
1291
1355
  await this._evaluateHandeInContext(el => el.innerHTML = '', el);
1292
1356
  }
1293
1357
 
1294
- highlightActiveElement.call(this, el, this.page);
1358
+ highlightActiveElement.call(this, el, await this._getContext());
1295
1359
  await el.type(value.toString(), { delay: this.options.pressKeyDelay });
1296
1360
 
1297
1361
  return this._waitForAction();
@@ -1312,7 +1376,7 @@ class Puppeteer extends Helper {
1312
1376
  async appendField(field, value) {
1313
1377
  const els = await findVisibleFields.call(this, field);
1314
1378
  assertElementExists(els, field, 'Field');
1315
- highlightActiveElement.call(this, els[0], this.page);
1379
+ highlightActiveElement.call(this, els[0], await this._getContext());
1316
1380
  await els[0].press('End');
1317
1381
  await els[0].type(value.toString(), { delay: this.options.pressKeyDelay });
1318
1382
  return this._waitForAction();
@@ -1322,20 +1386,22 @@ class Puppeteer extends Helper {
1322
1386
  * {{> seeInField }}
1323
1387
  */
1324
1388
  async seeInField(field, value) {
1325
- return proceedSeeInField.call(this, 'assert', field, value);
1389
+ const _value = (typeof value === 'boolean') ? value : value.toString();
1390
+ return proceedSeeInField.call(this, 'assert', field, _value);
1326
1391
  }
1327
1392
 
1328
1393
  /**
1329
1394
  * {{> dontSeeInField }}
1330
1395
  */
1331
1396
  async dontSeeInField(field, value) {
1332
- return proceedSeeInField.call(this, 'negate', field, value);
1397
+ const _value = (typeof value === 'boolean') ? value : value.toString();
1398
+ return proceedSeeInField.call(this, 'negate', field, _value);
1333
1399
  }
1334
1400
 
1335
1401
  /**
1336
- * {{> attachFile }}
1337
- *
1338
1402
  * > ⚠ There is an [issue with file upload in Puppeteer 2.1.0 & 2.1.1](https://github.com/puppeteer/puppeteer/issues/5420), downgrade to 2.0.0 if you face it.
1403
+ *
1404
+ * {{> attachFile }}
1339
1405
  */
1340
1406
  async attachFile(locator, pathToFile) {
1341
1407
  const file = path.join(global.codecept_dir, pathToFile);
@@ -1359,7 +1425,7 @@ class Puppeteer extends Helper {
1359
1425
  if (await el.getProperty('tagName').then(t => t.jsonValue()) !== 'SELECT') {
1360
1426
  throw new Error('Element is not <select>');
1361
1427
  }
1362
- highlightActiveElement.call(this, els[0], this.page);
1428
+ highlightActiveElement.call(this, els[0], await this._getContext());
1363
1429
  if (!Array.isArray(option)) option = [option];
1364
1430
 
1365
1431
  for (const key in option) {
@@ -1566,9 +1632,9 @@ class Puppeteer extends Helper {
1566
1632
  }
1567
1633
 
1568
1634
  /**
1569
- * {{> executeScript }}
1635
+ * If a function returns a Promise, tt will wait for its resolution.
1570
1636
  *
1571
- * If a function returns a Promise It will wait for it resolution.
1637
+ * {{> executeScript }}
1572
1638
  */
1573
1639
  async executeScript(...args) {
1574
1640
  let context = this.page;
@@ -1579,9 +1645,8 @@ class Puppeteer extends Helper {
1579
1645
  }
1580
1646
 
1581
1647
  /**
1582
- * {{> executeAsyncScript }}
1583
- *
1584
1648
  * Asynchronous scripts can also be executed with `executeScript` if a function returns a Promise.
1649
+ * {{> executeAsyncScript }}
1585
1650
  */
1586
1651
  async executeAsyncScript(...args) {
1587
1652
  const asyncFn = function () {
@@ -1708,29 +1773,26 @@ class Puppeteer extends Helper {
1708
1773
 
1709
1774
  const cssPropertiesCamelCase = convertCssPropertiesToCamelCase(cssProperties);
1710
1775
  const elemAmount = res.length;
1711
- const commands = [];
1712
- res.forEach((el) => {
1713
- Object.keys(cssPropertiesCamelCase).forEach((prop) => {
1714
- commands.push(el.executionContext()
1715
- .evaluate((el) => {
1716
- const style = window.getComputedStyle ? getComputedStyle(el) : el.currentStyle;
1717
- return JSON.parse(JSON.stringify(style));
1718
- }, el)
1719
- .then((props) => {
1720
- if (isColorProperty(prop)) {
1721
- return convertColorToRGBA(props[prop]);
1722
- }
1723
- return props[prop];
1724
- }));
1725
- });
1726
- });
1727
- let props = await Promise.all(commands);
1776
+ let props = [];
1777
+
1778
+ for (const element of res) {
1779
+ for (const prop of Object.keys(cssProperties)) {
1780
+ const cssProp = await this.grabCssPropertyFrom(locator, prop);
1781
+ if (isColorProperty(prop)) {
1782
+ props.push(convertColorToRGBA(cssProp));
1783
+ } else {
1784
+ props.push(cssProp);
1785
+ }
1786
+ }
1787
+ }
1788
+
1728
1789
  const values = Object.keys(cssPropertiesCamelCase).map(key => cssPropertiesCamelCase[key]);
1729
1790
  if (!Array.isArray(props)) props = [props];
1730
1791
  let chunked = chunkArray(props, values.length);
1731
1792
  chunked = chunked.filter((val) => {
1732
1793
  for (let i = 0; i < val.length; ++i) {
1733
- if (val[i] !== values[i]) return false;
1794
+ // eslint-disable-next-line eqeqeq
1795
+ if (val[i] != values[i]) return false;
1734
1796
  }
1735
1797
  return true;
1736
1798
  });
@@ -1761,7 +1823,10 @@ class Puppeteer extends Helper {
1761
1823
  let chunked = chunkArray(attrs, values.length);
1762
1824
  chunked = chunked.filter((val) => {
1763
1825
  for (let i = 0; i < val.length; ++i) {
1764
- if (val[i] !== values[i]) return false;
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;
1765
1830
  }
1766
1831
  return true;
1767
1832
  });
@@ -1865,8 +1930,30 @@ class Puppeteer extends Helper {
1865
1930
  }
1866
1931
  }
1867
1932
 
1868
- async _failed() {
1933
+ async _failed(test) {
1869
1934
  await this._withinEnd();
1935
+
1936
+ if (this.options.trace) {
1937
+ await this.page.tracing.stop();
1938
+ const _traceName = this.currentRunningTest.artifacts.trace.replace('.json', '.failed.json');
1939
+ fs.renameSync(this.currentRunningTest.artifacts.trace, _traceName);
1940
+ test.artifacts.trace = _traceName;
1941
+ }
1942
+ }
1943
+
1944
+ async _passed(test) {
1945
+ await this._withinEnd();
1946
+
1947
+ if (this.options.trace) {
1948
+ await this.page.tracing.stop();
1949
+ if (this.options.keepTraceForPassedTests) {
1950
+ const _traceName = this.currentRunningTest.artifacts.trace.replace('.json', '.passed.json');
1951
+ fs.renameSync(this.currentRunningTest.artifacts.trace, _traceName);
1952
+ test.artifacts.trace = _traceName;
1953
+ } else {
1954
+ fs.unlinkSync(this.currentRunningTest.artifacts.trace);
1955
+ }
1956
+ }
1870
1957
  }
1871
1958
 
1872
1959
  /**
@@ -1978,7 +2065,7 @@ class Puppeteer extends Helper {
1978
2065
  assertElementExists(els, locator);
1979
2066
 
1980
2067
  return this.waitForFunction(isElementClickable, [els[0]], waitTimeout).catch(async (e) => {
1981
- if (/failed: timeout/i.test(e.message)) {
2068
+ if (/Waiting failed/i.test(e.message) || /failed: timeout/i.test(e.message)) {
1982
2069
  throw new Error(`element ${new Locator(locator).toString()} still not clickable after ${waitTimeout || this.options.waitForTimeout / 1000} sec`);
1983
2070
  } else {
1984
2071
  throw e;
@@ -2009,7 +2096,7 @@ class Puppeteer extends Helper {
2009
2096
  /**
2010
2097
  * {{> waitForVisible }}
2011
2098
  *
2012
- * This method accepts [React selectors](https://codecept.io/react).
2099
+ * {{ react }}
2013
2100
  */
2014
2101
  async waitForVisible(locator, sec) {
2015
2102
  const waitTimeout = sec ? sec * 1000 : this.options.waitForTimeout;
@@ -2064,6 +2151,24 @@ class Puppeteer extends Helper {
2064
2151
  });
2065
2152
  }
2066
2153
 
2154
+ /**
2155
+ * {{> waitForNumberOfTabs }}
2156
+ */
2157
+ async waitForNumberOfTabs(expectedTabs, sec) {
2158
+ const waitTimeout = sec ? sec * 1000 : this.options.waitForTimeout;
2159
+ let currentTabs;
2160
+ let count = 0;
2161
+
2162
+ do {
2163
+ currentTabs = await this.grabNumberOfOpenTabs();
2164
+ await this.wait(1);
2165
+ count += 1000;
2166
+ if (currentTabs >= expectedTabs) return;
2167
+ } while (count <= waitTimeout);
2168
+
2169
+ throw new Error(`Expected ${expectedTabs} tabs are not met after ${waitTimeout / 1000} sec.`);
2170
+ }
2171
+
2067
2172
  async _getContext() {
2068
2173
  if (this.context && this.context.constructor.name === 'Frame') {
2069
2174
  return this.context;
@@ -2082,7 +2187,7 @@ class Puppeteer extends Helper {
2082
2187
  return currUrl.indexOf(urlPart) > -1;
2083
2188
  }, { timeout: waitTimeout }, urlPart).catch(async (e) => {
2084
2189
  const currUrl = await this._getPageUrl(); // Required because the waitForFunction can't return data.
2085
- if (/failed: timeout/i.test(e.message)) {
2190
+ if (/Waiting failed:/i.test(e.message) || /failed: timeout/i.test(e.message)) {
2086
2191
  throw new Error(`expected url to include ${urlPart}, but found ${currUrl}`);
2087
2192
  } else {
2088
2193
  throw e;
@@ -2106,7 +2211,7 @@ class Puppeteer extends Helper {
2106
2211
  return currUrl.indexOf(urlPart) > -1;
2107
2212
  }, { timeout: waitTimeout }, urlPart).catch(async (e) => {
2108
2213
  const currUrl = await this._getPageUrl(); // Required because the waitForFunction can't return data.
2109
- if (/failed: timeout/i.test(e.message)) {
2214
+ if (/Waiting failed/i.test(e.message) || /failed: timeout/i.test(e.message)) {
2110
2215
  throw new Error(`expected url to be ${urlPart}, but found ${currUrl}`);
2111
2216
  } else {
2112
2217
  throw e;
@@ -2238,9 +2343,9 @@ class Puppeteer extends Helper {
2238
2343
  }
2239
2344
 
2240
2345
  /**
2241
- * Waits for navigation to finish. By default takes configured `waitForNavigation` option.
2346
+ * Waits for navigation to finish. By default, takes configured `waitForNavigation` option.
2242
2347
  *
2243
- * See [Pupeteer's reference](https://github.com/GoogleChrome/puppeteer/blob/master/docs/api.md#pagewaitfornavigationoptions)
2348
+ * See [Puppeteer's reference](https://github.com/GoogleChrome/puppeteer/blob/master/docs/api.md#pagewaitfornavigationoptions)
2244
2349
  *
2245
2350
  * @param {*} opts
2246
2351
  */
@@ -2315,6 +2420,10 @@ async function findElements(matcher, locator) {
2315
2420
  if (locator.react) return findReact(matcher.executionContext(), locator);
2316
2421
  locator = new Locator(locator, 'css');
2317
2422
  if (!locator.isXPath()) return matcher.$$(locator.simplify());
2423
+ // puppeteer version < 19.4.0 is no longer supported. This one is backward support.
2424
+ if (puppeteer.default?.defaultBrowserRevision) {
2425
+ return matcher.$$(`xpath/${locator.value}`);
2426
+ }
2318
2427
  return matcher.$x(locator.value);
2319
2428
  }
2320
2429
 
@@ -2332,7 +2441,7 @@ async function proceedClick(locator, context = null, options = {}) {
2332
2441
  assertElementExists(els, locator, 'Clickable element');
2333
2442
  }
2334
2443
 
2335
- highlightActiveElement.call(this, els[0], this.page);
2444
+ highlightActiveElement.call(this, els[0], await this._getContext());
2336
2445
 
2337
2446
  await els[0].click(options);
2338
2447
  const promises = [];
@@ -2392,7 +2501,7 @@ async function proceedSee(assertType, text, context, strict = false) {
2392
2501
  if (strict) {
2393
2502
  return allText.map(elText => equals(description)[assertType](text, elText));
2394
2503
  }
2395
- return stringIncludes(description)[assertType](text, allText.join(' | '));
2504
+ return stringIncludes(description)[assertType](normalizeSpacesInString(text), normalizeSpacesInString(allText.join(' | ')));
2396
2505
  }
2397
2506
 
2398
2507
  async function findCheckable(locator, context) {
@@ -2569,7 +2678,10 @@ async function elementSelected(element) {
2569
2678
 
2570
2679
  function isFrameLocator(locator) {
2571
2680
  locator = new Locator(locator);
2572
- if (locator.isFrame()) return locator.value;
2681
+ if (locator.isFrame()) {
2682
+ const _locator = new Locator(locator);
2683
+ return _locator.value;
2684
+ }
2573
2685
  return false;
2574
2686
  }
2575
2687
 
@@ -2687,7 +2799,7 @@ function getNormalizedKey(key) {
2687
2799
  }
2688
2800
 
2689
2801
  function highlightActiveElement(element, context) {
2690
- if (!this.options.enableHighlight && !store.debugMode) return;
2691
-
2692
- highlightElement(element, context);
2802
+ if (this.options.highlightElement && global.debugMode) {
2803
+ highlightElement(element, context);
2804
+ }
2693
2805
  }