codeceptjs 4.0.0-rc.8 → 4.0.0

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 (314) hide show
  1. package/README.md +9 -10
  2. package/bin/codecept.js +15 -2
  3. package/bin/codeceptq.js +49 -0
  4. package/bin/mcp-server.js +751 -172
  5. package/docs/advanced.md +201 -0
  6. package/docs/agents.md +181 -0
  7. package/docs/ai.md +489 -0
  8. package/docs/aitrace.md +266 -0
  9. package/docs/api.md +332 -0
  10. package/docs/architecture.md +235 -0
  11. package/docs/assertions.md +415 -0
  12. package/docs/auth.md +318 -0
  13. package/docs/basics.md +424 -0
  14. package/docs/bdd.md +539 -0
  15. package/docs/best.md +240 -0
  16. package/docs/bootstrap.md +132 -0
  17. package/docs/commands.md +352 -0
  18. package/docs/community-helpers.md +63 -0
  19. package/docs/configuration.md +185 -0
  20. package/docs/continuous-integration.md +431 -0
  21. package/docs/custom-helpers.md +297 -0
  22. package/docs/data.md +448 -0
  23. package/docs/debugging.md +332 -0
  24. package/docs/detox.md +235 -0
  25. package/docs/docker.md +107 -0
  26. package/docs/effects.md +179 -0
  27. package/docs/element-based-testing.md +295 -0
  28. package/docs/element-selection.md +125 -0
  29. package/docs/els.md +328 -0
  30. package/docs/environment-variables.md +131 -0
  31. package/docs/examples.md +160 -0
  32. package/docs/heal.md +213 -0
  33. package/docs/helpers/ApiDataFactory.md +267 -0
  34. package/docs/helpers/Appium.md +1419 -0
  35. package/docs/helpers/Detox.md +665 -0
  36. package/docs/helpers/ExpectHelper.md +275 -0
  37. package/docs/helpers/FileSystem.md +152 -0
  38. package/docs/helpers/GraphQL.md +152 -0
  39. package/docs/helpers/GraphQLDataFactory.md +226 -0
  40. package/docs/helpers/JSONResponse.md +255 -0
  41. package/docs/helpers/MockRequest.md +377 -0
  42. package/docs/helpers/Playwright.md +2970 -0
  43. package/docs/helpers/Puppeteer-firefox.md +86 -0
  44. package/docs/helpers/Puppeteer.md +2583 -0
  45. package/docs/helpers/REST.md +289 -0
  46. package/docs/helpers/WebDriver.md +2639 -0
  47. package/docs/hooks.md +148 -0
  48. package/docs/index.md +111 -0
  49. package/docs/installation.md +121 -0
  50. package/docs/internal-test-server.md +89 -0
  51. package/docs/locators.md +355 -0
  52. package/docs/mcp.md +485 -0
  53. package/docs/migrate-from-cypress.md +98 -0
  54. package/docs/migrate-from-java.md +108 -0
  55. package/docs/migrate-from-protractor.md +101 -0
  56. package/docs/migrate-from-testcafe.md +99 -0
  57. package/docs/migration-4.md +743 -0
  58. package/docs/mobile.md +338 -0
  59. package/docs/pageobjects.md +399 -0
  60. package/docs/parallel.md +187 -0
  61. package/docs/playwright.md +714 -0
  62. package/docs/plugins/aiTrace.md +49 -0
  63. package/docs/plugins/analyze.md +66 -0
  64. package/docs/plugins/auth.md +241 -0
  65. package/docs/plugins/autoDelay.md +48 -0
  66. package/docs/plugins/browser.md +41 -0
  67. package/docs/plugins/coverage.md +39 -0
  68. package/docs/plugins/customLocator.md +119 -0
  69. package/docs/plugins/customReporter.md +16 -0
  70. package/docs/plugins/expose.md +75 -0
  71. package/docs/plugins/heal.md +44 -0
  72. package/docs/plugins/junitReporter.md +51 -0
  73. package/docs/plugins/pageInfo.md +34 -0
  74. package/docs/plugins/pause.md +43 -0
  75. package/docs/plugins/pauseOnFail.md +18 -0
  76. package/docs/plugins/retryFailedStep.md +75 -0
  77. package/docs/plugins/screencast.md +55 -0
  78. package/docs/plugins/screenshot.md +58 -0
  79. package/docs/plugins/screenshotOnFail.md +18 -0
  80. package/docs/plugins/stepTimeout.md +65 -0
  81. package/docs/plugins.md +87 -0
  82. package/docs/puppeteer.md +314 -0
  83. package/docs/quickstart.md +120 -0
  84. package/docs/reports.md +198 -0
  85. package/docs/retry.md +311 -0
  86. package/docs/secrets.md +150 -0
  87. package/docs/sessions.md +80 -0
  88. package/docs/shadow.md +68 -0
  89. package/docs/store.md +94 -0
  90. package/docs/test-structure.md +275 -0
  91. package/docs/timeouts.md +183 -0
  92. package/docs/translation.md +247 -0
  93. package/docs/tutorial.md +323 -0
  94. package/docs/typescript.md +159 -0
  95. package/docs/web-element.md +251 -0
  96. package/docs/webdriver.md +641 -0
  97. package/docs/within.md +55 -0
  98. package/lib/actor.js +1 -36
  99. package/lib/ai.js +3 -2
  100. package/lib/aria.js +260 -0
  101. package/lib/assertions.js +18 -0
  102. package/lib/codecept.js +7 -7
  103. package/lib/command/check.js +2 -1
  104. package/lib/command/dryRun.js +24 -5
  105. package/lib/command/generate.js +2 -0
  106. package/lib/command/gherkin/snippets.js +5 -4
  107. package/lib/command/init.js +248 -266
  108. package/lib/command/list.js +150 -10
  109. package/lib/command/query.js +218 -0
  110. package/lib/command/run-multiple.js +3 -2
  111. package/lib/command/run-workers.js +1 -14
  112. package/lib/command/run.js +3 -17
  113. package/lib/command/utils.js +14 -0
  114. package/lib/command/workers/runTests.js +11 -15
  115. package/lib/config.js +77 -4
  116. package/lib/container.js +97 -15
  117. package/lib/effects.js +17 -0
  118. package/lib/element/WebElement.js +195 -3
  119. package/lib/els.js +12 -6
  120. package/lib/globals.js +32 -19
  121. package/lib/heal.js +7 -4
  122. package/lib/helper/ApiDataFactory.js +2 -1
  123. package/lib/helper/FileSystem.js +3 -2
  124. package/lib/helper/GraphQLDataFactory.js +2 -1
  125. package/lib/helper/Playwright.js +96 -115
  126. package/lib/helper/Puppeteer.js +43 -131
  127. package/lib/helper/WebDriver.js +42 -52
  128. package/lib/helper/errors/NonFocusedType.js +8 -0
  129. package/lib/helper/extras/Download.js +45 -0
  130. package/lib/helper/extras/PlaywrightLocator.js +10 -0
  131. package/lib/helper/extras/elementSelection.js +58 -0
  132. package/lib/helper/extras/focusCheck.js +43 -0
  133. package/lib/helper/extras/richTextEditor.js +178 -0
  134. package/lib/history.js +3 -2
  135. package/lib/html.js +90 -16
  136. package/lib/index.js +9 -1
  137. package/lib/listener/config.js +6 -4
  138. package/lib/listener/emptyRun.js +2 -1
  139. package/lib/listener/helpers.js +4 -1
  140. package/lib/listener/mocha.js +2 -1
  141. package/lib/listener/pageobjects.js +43 -0
  142. package/lib/listener/result.js +3 -2
  143. package/lib/locator.js +126 -16
  144. package/lib/mocha/cli.js +4 -2
  145. package/lib/mocha/factory.js +7 -2
  146. package/lib/mocha/inject.js +1 -1
  147. package/lib/mocha/scenarioConfig.js +2 -1
  148. package/lib/mocha/ui.js +5 -6
  149. package/lib/parser.js +2 -2
  150. package/lib/pause.js +38 -4
  151. package/lib/plugin/aiTrace.js +96 -103
  152. package/lib/plugin/analyze.js +9 -9
  153. package/lib/plugin/auth.js +3 -3
  154. package/lib/plugin/browser.js +77 -0
  155. package/lib/plugin/expose.js +159 -0
  156. package/lib/plugin/heal.js +47 -3
  157. package/lib/plugin/junitReporter.js +303 -0
  158. package/lib/plugin/pageInfo.js +54 -52
  159. package/lib/plugin/pause.js +131 -0
  160. package/lib/plugin/pauseOnFail.js +11 -33
  161. package/lib/plugin/retryFailedStep.js +15 -13
  162. package/lib/plugin/screencast.js +289 -0
  163. package/lib/plugin/screenshot.js +558 -0
  164. package/lib/plugin/screenshotOnFail.js +9 -170
  165. package/lib/plugin/stepTimeout.js +3 -2
  166. package/lib/recorder.js +1 -1
  167. package/lib/rerun.js +2 -1
  168. package/lib/result.js +2 -1
  169. package/lib/step/base.js +10 -9
  170. package/lib/step/comment.js +2 -2
  171. package/lib/step/config.js +15 -2
  172. package/lib/step/helper.js +4 -4
  173. package/lib/step/meta.js +3 -3
  174. package/lib/step/record.js +5 -5
  175. package/lib/store.js +72 -3
  176. package/lib/translation.js +2 -1
  177. package/lib/utils/mask_data.js +2 -1
  178. package/lib/utils/pluginParser.js +151 -0
  179. package/lib/utils/trace.js +297 -0
  180. package/lib/utils.js +29 -3
  181. package/lib/workers.js +14 -22
  182. package/package.json +17 -14
  183. package/typings/index.d.ts +19 -5
  184. package/docs/webapi/amOnPage.mustache +0 -11
  185. package/docs/webapi/appendField.mustache +0 -16
  186. package/docs/webapi/attachFile.mustache +0 -24
  187. package/docs/webapi/blur.mustache +0 -18
  188. package/docs/webapi/checkOption.mustache +0 -13
  189. package/docs/webapi/clearCookie.mustache +0 -9
  190. package/docs/webapi/clearField.mustache +0 -14
  191. package/docs/webapi/click.mustache +0 -29
  192. package/docs/webapi/clickLink.mustache +0 -8
  193. package/docs/webapi/closeCurrentTab.mustache +0 -7
  194. package/docs/webapi/closeOtherTabs.mustache +0 -8
  195. package/docs/webapi/dontSee.mustache +0 -11
  196. package/docs/webapi/dontSeeCheckboxIsChecked.mustache +0 -10
  197. package/docs/webapi/dontSeeCookie.mustache +0 -8
  198. package/docs/webapi/dontSeeCurrentPathEquals.mustache +0 -10
  199. package/docs/webapi/dontSeeCurrentUrlEquals.mustache +0 -10
  200. package/docs/webapi/dontSeeElement.mustache +0 -12
  201. package/docs/webapi/dontSeeElementInDOM.mustache +0 -8
  202. package/docs/webapi/dontSeeInCurrentUrl.mustache +0 -4
  203. package/docs/webapi/dontSeeInField.mustache +0 -16
  204. package/docs/webapi/dontSeeInSource.mustache +0 -8
  205. package/docs/webapi/dontSeeInTitle.mustache +0 -8
  206. package/docs/webapi/dontSeeTraffic.mustache +0 -13
  207. package/docs/webapi/doubleClick.mustache +0 -13
  208. package/docs/webapi/downloadFile.mustache +0 -12
  209. package/docs/webapi/dragAndDrop.mustache +0 -9
  210. package/docs/webapi/dragSlider.mustache +0 -11
  211. package/docs/webapi/executeAsyncScript.mustache +0 -24
  212. package/docs/webapi/executeScript.mustache +0 -26
  213. package/docs/webapi/fillField.mustache +0 -21
  214. package/docs/webapi/flushNetworkTraffics.mustache +0 -5
  215. package/docs/webapi/focus.mustache +0 -13
  216. package/docs/webapi/forceClick.mustache +0 -28
  217. package/docs/webapi/forceRightClick.mustache +0 -18
  218. package/docs/webapi/grabAllWindowHandles.mustache +0 -7
  219. package/docs/webapi/grabAttributeFrom.mustache +0 -10
  220. package/docs/webapi/grabAttributeFromAll.mustache +0 -9
  221. package/docs/webapi/grabBrowserLogs.mustache +0 -9
  222. package/docs/webapi/grabCookie.mustache +0 -11
  223. package/docs/webapi/grabCssPropertyFrom.mustache +0 -11
  224. package/docs/webapi/grabCssPropertyFromAll.mustache +0 -10
  225. package/docs/webapi/grabCurrentUrl.mustache +0 -9
  226. package/docs/webapi/grabCurrentWindowHandle.mustache +0 -6
  227. package/docs/webapi/grabDataFromPerformanceTiming.mustache +0 -20
  228. package/docs/webapi/grabElementBoundingRect.mustache +0 -20
  229. package/docs/webapi/grabGeoLocation.mustache +0 -8
  230. package/docs/webapi/grabHTMLFrom.mustache +0 -10
  231. package/docs/webapi/grabHTMLFromAll.mustache +0 -9
  232. package/docs/webapi/grabNumberOfOpenTabs.mustache +0 -8
  233. package/docs/webapi/grabNumberOfVisibleElements.mustache +0 -9
  234. package/docs/webapi/grabPageScrollPosition.mustache +0 -8
  235. package/docs/webapi/grabPopupText.mustache +0 -5
  236. package/docs/webapi/grabRecordedNetworkTraffics.mustache +0 -10
  237. package/docs/webapi/grabSource.mustache +0 -8
  238. package/docs/webapi/grabTextFrom.mustache +0 -10
  239. package/docs/webapi/grabTextFromAll.mustache +0 -9
  240. package/docs/webapi/grabTitle.mustache +0 -8
  241. package/docs/webapi/grabValueFrom.mustache +0 -9
  242. package/docs/webapi/grabValueFromAll.mustache +0 -8
  243. package/docs/webapi/grabWebElement.mustache +0 -9
  244. package/docs/webapi/grabWebElements.mustache +0 -9
  245. package/docs/webapi/moveCursorTo.mustache +0 -16
  246. package/docs/webapi/openNewTab.mustache +0 -7
  247. package/docs/webapi/pressKey.mustache +0 -12
  248. package/docs/webapi/pressKeyDown.mustache +0 -12
  249. package/docs/webapi/pressKeyUp.mustache +0 -12
  250. package/docs/webapi/pressKeyWithKeyNormalization.mustache +0 -60
  251. package/docs/webapi/refreshPage.mustache +0 -6
  252. package/docs/webapi/resizeWindow.mustache +0 -6
  253. package/docs/webapi/rightClick.mustache +0 -14
  254. package/docs/webapi/saveElementScreenshot.mustache +0 -10
  255. package/docs/webapi/saveScreenshot.mustache +0 -12
  256. package/docs/webapi/say.mustache +0 -10
  257. package/docs/webapi/scrollIntoView.mustache +0 -11
  258. package/docs/webapi/scrollPageToBottom.mustache +0 -6
  259. package/docs/webapi/scrollPageToTop.mustache +0 -6
  260. package/docs/webapi/scrollTo.mustache +0 -12
  261. package/docs/webapi/see.mustache +0 -11
  262. package/docs/webapi/seeAttributesOnElements.mustache +0 -9
  263. package/docs/webapi/seeCheckboxIsChecked.mustache +0 -10
  264. package/docs/webapi/seeCookie.mustache +0 -8
  265. package/docs/webapi/seeCssPropertiesOnElements.mustache +0 -9
  266. package/docs/webapi/seeCurrentPathEquals.mustache +0 -10
  267. package/docs/webapi/seeCurrentUrlEquals.mustache +0 -11
  268. package/docs/webapi/seeElement.mustache +0 -12
  269. package/docs/webapi/seeElementInDOM.mustache +0 -8
  270. package/docs/webapi/seeInCurrentUrl.mustache +0 -8
  271. package/docs/webapi/seeInField.mustache +0 -17
  272. package/docs/webapi/seeInPopup.mustache +0 -8
  273. package/docs/webapi/seeInSource.mustache +0 -7
  274. package/docs/webapi/seeInTitle.mustache +0 -8
  275. package/docs/webapi/seeNumberOfElements.mustache +0 -11
  276. package/docs/webapi/seeNumberOfVisibleElements.mustache +0 -10
  277. package/docs/webapi/seeTextEquals.mustache +0 -9
  278. package/docs/webapi/seeTitleEquals.mustache +0 -8
  279. package/docs/webapi/seeTraffic.mustache +0 -36
  280. package/docs/webapi/selectOption.mustache +0 -26
  281. package/docs/webapi/setCookie.mustache +0 -16
  282. package/docs/webapi/setGeoLocation.mustache +0 -12
  283. package/docs/webapi/startRecordingTraffic.mustache +0 -8
  284. package/docs/webapi/startRecordingWebSocketMessages.mustache +0 -8
  285. package/docs/webapi/stopRecordingTraffic.mustache +0 -5
  286. package/docs/webapi/stopRecordingWebSocketMessages.mustache +0 -7
  287. package/docs/webapi/switchTo.mustache +0 -9
  288. package/docs/webapi/switchToNextTab.mustache +0 -10
  289. package/docs/webapi/switchToPreviousTab.mustache +0 -10
  290. package/docs/webapi/type.mustache +0 -21
  291. package/docs/webapi/uncheckOption.mustache +0 -13
  292. package/docs/webapi/wait.mustache +0 -8
  293. package/docs/webapi/waitForClickable.mustache +0 -11
  294. package/docs/webapi/waitForCookie.mustache +0 -9
  295. package/docs/webapi/waitForDetached.mustache +0 -10
  296. package/docs/webapi/waitForDisabled.mustache +0 -6
  297. package/docs/webapi/waitForElement.mustache +0 -11
  298. package/docs/webapi/waitForEnabled.mustache +0 -6
  299. package/docs/webapi/waitForFunction.mustache +0 -17
  300. package/docs/webapi/waitForInvisible.mustache +0 -10
  301. package/docs/webapi/waitForNumberOfTabs.mustache +0 -9
  302. package/docs/webapi/waitForText.mustache +0 -13
  303. package/docs/webapi/waitForValue.mustache +0 -10
  304. package/docs/webapi/waitForVisible.mustache +0 -10
  305. package/docs/webapi/waitInUrl.mustache +0 -9
  306. package/docs/webapi/waitNumberOfVisibleElements.mustache +0 -10
  307. package/docs/webapi/waitToHide.mustache +0 -10
  308. package/docs/webapi/waitUrlEquals.mustache +0 -10
  309. package/lib/helper/AI.js +0 -214
  310. package/lib/helper/Mochawesome.js +0 -96
  311. package/lib/helper/extras/PlaywrightReactVueLocator.js +0 -52
  312. package/lib/helper/extras/React.js +0 -65
  313. package/lib/plugin/stepByStepReport.js +0 -431
  314. package/lib/plugin/subtitles.js +0 -89
@@ -8,6 +8,7 @@ import promiseRetry from 'promise-retry'
8
8
  import Locator from '../locator.js'
9
9
  import recorder from '../recorder.js'
10
10
  import store from '../store.js'
11
+ import { checkFocusBeforeType, checkFocusBeforePressKey } from './extras/focusCheck.js'
11
12
  import { includes as stringIncludes } from '../assert/include.js'
12
13
  import { urlEquals, equals } from '../assert/equal.js'
13
14
  import { empty } from '../assert/empty.js'
@@ -43,6 +44,8 @@ import { dropFile } from './scripts/dropFile.js'
43
44
  import { dontSeeElementError, seeElementError, dontSeeElementInDOMError, seeElementInDOMError } from './errors/ElementAssertion.js'
44
45
  import { dontSeeTraffic, seeTraffic, grabRecordedNetworkTraffics, stopRecordingTraffic, flushNetworkTraffics } from './network/actions.js'
45
46
  import WebElement from '../element/WebElement.js'
47
+ import { selectElement } from './extras/elementSelection.js'
48
+ import { fillRichEditor } from './extras/richTextEditor.js'
46
49
 
47
50
  let puppeteer
48
51
 
@@ -750,7 +753,7 @@ class Puppeteer extends Helper {
750
753
  }
751
754
 
752
755
  if (this.options.trace) {
753
- const fileName = `${`${global.output_dir}${path.sep}trace${path.sep}${uuidv4()}_${clearString(this.currentRunningTest.title)}`.slice(0, 245)}.json`
756
+ const fileName = `${`${store.outputDir}${path.sep}trace${path.sep}${uuidv4()}_${clearString(this.currentRunningTest.title)}`.slice(0, 245)}.json`
754
757
  const dir = path.dirname(fileName)
755
758
  if (!fileExists(dir)) fs.mkdirSync(dir)
756
759
  await this.page.tracing.start({ screenshots: true, path: fileName })
@@ -818,7 +821,6 @@ class Puppeteer extends Helper {
818
821
 
819
822
  /**
820
823
  * {{> moveCursorTo }}
821
- * {{ react }}
822
824
  */
823
825
  async moveCursorTo(locator, offsetX = 0, offsetY = 0) {
824
826
  let context = null
@@ -989,7 +991,6 @@ class Puppeteer extends Helper {
989
991
  * const elements = await this.helpers['Puppeteer']._locate({name: 'password'});
990
992
  * ```
991
993
  *
992
- * {{ react }}
993
994
  */
994
995
  async _locate(locator) {
995
996
  const context = await this.context
@@ -1004,17 +1005,16 @@ class Puppeteer extends Helper {
1004
1005
  * const element = await this.helpers['Puppeteer']._locateElement({name: 'password'});
1005
1006
  * ```
1006
1007
  *
1007
- * {{ react }}
1008
1008
  */
1009
1009
  async _locateElement(locator) {
1010
1010
  const context = await this.context
1011
- if (this.options.strict) {
1011
+ const elementIndex = store.currentStep?.opts?.elementIndex
1012
+ if (this.options.strict || elementIndex) {
1012
1013
  const elements = await findElements.call(this, context, locator)
1013
1014
  if (elements.length === 0) {
1014
1015
  throw new ElementNotFound(locator, 'Element', 'was not found')
1015
1016
  }
1016
- assertOnlyOneElement(elements, locator, this)
1017
- return elements[0]
1017
+ return selectElement(elements, locator, this)
1018
1018
  }
1019
1019
  return findElement.call(this, context, locator)
1020
1020
  }
@@ -1033,8 +1033,7 @@ class Puppeteer extends Helper {
1033
1033
  if (!els || els.length === 0) {
1034
1034
  throw new ElementNotFound(locator, 'Checkbox or radio')
1035
1035
  }
1036
- if (this.options.strict) assertOnlyOneElement(els, locator, this)
1037
- return els[0]
1036
+ return selectElement(els, locator, this)
1038
1037
  }
1039
1038
 
1040
1039
  /**
@@ -1189,7 +1188,6 @@ class Puppeteer extends Helper {
1189
1188
 
1190
1189
  /**
1191
1190
  * {{> seeElement }}
1192
- * {{ react }}
1193
1191
  */
1194
1192
  async seeElement(locator, context = null) {
1195
1193
  let els
@@ -1213,7 +1211,6 @@ class Puppeteer extends Helper {
1213
1211
 
1214
1212
  /**
1215
1213
  * {{> dontSeeElement }}
1216
- * {{ react }}
1217
1214
  */
1218
1215
  async dontSeeElement(locator, context = null) {
1219
1216
  let els
@@ -1262,7 +1259,6 @@ class Puppeteer extends Helper {
1262
1259
  /**
1263
1260
  * {{> click }}
1264
1261
  *
1265
- * {{ react }}
1266
1262
  */
1267
1263
  async click(locator = '//body', context = null) {
1268
1264
  return proceedClick.call(this, locator, context)
@@ -1271,7 +1267,6 @@ class Puppeteer extends Helper {
1271
1267
  /**
1272
1268
  * {{> forceClick }}
1273
1269
  *
1274
- * {{ react }}
1275
1270
  */
1276
1271
  async forceClick(locator, context = null) {
1277
1272
  let matcher = await this.context
@@ -1301,7 +1296,6 @@ class Puppeteer extends Helper {
1301
1296
  /**
1302
1297
  * {{> clickLink }}
1303
1298
  *
1304
- * {{ react }}
1305
1299
  */
1306
1300
  async clickLink(locator, context = null) {
1307
1301
  return proceedClick.call(this, locator, context, { waitForNavigation: true })
@@ -1325,7 +1319,7 @@ class Puppeteer extends Helper {
1325
1319
  * @param {string} [downloadPath='downloads'] change this parameter to set another directory for saving
1326
1320
  */
1327
1321
  async handleDownloads(downloadPath = 'downloads') {
1328
- downloadPath = path.join(global.output_dir, downloadPath)
1322
+ downloadPath = path.join(store.outputDir, downloadPath)
1329
1323
  if (!fs.existsSync(downloadPath)) {
1330
1324
  fs.mkdirSync(downloadPath, '0777')
1331
1325
  }
@@ -1387,7 +1381,7 @@ class Puppeteer extends Helper {
1387
1381
  },
1388
1382
  })
1389
1383
 
1390
- const outputFile = path.join(`${global.output_dir}/${fileName}`)
1384
+ const outputFile = path.join(`${store.outputDir}/${fileName}`)
1391
1385
 
1392
1386
  try {
1393
1387
  await new Promise((resolve, reject) => {
@@ -1409,7 +1403,6 @@ class Puppeteer extends Helper {
1409
1403
  /**
1410
1404
  * {{> doubleClick }}
1411
1405
  *
1412
- * {{ react }}
1413
1406
  */
1414
1407
  async doubleClick(locator, context = null) {
1415
1408
  return proceedClick.call(this, locator, context, { clickCount: 2 })
@@ -1418,7 +1411,6 @@ class Puppeteer extends Helper {
1418
1411
  /**
1419
1412
  * {{> rightClick }}
1420
1413
  *
1421
- * {{ react }}
1422
1414
  */
1423
1415
  async rightClick(locator, context = null) {
1424
1416
  return proceedClick.call(this, locator, context, { button: 'right' })
@@ -1547,6 +1539,7 @@ class Puppeteer extends Helper {
1547
1539
  * {{> pressKeyWithKeyNormalization }}
1548
1540
  */
1549
1541
  async pressKey(key) {
1542
+ await checkFocusBeforePressKey(this, key)
1550
1543
  const modifiers = []
1551
1544
  if (Array.isArray(key)) {
1552
1545
  for (let k of key) {
@@ -1575,6 +1568,8 @@ class Puppeteer extends Helper {
1575
1568
  * {{> type }}
1576
1569
  */
1577
1570
  async type(keys, delay = null) {
1571
+ await checkFocusBeforeType(this)
1572
+
1578
1573
  if (!Array.isArray(keys)) {
1579
1574
  keys = keys.toString()
1580
1575
  keys = keys.split('')
@@ -1588,13 +1583,20 @@ class Puppeteer extends Helper {
1588
1583
 
1589
1584
  /**
1590
1585
  * {{> fillField }}
1591
- * {{ react }}
1592
1586
  */
1593
1587
  async fillField(field, value, context = null) {
1594
- const els = await findVisibleFields.call(this, field, context)
1588
+ let els = await findVisibleFields.call(this, field, context)
1589
+ if (!els.length) {
1590
+ els = await findFields.call(this, field, context)
1591
+ }
1595
1592
  assertElementExists(els, field, 'Field')
1596
- if (this.options.strict) assertOnlyOneElement(els, field, this)
1597
- const el = els[0]
1593
+ const el = selectElement(els, field, this)
1594
+
1595
+ if (await fillRichEditor(this, el, value)) {
1596
+ highlightActiveElement.call(this, el, await this._getContext())
1597
+ return this._waitForAction()
1598
+ }
1599
+
1598
1600
  const tag = await el.getProperty('tagName').then(el => el.jsonValue())
1599
1601
  const editable = await el.getProperty('contenteditable').then(el => el.jsonValue())
1600
1602
  if (tag === 'INPUT' || tag === 'TEXTAREA') {
@@ -1619,15 +1621,14 @@ class Puppeteer extends Helper {
1619
1621
  /**
1620
1622
  * {{> appendField }}
1621
1623
  *
1622
- * {{ react }}
1623
1624
  */
1624
1625
  async appendField(field, value, context = null) {
1625
1626
  const els = await findVisibleFields.call(this, field, context)
1626
1627
  assertElementExists(els, field, 'Field')
1627
- if (this.options.strict) assertOnlyOneElement(els, field, this)
1628
- highlightActiveElement.call(this, els[0], await this._getContext())
1629
- await els[0].press('End')
1630
- await els[0].type(value.toString(), { delay: this.options.pressKeyDelay })
1628
+ const el = selectElement(els, field, this)
1629
+ highlightActiveElement.call(this, el, await this._getContext())
1630
+ await el.press('End')
1631
+ await el.type(value.toString(), { delay: this.options.pressKeyDelay })
1631
1632
  return this._waitForAction()
1632
1633
  }
1633
1634
 
@@ -1653,29 +1654,31 @@ class Puppeteer extends Helper {
1653
1654
  * {{> attachFile }}
1654
1655
  */
1655
1656
  async attachFile(locator, pathToFile, context = null) {
1656
- const file = path.join(global.codecept_dir, pathToFile)
1657
+ const file = path.join(store.codeceptDir, pathToFile)
1657
1658
 
1658
1659
  if (!fileExists(file)) {
1659
1660
  throw new Error(`File at ${file} can not be found on local system`)
1660
1661
  }
1661
1662
  const els = await findFields.call(this, locator, context)
1662
1663
  if (els.length) {
1663
- const tag = await els[0].evaluate(el => el.tagName)
1664
- const type = await els[0].evaluate(el => el.type)
1664
+ const el = selectElement(els, locator, this)
1665
+ const tag = await el.evaluate(el => el.tagName)
1666
+ const type = await el.evaluate(el => el.type)
1665
1667
  if (tag === 'INPUT' && type === 'file') {
1666
- await els[0].uploadFile(file)
1668
+ await el.uploadFile(file)
1667
1669
  return this._waitForAction()
1668
1670
  }
1669
1671
  }
1670
1672
 
1671
1673
  const targetEls = els.length ? els : await this._locate(locator)
1672
1674
  assertElementExists(targetEls, locator, 'Element')
1675
+ const el = selectElement(targetEls, locator, this)
1673
1676
  const fileData = {
1674
1677
  base64Content: base64EncodeFile(file),
1675
1678
  fileName: path.basename(file),
1676
1679
  mimeType: getMimeType(path.basename(file)),
1677
1680
  }
1678
- await targetEls[0].evaluate(dropFile, fileData)
1681
+ await el.evaluate(dropFile, fileData)
1679
1682
  return this._waitForAction()
1680
1683
  }
1681
1684
 
@@ -1698,28 +1701,27 @@ class Puppeteer extends Helper {
1698
1701
  this.debugSection('SelectOption', `Strict: ${JSON.stringify(select)}`)
1699
1702
  const els = contextEl ? await findElements.call(this, contextEl, select) : await this._locate(select)
1700
1703
  assertElementExists(els, select, 'Selectable element')
1701
- return proceedSelect.call(this, pageContext, els[0], option)
1704
+ return proceedSelect.call(this, pageContext, selectElement(els, select, this), option)
1702
1705
  }
1703
1706
 
1704
1707
  // Fuzzy: try combobox
1705
1708
  this.debugSection('SelectOption', `Fuzzy: "${matchedLocator.value}"`)
1706
1709
  const comboboxSearchCtx = contextEl || pageContext
1707
1710
  let els = await findByRole(comboboxSearchCtx, { role: 'combobox', name: matchedLocator.value })
1708
- if (els?.length) return proceedSelect.call(this, pageContext, els[0], option)
1711
+ if (els?.length) return proceedSelect.call(this, pageContext, selectElement(els, select, this), option)
1709
1712
 
1710
1713
  // Fuzzy: try listbox
1711
1714
  els = await findByRole(comboboxSearchCtx, { role: 'listbox', name: matchedLocator.value })
1712
- if (els?.length) return proceedSelect.call(this, pageContext, els[0], option)
1715
+ if (els?.length) return proceedSelect.call(this, pageContext, selectElement(els, select, this), option)
1713
1716
 
1714
1717
  // Fuzzy: try native select
1715
1718
  const visibleEls = await findVisibleFields.call(this, select, context)
1716
1719
  assertElementExists(visibleEls, select, 'Selectable field')
1717
- return proceedSelect.call(this, pageContext, visibleEls[0], option)
1720
+ return proceedSelect.call(this, pageContext, selectElement(visibleEls, select, this), option)
1718
1721
  }
1719
1722
 
1720
1723
  /**
1721
1724
  * {{> grabNumberOfVisibleElements }}
1722
- * {{ react }}
1723
1725
  */
1724
1726
  async grabNumberOfVisibleElements(locator) {
1725
1727
  let els = await this._locate(locator)
@@ -1781,7 +1783,6 @@ class Puppeteer extends Helper {
1781
1783
  /**
1782
1784
  * {{> see }}
1783
1785
  *
1784
- * {{ react }}
1785
1786
  */
1786
1787
  async see(text, context = null) {
1787
1788
  return proceedSee.call(this, 'assert', text, context)
@@ -1797,7 +1798,6 @@ class Puppeteer extends Helper {
1797
1798
  /**
1798
1799
  * {{> dontSee }}
1799
1800
  *
1800
- * {{ react }}
1801
1801
  */
1802
1802
  async dontSee(text, context = null) {
1803
1803
  return proceedSee.call(this, 'negate', text, context)
@@ -1851,7 +1851,6 @@ class Puppeteer extends Helper {
1851
1851
  /**
1852
1852
  * {{> seeNumberOfElements }}
1853
1853
  *
1854
- * {{ react }}
1855
1854
  */
1856
1855
  async seeNumberOfElements(locator, num) {
1857
1856
  const elements = await this._locate(locator)
@@ -1861,7 +1860,6 @@ class Puppeteer extends Helper {
1861
1860
  /**
1862
1861
  * {{> seeNumberOfVisibleElements }}
1863
1862
  *
1864
- * {{ react }}
1865
1863
  */
1866
1864
  async seeNumberOfVisibleElements(locator, num) {
1867
1865
  const res = await this.grabNumberOfVisibleElements(locator)
@@ -1988,7 +1986,6 @@ class Puppeteer extends Helper {
1988
1986
 
1989
1987
  /**
1990
1988
  * {{> grabTextFromAll }}
1991
- * {{ react }}
1992
1989
  */
1993
1990
  async grabTextFromAll(locator) {
1994
1991
  const els = await this._locate(locator)
@@ -2001,7 +1998,6 @@ class Puppeteer extends Helper {
2001
1998
 
2002
1999
  /**
2003
2000
  * {{> grabTextFrom }}
2004
- * {{ react }}
2005
2001
  */
2006
2002
  async grabTextFrom(locator) {
2007
2003
  const texts = await this.grabTextFromAll(locator)
@@ -2062,7 +2058,6 @@ class Puppeteer extends Helper {
2062
2058
 
2063
2059
  /**
2064
2060
  * {{> grabCssPropertyFromAll }}
2065
- * {{ react }}
2066
2061
  */
2067
2062
  async grabCssPropertyFromAll(locator, cssProperty) {
2068
2063
  const els = await this._locate(locator)
@@ -2074,7 +2069,6 @@ class Puppeteer extends Helper {
2074
2069
 
2075
2070
  /**
2076
2071
  * {{> grabCssPropertyFrom }}
2077
- * {{ react }}
2078
2072
  */
2079
2073
  async grabCssPropertyFrom(locator, cssProperty) {
2080
2074
  const cssValues = await this.grabCssPropertyFromAll(locator, cssProperty)
@@ -2089,7 +2083,6 @@ class Puppeteer extends Helper {
2089
2083
 
2090
2084
  /**
2091
2085
  * {{> seeCssPropertiesOnElements }}
2092
- * {{ react }}
2093
2086
  */
2094
2087
  async seeCssPropertiesOnElements(locator, cssProperties) {
2095
2088
  const res = await this._locate(locator)
@@ -2124,7 +2117,6 @@ class Puppeteer extends Helper {
2124
2117
 
2125
2118
  /**
2126
2119
  * {{> seeAttributesOnElements }}
2127
- * {{ react }}
2128
2120
  */
2129
2121
  async seeAttributesOnElements(locator, attributes) {
2130
2122
  const elements = await this._locate(locator)
@@ -2162,7 +2154,6 @@ class Puppeteer extends Helper {
2162
2154
 
2163
2155
  /**
2164
2156
  * {{> dragSlider }}
2165
- * {{ react }}
2166
2157
  */
2167
2158
  async dragSlider(locator, offsetX = 0) {
2168
2159
  const src = await this._locate(locator)
@@ -2184,7 +2175,6 @@ class Puppeteer extends Helper {
2184
2175
 
2185
2176
  /**
2186
2177
  * {{> grabAttributeFromAll }}
2187
- * {{ react }}
2188
2178
  */
2189
2179
  async grabAttributeFromAll(locator, attr) {
2190
2180
  const els = await this._locate(locator)
@@ -2198,7 +2188,6 @@ class Puppeteer extends Helper {
2198
2188
 
2199
2189
  /**
2200
2190
  * {{> grabAttributeFrom }}
2201
- * {{ react }}
2202
2191
  */
2203
2192
  async grabAttributeFrom(locator, attr) {
2204
2193
  const attrs = await this.grabAttributeFromAll(locator, attr)
@@ -2361,7 +2350,6 @@ class Puppeteer extends Helper {
2361
2350
 
2362
2351
  /**
2363
2352
  * {{> waitNumberOfVisibleElements }}
2364
- * {{ react }}
2365
2353
  */
2366
2354
  async waitNumberOfVisibleElements(locator, num, sec) {
2367
2355
  const waitTimeout = sec ? sec * 1000 : this.options.waitForTimeout
@@ -2409,7 +2397,6 @@ class Puppeteer extends Helper {
2409
2397
 
2410
2398
  /**
2411
2399
  * {{> waitForElement }}
2412
- * {{ react }}
2413
2400
  */
2414
2401
  async waitForElement(locator, sec) {
2415
2402
  const waitTimeout = sec ? sec * 1000 : this.options.waitForTimeout
@@ -2430,7 +2417,6 @@ class Puppeteer extends Helper {
2430
2417
  /**
2431
2418
  * {{> waitForVisible }}
2432
2419
  *
2433
- * {{ react }}
2434
2420
  */
2435
2421
  async waitForVisible(locator, sec) {
2436
2422
  const waitTimeout = sec ? sec * 1000 : this.options.waitForTimeout
@@ -3007,10 +2993,6 @@ export default Puppeteer
3007
2993
  * @returns {Promise<Array>} Array of ElementHandle objects
3008
2994
  */
3009
2995
  async function findElements(matcher, locator) {
3010
- // Check if locator is a Locator object with react type, or a raw object with react property
3011
- const isReactLocator = locator.type === 'react' || (locator.locator && locator.locator.react) || locator.react
3012
- if (isReactLocator) return findReactElements.call(this, locator)
3013
-
3014
2996
  locator = new Locator(locator, 'css')
3015
2997
 
3016
2998
  // Check if locator is a role locator and call findByRole
@@ -3077,7 +3059,6 @@ async function findElements(matcher, locator) {
3077
3059
  * @returns {Promise<Object>} Single ElementHandle object
3078
3060
  */
3079
3061
  async function findElement(matcher, locator) {
3080
- if (locator.react) return findReactElements.call(this, locator)
3081
3062
  locator = new Locator(locator, 'css')
3082
3063
 
3083
3064
  // Check if locator is a role locator and call findByRole
@@ -3121,11 +3102,11 @@ async function proceedClick(locator, context = null, options = {}) {
3121
3102
  } else {
3122
3103
  assertElementExists(els, locator, 'Clickable element')
3123
3104
  }
3124
- if (this.options.strict) assertOnlyOneElement(els, locator, this)
3105
+ const el = selectElement(els, locator, this)
3125
3106
 
3126
- highlightActiveElement.call(this, els[0], await this._getContext())
3107
+ highlightActiveElement.call(this, el, await this._getContext())
3127
3108
 
3128
- await els[0].click(options)
3109
+ await el.click(options)
3129
3110
  const promises = []
3130
3111
  if (options.waitForNavigation) {
3131
3112
  promises.push(this.waitForNavigation())
@@ -3563,7 +3544,7 @@ function getNormalizedKey(key) {
3563
3544
  }
3564
3545
 
3565
3546
  function highlightActiveElement(element, context) {
3566
- if (this.options.highlightElement && global.debugMode) {
3547
+ if (this.options.highlightElement && store.debugMode) {
3567
3548
  highlightElement(element, context)
3568
3549
  }
3569
3550
  }
@@ -3576,75 +3557,6 @@ function _waitForElement(locator, options) {
3576
3557
  }
3577
3558
  }
3578
3559
 
3579
- async function findReactElements(locator) {
3580
- // Handle both Locator objects and raw locator objects
3581
- const resolved = locator.locator ? locator.locator : toLocatorConfig(locator, 'react')
3582
- this.debug(`Finding React elements: ${JSON.stringify(resolved)}`)
3583
-
3584
- // Use createRequire to access require.resolve in ESM
3585
- const { createRequire } = await import('module')
3586
- const require = createRequire(import.meta.url)
3587
- const resqScript = await fs.promises.readFile(require.resolve('resq'), 'utf-8')
3588
- await this.page.evaluate(resqScript.toString())
3589
-
3590
- await this.page.evaluate(() => window.resq.waitToLoadReact())
3591
- const arrayHandle = await this.page.evaluateHandle(
3592
- obj => {
3593
- const { selector, props, state } = obj
3594
- let elements = window.resq.resq$$(selector)
3595
- if (Object.keys(props).length) {
3596
- elements = elements.byProps(props)
3597
- }
3598
- if (Object.keys(state).length) {
3599
- elements = elements.byState(state)
3600
- }
3601
-
3602
- if (!elements.length) {
3603
- return []
3604
- }
3605
-
3606
- // resq returns an array of HTMLElements if the React component is a fragment
3607
- // this avoids having nested arrays of nodes which the driver does not understand
3608
- // [[div, div], [div, div]] => [div, div, div, div]
3609
- let nodes = []
3610
-
3611
- elements.forEach(element => {
3612
- let { node, isFragment } = element
3613
-
3614
- if (!node) {
3615
- isFragment = true
3616
- node = element.children
3617
- }
3618
-
3619
- if (isFragment) {
3620
- nodes = nodes.concat(node)
3621
- } else {
3622
- nodes.push(node)
3623
- }
3624
- })
3625
-
3626
- return [...nodes]
3627
- },
3628
- {
3629
- selector: resolved.react,
3630
- props: resolved.props || {},
3631
- state: resolved.state || {},
3632
- },
3633
- )
3634
-
3635
- const properties = await arrayHandle.getProperties()
3636
- const result = []
3637
- for (const property of properties.values()) {
3638
- const elementHandle = property.asElement()
3639
- if (elementHandle) {
3640
- result.push(elementHandle)
3641
- }
3642
- }
3643
-
3644
- await arrayHandle.dispose()
3645
- return result
3646
- }
3647
-
3648
3560
  async function findByRole(matcher, locator) {
3649
3561
  const resolved = toLocatorConfig(locator, 'role')
3650
3562
  const roleSelector = buildRoleSelector(resolved)