codeceptjs 4.0.2-beta.8 → 4.0.2

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 (326) hide show
  1. package/README.md +39 -28
  2. package/bin/codecept.js +15 -2
  3. package/bin/codeceptq.js +49 -0
  4. package/bin/mcp-server.js +1189 -0
  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 +745 -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 +195 -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 +34 -25
  103. package/lib/command/check.js +2 -1
  104. package/lib/command/definitions.js +6 -7
  105. package/lib/command/dryRun.js +24 -5
  106. package/lib/command/generate.js +3 -1
  107. package/lib/command/gherkin/snippets.js +5 -4
  108. package/lib/command/init.js +249 -270
  109. package/lib/command/list.js +150 -10
  110. package/lib/command/query.js +218 -0
  111. package/lib/command/run-multiple.js +3 -1
  112. package/lib/command/run-workers.js +2 -14
  113. package/lib/command/run.js +3 -17
  114. package/lib/command/utils.js +14 -0
  115. package/lib/command/workers/runTests.js +91 -37
  116. package/lib/config.js +96 -18
  117. package/lib/container.js +115 -17
  118. package/lib/effects.js +17 -0
  119. package/lib/element/WebElement.js +246 -2
  120. package/lib/els.js +12 -6
  121. package/lib/globals.js +32 -19
  122. package/lib/heal.js +7 -4
  123. package/lib/helper/ApiDataFactory.js +2 -1
  124. package/lib/helper/Appium.js +8 -8
  125. package/lib/helper/FileSystem.js +3 -2
  126. package/lib/helper/GraphQLDataFactory.js +2 -1
  127. package/lib/helper/Playwright.js +358 -467
  128. package/lib/helper/Puppeteer.js +335 -192
  129. package/lib/helper/WebDriver.js +324 -111
  130. package/lib/helper/errors/ElementNotFound.js +5 -2
  131. package/lib/helper/errors/MultipleElementsFound.js +52 -0
  132. package/lib/helper/errors/NonFocusedType.js +8 -0
  133. package/lib/helper/extras/Download.js +45 -0
  134. package/lib/helper/extras/PlaywrightLocator.js +7 -107
  135. package/lib/helper/extras/elementSelection.js +58 -0
  136. package/lib/helper/extras/focusCheck.js +43 -0
  137. package/lib/helper/extras/richTextEditor.js +178 -0
  138. package/lib/helper/scripts/dropFile.js +11 -0
  139. package/lib/history.js +3 -2
  140. package/lib/html.js +103 -16
  141. package/lib/index.js +9 -1
  142. package/lib/listener/config.js +6 -4
  143. package/lib/listener/emptyRun.js +2 -1
  144. package/lib/listener/globalRetry.js +32 -6
  145. package/lib/listener/helpers.js +4 -1
  146. package/lib/listener/mocha.js +2 -1
  147. package/lib/listener/pageobjects.js +43 -0
  148. package/lib/listener/result.js +3 -2
  149. package/lib/locator.js +158 -16
  150. package/lib/mocha/cli.js +19 -1
  151. package/lib/mocha/factory.js +11 -1
  152. package/lib/mocha/inject.js +1 -1
  153. package/lib/mocha/scenarioConfig.js +2 -1
  154. package/lib/mocha/ui.js +5 -6
  155. package/lib/parser.js +2 -2
  156. package/lib/pause.js +38 -4
  157. package/lib/plugin/aiTrace.js +457 -0
  158. package/lib/plugin/analyze.js +9 -9
  159. package/lib/plugin/auth.js +5 -4
  160. package/lib/plugin/browser.js +77 -0
  161. package/lib/plugin/expose.js +159 -0
  162. package/lib/plugin/heal.js +47 -3
  163. package/lib/plugin/junitReporter.js +303 -0
  164. package/lib/plugin/pageInfo.js +54 -52
  165. package/lib/plugin/pause.js +131 -0
  166. package/lib/plugin/pauseOnFail.js +11 -33
  167. package/lib/plugin/retryFailedStep.js +43 -32
  168. package/lib/plugin/screencast.js +289 -0
  169. package/lib/plugin/screenshot.js +558 -0
  170. package/lib/plugin/screenshotOnFail.js +9 -170
  171. package/lib/plugin/stepTimeout.js +3 -2
  172. package/lib/recorder.js +1 -1
  173. package/lib/rerun.js +2 -1
  174. package/lib/result.js +2 -1
  175. package/lib/step/base.js +10 -9
  176. package/lib/step/comment.js +2 -2
  177. package/lib/step/config.js +15 -2
  178. package/lib/step/helper.js +4 -4
  179. package/lib/step/meta.js +3 -3
  180. package/lib/step/record.js +5 -5
  181. package/lib/store.js +72 -3
  182. package/lib/translation.js +2 -1
  183. package/lib/utils/loaderCheck.js +28 -0
  184. package/lib/utils/mask_data.js +2 -1
  185. package/lib/utils/pluginParser.js +151 -0
  186. package/lib/utils/trace.js +297 -0
  187. package/lib/utils/typescript.js +188 -23
  188. package/lib/utils.js +77 -3
  189. package/lib/workers.js +65 -40
  190. package/package.json +35 -30
  191. package/typings/index.d.ts +119 -8
  192. package/typings/promiseBasedTypes.d.ts +3158 -6065
  193. package/typings/types.d.ts +3453 -6494
  194. package/docs/webapi/amOnPage.mustache +0 -11
  195. package/docs/webapi/appendField.mustache +0 -11
  196. package/docs/webapi/attachFile.mustache +0 -12
  197. package/docs/webapi/blur.mustache +0 -18
  198. package/docs/webapi/checkOption.mustache +0 -13
  199. package/docs/webapi/clearCookie.mustache +0 -9
  200. package/docs/webapi/clearField.mustache +0 -9
  201. package/docs/webapi/click.mustache +0 -29
  202. package/docs/webapi/clickLink.mustache +0 -8
  203. package/docs/webapi/closeCurrentTab.mustache +0 -7
  204. package/docs/webapi/closeOtherTabs.mustache +0 -8
  205. package/docs/webapi/dontSee.mustache +0 -11
  206. package/docs/webapi/dontSeeCheckboxIsChecked.mustache +0 -10
  207. package/docs/webapi/dontSeeCookie.mustache +0 -8
  208. package/docs/webapi/dontSeeCurrentUrlEquals.mustache +0 -10
  209. package/docs/webapi/dontSeeElement.mustache +0 -8
  210. package/docs/webapi/dontSeeElementInDOM.mustache +0 -8
  211. package/docs/webapi/dontSeeInCurrentUrl.mustache +0 -4
  212. package/docs/webapi/dontSeeInField.mustache +0 -11
  213. package/docs/webapi/dontSeeInSource.mustache +0 -8
  214. package/docs/webapi/dontSeeInTitle.mustache +0 -8
  215. package/docs/webapi/dontSeeTraffic.mustache +0 -13
  216. package/docs/webapi/doubleClick.mustache +0 -13
  217. package/docs/webapi/downloadFile.mustache +0 -12
  218. package/docs/webapi/dragAndDrop.mustache +0 -9
  219. package/docs/webapi/dragSlider.mustache +0 -11
  220. package/docs/webapi/executeAsyncScript.mustache +0 -24
  221. package/docs/webapi/executeScript.mustache +0 -26
  222. package/docs/webapi/fillField.mustache +0 -16
  223. package/docs/webapi/flushNetworkTraffics.mustache +0 -5
  224. package/docs/webapi/focus.mustache +0 -13
  225. package/docs/webapi/forceClick.mustache +0 -28
  226. package/docs/webapi/forceRightClick.mustache +0 -18
  227. package/docs/webapi/grabAllWindowHandles.mustache +0 -7
  228. package/docs/webapi/grabAttributeFrom.mustache +0 -10
  229. package/docs/webapi/grabAttributeFromAll.mustache +0 -9
  230. package/docs/webapi/grabBrowserLogs.mustache +0 -9
  231. package/docs/webapi/grabCookie.mustache +0 -11
  232. package/docs/webapi/grabCssPropertyFrom.mustache +0 -11
  233. package/docs/webapi/grabCssPropertyFromAll.mustache +0 -10
  234. package/docs/webapi/grabCurrentUrl.mustache +0 -9
  235. package/docs/webapi/grabCurrentWindowHandle.mustache +0 -6
  236. package/docs/webapi/grabDataFromPerformanceTiming.mustache +0 -20
  237. package/docs/webapi/grabElementBoundingRect.mustache +0 -20
  238. package/docs/webapi/grabGeoLocation.mustache +0 -8
  239. package/docs/webapi/grabHTMLFrom.mustache +0 -10
  240. package/docs/webapi/grabHTMLFromAll.mustache +0 -9
  241. package/docs/webapi/grabNumberOfOpenTabs.mustache +0 -8
  242. package/docs/webapi/grabNumberOfVisibleElements.mustache +0 -9
  243. package/docs/webapi/grabPageScrollPosition.mustache +0 -8
  244. package/docs/webapi/grabPopupText.mustache +0 -5
  245. package/docs/webapi/grabRecordedNetworkTraffics.mustache +0 -10
  246. package/docs/webapi/grabSource.mustache +0 -8
  247. package/docs/webapi/grabTextFrom.mustache +0 -10
  248. package/docs/webapi/grabTextFromAll.mustache +0 -9
  249. package/docs/webapi/grabTitle.mustache +0 -8
  250. package/docs/webapi/grabValueFrom.mustache +0 -9
  251. package/docs/webapi/grabValueFromAll.mustache +0 -8
  252. package/docs/webapi/grabWebElement.mustache +0 -9
  253. package/docs/webapi/grabWebElements.mustache +0 -9
  254. package/docs/webapi/moveCursorTo.mustache +0 -12
  255. package/docs/webapi/openNewTab.mustache +0 -7
  256. package/docs/webapi/pressKey.mustache +0 -12
  257. package/docs/webapi/pressKeyDown.mustache +0 -12
  258. package/docs/webapi/pressKeyUp.mustache +0 -12
  259. package/docs/webapi/pressKeyWithKeyNormalization.mustache +0 -60
  260. package/docs/webapi/refreshPage.mustache +0 -6
  261. package/docs/webapi/resizeWindow.mustache +0 -6
  262. package/docs/webapi/rightClick.mustache +0 -14
  263. package/docs/webapi/saveElementScreenshot.mustache +0 -10
  264. package/docs/webapi/saveScreenshot.mustache +0 -12
  265. package/docs/webapi/say.mustache +0 -10
  266. package/docs/webapi/scrollIntoView.mustache +0 -11
  267. package/docs/webapi/scrollPageToBottom.mustache +0 -6
  268. package/docs/webapi/scrollPageToTop.mustache +0 -6
  269. package/docs/webapi/scrollTo.mustache +0 -12
  270. package/docs/webapi/see.mustache +0 -11
  271. package/docs/webapi/seeAttributesOnElements.mustache +0 -9
  272. package/docs/webapi/seeCheckboxIsChecked.mustache +0 -10
  273. package/docs/webapi/seeCookie.mustache +0 -8
  274. package/docs/webapi/seeCssPropertiesOnElements.mustache +0 -9
  275. package/docs/webapi/seeCurrentUrlEquals.mustache +0 -11
  276. package/docs/webapi/seeElement.mustache +0 -8
  277. package/docs/webapi/seeElementInDOM.mustache +0 -8
  278. package/docs/webapi/seeInCurrentUrl.mustache +0 -8
  279. package/docs/webapi/seeInField.mustache +0 -12
  280. package/docs/webapi/seeInPopup.mustache +0 -8
  281. package/docs/webapi/seeInSource.mustache +0 -7
  282. package/docs/webapi/seeInTitle.mustache +0 -8
  283. package/docs/webapi/seeNumberOfElements.mustache +0 -11
  284. package/docs/webapi/seeNumberOfVisibleElements.mustache +0 -10
  285. package/docs/webapi/seeTextEquals.mustache +0 -9
  286. package/docs/webapi/seeTitleEquals.mustache +0 -8
  287. package/docs/webapi/seeTraffic.mustache +0 -36
  288. package/docs/webapi/selectOption.mustache +0 -21
  289. package/docs/webapi/setCookie.mustache +0 -16
  290. package/docs/webapi/setGeoLocation.mustache +0 -12
  291. package/docs/webapi/startRecordingTraffic.mustache +0 -8
  292. package/docs/webapi/startRecordingWebSocketMessages.mustache +0 -8
  293. package/docs/webapi/stopRecordingTraffic.mustache +0 -5
  294. package/docs/webapi/stopRecordingWebSocketMessages.mustache +0 -7
  295. package/docs/webapi/switchTo.mustache +0 -9
  296. package/docs/webapi/switchToNextTab.mustache +0 -10
  297. package/docs/webapi/switchToPreviousTab.mustache +0 -10
  298. package/docs/webapi/type.mustache +0 -21
  299. package/docs/webapi/uncheckOption.mustache +0 -13
  300. package/docs/webapi/wait.mustache +0 -8
  301. package/docs/webapi/waitForClickable.mustache +0 -11
  302. package/docs/webapi/waitForCookie.mustache +0 -9
  303. package/docs/webapi/waitForDetached.mustache +0 -10
  304. package/docs/webapi/waitForDisabled.mustache +0 -6
  305. package/docs/webapi/waitForElement.mustache +0 -11
  306. package/docs/webapi/waitForEnabled.mustache +0 -6
  307. package/docs/webapi/waitForFunction.mustache +0 -17
  308. package/docs/webapi/waitForInvisible.mustache +0 -10
  309. package/docs/webapi/waitForNumberOfTabs.mustache +0 -9
  310. package/docs/webapi/waitForText.mustache +0 -13
  311. package/docs/webapi/waitForValue.mustache +0 -10
  312. package/docs/webapi/waitForVisible.mustache +0 -10
  313. package/docs/webapi/waitInUrl.mustache +0 -9
  314. package/docs/webapi/waitNumberOfVisibleElements.mustache +0 -10
  315. package/docs/webapi/waitToHide.mustache +0 -10
  316. package/docs/webapi/waitUrlEquals.mustache +0 -10
  317. package/lib/helper/AI.js +0 -214
  318. package/lib/helper/Mochawesome.js +0 -96
  319. package/lib/helper/extras/PlaywrightReactVueLocator.js +0 -52
  320. package/lib/helper/extras/React.js +0 -65
  321. package/lib/listener/enhancedGlobalRetry.js +0 -110
  322. package/lib/plugin/enhancedRetryFailedStep.js +0 -99
  323. package/lib/plugin/htmlReporter.js +0 -3648
  324. package/lib/plugin/stepByStepReport.js +0 -427
  325. package/lib/plugin/subtitles.js +0 -89
  326. package/lib/retryCoordinator.js +0 -207
package/docs/heal.md ADDED
@@ -0,0 +1,213 @@
1
+ # Self-Healing Tests
2
+
3
+ Browser and Mobile tests can fail for vareity of reasons. However, on a big projects there are about 5-10 causes of flaky tests. The more you work and understand your end-to-end tests the more you learn patterns of failure. And after the research you understand how a test could have been fixed: to reload a page, to click that button once again, restart API request. If by looking into a failure you understand what, as a user, you would do to fix that error, then maybe you could teach your tests to heal themselves.
4
+
5
+ ## What is Healing
6
+
7
+ **Healing defines the way how a test reacts to failure**. You can define multiple healing recipes that could take all needed information: error message, failed test, step, page URL, HTML, etc. A healing recipe can perform some action to fix the failing test on the fly and continue its execution.
8
+
9
+ ![](/img/healing.png)
10
+
11
+ Let's start with a common scenario. If a user suddenly becomes unauthorized and is moved to a sign-in page, or receives an unauthorized message on the page, we can heal by navigating to the `/login` page and trying to enter credentials again.
12
+
13
+ ```js
14
+ heal.addRecipe('loginOnUnauthorized', {
15
+ priority: 10,
16
+ steps: ['click', 'see', 'amOnPage'],
17
+ prepare: {
18
+ url: ({ I }) => I.grabCurrentUrl(),
19
+ html: ({ I }) => I.grabHTMLFrom('body'),
20
+ },
21
+ fn: async ({ url, error, step, html }) => {
22
+ if (!url.includes('/login') && !error.message.toLowerCase().includes('unauthorized') && !html.toLowerCase().includes('unauthorized')) return;
23
+
24
+ return ({ I }) => {
25
+ I.amOnPage('/login');
26
+ I.fillField('Email', 'test@example.com');
27
+ I.fillField('Password', '123456');
28
+ I.click('Sign in');
29
+ I[step.name](...step.args);
30
+ };
31
+ },
32
+ });
33
+ ```
34
+
35
+ Another example is a very basic healing recipe. If after a click test has failed, try to reload page, and continue.
36
+
37
+ ```js
38
+ heal.addRecipe('reload', {
39
+ priority: 10,
40
+ steps: ['click'],
41
+ fn: async ({ step }) => {
42
+ return ({ I }) => {
43
+ I.refreshPage();
44
+ I[step.name](...step.args);
45
+ };
46
+ },
47
+ });
48
+ ```
49
+
50
+ Sure, this won't always work and probably won't be useful on every project. But let's follow the idea: if a click has failed, probably the button is not on a page, maybe it is an issue of rendering, maybe some other element overlapped our button, so if we try to reload page we can continue test execution. At least, this is what manual QA would do if they will run the following test in a browser. They will try to reload a page before reporting "it has failed".
51
+
52
+ So if it is a long end-2-end test that implements user journey, it is more valuable to continue its execution when possible, then fixing a minor issues like overlapping elements. Healing like this can improve the stability of a test.
53
+
54
+ The example above is only one way a test can be healed. But you can define as many heal recipes as you like. What heal recipe would be effective in your case is depends on a system you test, so **there are no pre-defined heal recipes**.
55
+
56
+ ## Healing Patterns
57
+
58
+ There are some ideas where healing can be useful to you:
59
+
60
+ * **Authorization**. If a user suddenly becomes unauthorized and is moved to a sign-in page, or receives an unauthorized message on the page, we can heal by navigating to the `/login` page and trying to enter credentials.
61
+ * **Networking**. If a test depends on a remote resource, and fails because this resource is not available, you may try to send API request to restore that resource before throwing an error.
62
+ * **Data Consistency**. A test may fail because you noticed the data glitch in a system. Instead of failing a test you may try to clean up the data and try again to proceed.
63
+ * **UI Change**. If there is a planned UI migration of a component, for instance Button was changed to Dropdown. You can prepare test so if it fails clicking Button it can try to do so with Dropdown.
64
+ * **Do it again**. If you know, that going one step back and trying to do same actions may solve the issue, you can do so from healers. For instance, a modal didn't render correctly, so you can close it and try to click to open it again.
65
+
66
+ ## Healing vs Retries
67
+
68
+ Unlike retries heal recipes has following benefits:
69
+
70
+ * Heal recipes are **declarative**, they are not added directly into into the test code. This keeps test clean and scenario-focused,
71
+ * Retry can only re-run failed step(s), but heal recipe can **perform wide set of actions**
72
+ * Heal recipe **can react to any step of any test**. So if you catch a common error and you can heal it, you won't need to guess where it can be thrown.
73
+
74
+ ## How to Start Healing
75
+
76
+ To enable healing, you need to define healing recipes and enable heal plugin.
77
+
78
+ Create basic healing recipes using this command:
79
+
80
+ ```
81
+ npx codeceptjs generate:heal
82
+ ```
83
+
84
+ or
85
+
86
+ ```
87
+ npx codeceptjs gr
88
+ ```
89
+
90
+ this will generate `recipes.js` (or `recipes.ts`) in the root directory. Provided default recipe include [AI healing](#ai-healing) and `clickAndType` recipe that replaces `fillField` with `click`+`type`. Use them as examples to write your own heal recipes that will fit for application you are testing.
91
+
92
+ Require `recipes` file and add `heal` plugin to `codecept.conf` file:
93
+
94
+ ```js
95
+
96
+ import './heal';
97
+
98
+ export const config = {
99
+ // ...
100
+ plugins: {
101
+ heal: {
102
+ enabled: true
103
+ }
104
+ }
105
+ }
106
+ ```
107
+
108
+ > Please note that, healing has no sense while developing tests, so it won't work in `--debug` mode.
109
+
110
+ ## Writing Recipes
111
+
112
+ Custom heal recipes can be added by running `heal.addRecipe()` function. By default it should be added to `recipes.js` (or `recipes.ts`) file.
113
+
114
+ Let's see what recipe consist of:
115
+
116
+ ```js
117
+ heal.addRecipe('reloadPageOnUserAccount', {
118
+ // recipe priority
119
+ // which recipe should be tried first
120
+ priority: 10,
121
+
122
+ // an array of steps which may cause the error
123
+ // after which a recipe should be activate
124
+ steps: [
125
+ 'click',
126
+ ],
127
+
128
+ // if you need some additional information like URL of a page,
129
+ // or its HTML, you can add this context to healing function by
130
+ // defining `prepare` list of variable
131
+ prepare: {
132
+ url: ({ I }) => I.grabCurrentUrl(),
133
+ html: ({ I }) => I.grabHTMLFrom('body'),
134
+ // don't add variables that you won't use inside the recipe
135
+ },
136
+
137
+ // probably we want to execute recipes only on some tests
138
+ // so you can set a string or regex which will check if a test title matches the name
139
+ // in this case we execute recipe only on tests that have "@flaky" in their name
140
+ grep: '@flaky',
141
+
142
+ // function to launch healing process
143
+ fn: async ({
144
+ // standard context variables
145
+ step, test, error, prevSteps,
146
+
147
+ // variables coming from prepare function
148
+ html, url,
149
+
150
+ }) => {
151
+ const stepArgs = step.args;
152
+
153
+ // at this point we can decide, should we provide a healing recipe or not
154
+ // for instance, if URL is not the one we can heal at, we should not provide any recipes
155
+ if (!url.includes('/user/acccount')) return;
156
+
157
+ // otherwise we return a function that will be executed
158
+ return ({ I }) => {
159
+ // this is a very basic example action
160
+ // probably you should do something more sophisticated
161
+ // to heal the test
162
+ I.reloadPage();
163
+ I.wait(1);
164
+ I[step.name](...stepArgs);
165
+ };
166
+ },
167
+ });
168
+ ```
169
+
170
+ Let's briefly sum up the properties of a recipe:
171
+
172
+ * `grep` - selects tests by their name to apply heal to
173
+ * `steps` - defines on which steps a recipe should react
174
+ * `priority` - sets the order of recipes being applied
175
+ * `prepare` - declare variables from a context, which can be used for healing
176
+ * `fn` - a function to be applied for healing. It takes all context params: `test`, `step`, `error`, `prevSteps` and returns return either a function or a markdown text with recipes (used by AI healers). If no recipes match the context should not return anything;
177
+
178
+
179
+ ## AI Healing
180
+
181
+ AI can be used to heal failed tests. Large Language Models can analyze HTML of a failed test and provide a suggestion what actions should be performed instead. This can be helpful when running tests on CI as AI can make basic decisions to stabilize failing tests.
182
+
183
+ > Use **OpenAI, Azure OpenAI, Claude**, or any of other LLM that can take a prompt, analyze request and provide valid JS code which can be executed by CodeceptJS as a healing suggestion.
184
+
185
+ AI healing recipe is created within `recipes.js` file:
186
+
187
+ ```js
188
+ heal.addRecipe('ai', {
189
+ priority: 10,
190
+ prepare: {
191
+ html: ({ I }) => I.grabHTMLFrom('body'),
192
+ },
193
+ steps: [
194
+ 'click',
195
+ 'fillField',
196
+ 'appendField',
197
+ 'selectOption',
198
+ 'attachFile',
199
+ 'checkOption',
200
+ 'uncheckOption',
201
+ 'doubleClick',
202
+ ],
203
+ fn: async (args) => {
204
+ return ai.healFailedStep(args);
205
+ },
206
+ });
207
+ ```
208
+
209
+ As you use, it will be activated on failed steps and will use HTML of a page as additional information. The prompt, error, and the HTML will be sent to AI provider you configured.
210
+
211
+ Learn more how you can [configure AI provider](./ai).
212
+
213
+ To activate the AI healer don't forget to run tests with `--ai` flag.
@@ -0,0 +1,267 @@
1
+ ---
2
+ permalink: /helpers/ApiDataFactory
3
+ editLink: false
4
+ sidebar: auto
5
+ title: ApiDataFactory
6
+ ---
7
+
8
+ <!-- Generated by documentation.js. Update this documentation by updating the source code. -->
9
+
10
+ ## ApiDataFactory
11
+
12
+ **Extends Helper**
13
+
14
+ Helper for managing remote data using REST API.
15
+ Uses data generators like [rosie][1] or factory girl to create new record.
16
+
17
+ By defining a factory you set the rules of how data is generated.
18
+ This data will be saved on server via REST API and deleted in the end of a test.
19
+
20
+ ## Use Case
21
+
22
+ Acceptance tests interact with a websites using UI and real browser.
23
+ There is no way to create data for a specific test other than from user interface.
24
+ That makes tests slow and fragile. Instead of testing a single feature you need to follow all creation/removal process.
25
+
26
+ This helper solves this problem.
27
+ Most of web application have API, and it can be used to create and delete test records.
28
+ By combining REST API with Factories you can easily create records for tests:
29
+
30
+ ```js
31
+ I.have('user', { login: 'davert', email: 'davert@mail.com' });
32
+ let id = await I.have('post', { title: 'My first post'});
33
+ I.haveMultiple('comment', 3, {post_id: id});
34
+ ```
35
+
36
+ To make this work you need
37
+
38
+ 1. REST API endpoint which allows to perform create / delete requests and
39
+ 2. define data generation rules
40
+
41
+ ### Setup
42
+
43
+ Install [Rosie][1] and [Faker][2] libraries.
44
+
45
+ ```sh
46
+ npm i rosie @faker-js/faker --save-dev
47
+ ```
48
+
49
+ Create a factory file for a resource.
50
+
51
+ See the example for Posts factories:
52
+
53
+ ```js
54
+ // tests/factories/posts.js
55
+
56
+ const { Factory } = require('rosie');
57
+ const { faker } = require('@faker-js/faker');
58
+
59
+ module.exports = new Factory()
60
+ // no need to set id, it will be set by REST API
61
+ .attr('author', () => faker.person.findName())
62
+ .attr('title', () => faker.lorem.sentence())
63
+ .attr('body', () => faker.lorem.paragraph());
64
+ ```
65
+
66
+ For more options see [rosie documentation][1].
67
+
68
+ Then configure ApiDataHelper to match factories and REST API:
69
+
70
+ ### Configuration
71
+
72
+ ApiDataFactory has following config options:
73
+
74
+ * `endpoint`: base URL for the API to send requests to.
75
+ * `cleanup` (default: true): should inserted records be deleted up after tests
76
+ * `factories`: list of defined factories
77
+ * `returnId` (default: false): return id instead of a complete response when creating items.
78
+ * `headers`: list of headers
79
+ * `REST`: configuration for REST requests
80
+
81
+ See the example:
82
+
83
+ ```js
84
+ ApiDataFactory: {
85
+ endpoint: "http://user.com/api",
86
+ cleanup: true,
87
+ headers: {
88
+ 'Content-Type': 'application/json',
89
+ 'Accept': 'application/json',
90
+ },
91
+ factories: {
92
+ post: {
93
+ uri: "/posts",
94
+ factory: "./factories/post",
95
+ },
96
+ comment: {
97
+ factory: "./factories/comment",
98
+ create: { post: "/comments/create" },
99
+ delete: { post: "/comments/delete/{id}" },
100
+ fetchId: (data) => data.result.id
101
+ }
102
+ }
103
+ }
104
+ ```
105
+
106
+ It is required to set REST API `endpoint` which is the baseURL for all API requests.
107
+ Factory file is expected to be passed via `factory` option.
108
+
109
+ This Helper uses [REST][3] helper and accepts its configuration in "REST" section.
110
+ For instance, to set timeout you should add:
111
+
112
+ ```js
113
+ "ApiDataFactory": {
114
+ "REST": {
115
+ "timeout": "100000",
116
+ }
117
+ }
118
+ ```
119
+
120
+ ### Requests
121
+
122
+ By default to create a record ApiDataFactory will use endpoint and plural factory name:
123
+
124
+ * create: `POST {endpoint}/{resource} data`
125
+ * delete: `DELETE {endpoint}/{resource}/id`
126
+
127
+ Example (`endpoint`: `http://app.com/api`):
128
+
129
+ * create: POST request to `http://app.com/api/users`
130
+ * delete: DELETE request to `http://app.com/api/users/1`
131
+
132
+ This behavior can be configured with following options:
133
+
134
+ * `uri`: set different resource uri. Example: `uri: account` => `http://app.com/api/account`.
135
+ * `create`: override create options. Expected format: `{ method: uri }`. Example: `{ "post": "/users/create" }`
136
+ * `delete`: override delete options. Expected format: `{ method: uri }`. Example: `{ "post": "/users/delete/{id}" }`
137
+
138
+ Requests can also be overridden with a function which returns [axois request config][4].
139
+
140
+ ```js
141
+ create: (data) => ({ method: 'post', url: '/posts', data }),
142
+ delete: (id) => ({ method: 'delete', url: '/posts', data: { id } })
143
+
144
+ ```
145
+
146
+ Requests can be updated on the fly by using `onRequest` function. For instance, you can pass in current session from a cookie.
147
+
148
+ ```js
149
+ onRequest: async (request) => {
150
+ // using global codeceptjs instance
151
+ let cookie = await codeceptjs.container.helpers('WebDriver').grabCookie('session');
152
+ request.headers = { Cookie: `session=${cookie.value}` };
153
+ }
154
+ ```
155
+
156
+ ### Responses
157
+
158
+ By default `I.have()` returns a promise with a created data:
159
+
160
+ ```js
161
+ let client = await I.have('client');
162
+ ```
163
+
164
+ Ids of created records are collected and used in the end of a test for the cleanup.
165
+ If you need to receive `id` instead of full response enable `returnId` in a helper config:
166
+
167
+ ```js
168
+ // returnId: false
169
+ let clientId = await I.have('client');
170
+ // clientId == 1
171
+
172
+ // returnId: true
173
+ let clientId = await I.have('client');
174
+ // client == { name: 'John', email: 'john@snow.com' }
175
+ ```
176
+
177
+ By default `id` property of response is taken. This behavior can be changed by setting `fetchId` function in a factory config.
178
+
179
+ ```js
180
+ factories: {
181
+ post: {
182
+ uri: "/posts",
183
+ factory: "./factories/post",
184
+ fetchId: (data) => data.result.posts[0].id
185
+ }
186
+ }
187
+ ```
188
+
189
+ ## Methods
190
+
191
+ ### Parameters
192
+
193
+ * `config` &#x20;
194
+
195
+ ### _requestCreate
196
+
197
+ Executes request to create a record in API.
198
+ Can be replaced from a in custom helper.
199
+
200
+ #### Parameters
201
+
202
+ * `factory` **any**&#x20;
203
+ * `data` **any**&#x20;
204
+
205
+ ### _requestDelete
206
+
207
+ Executes request to delete a record in API
208
+ Can be replaced from a custom helper.
209
+
210
+ #### Parameters
211
+
212
+ * `factory` **any**&#x20;
213
+ * `id` **any**&#x20;
214
+
215
+ ### have
216
+
217
+ Generates a new record using factory and saves API request to store it.
218
+
219
+ ```js
220
+ // create a user
221
+ I.have('user');
222
+ // create user with defined email
223
+ // and receive it when inside async function
224
+ const user = await I.have('user', { email: 'user@user.com'});
225
+ // create a user with options that will not be included in the final request
226
+ I.have('user', { }, { age: 33, height: 55 })
227
+ ```
228
+
229
+ #### Parameters
230
+
231
+ * `factory` **any** factory to use
232
+ * `params` **any?** predefined parameters
233
+ * `options` **any?** options for programmatically generate the attributes
234
+
235
+ Returns **[Promise][5]<any>**&#x20;
236
+
237
+ ### haveMultiple
238
+
239
+ Generates bunch of records and saves multiple API requests to store them.
240
+
241
+ ```js
242
+ // create 3 posts
243
+ I.haveMultiple('post', 3);
244
+
245
+ // create 3 posts by one author
246
+ I.haveMultiple('post', 3, { author: 'davert' });
247
+
248
+ // create 3 posts by one author with options
249
+ I.haveMultiple('post', 3, { author: 'davert' }, { publish_date: '01.01.1997' });
250
+ ```
251
+
252
+ #### Parameters
253
+
254
+ * `factory` **any**&#x20;
255
+ * `times` **any**&#x20;
256
+ * `params` **any?**&#x20;
257
+ * `options` **any?**&#x20;
258
+
259
+ [1]: https://github.com/rosiejs/rosie
260
+
261
+ [2]: https://www.npmjs.com/package/faker
262
+
263
+ [3]: http://codecept.io/helpers/REST/
264
+
265
+ [4]: https://github.com/axios/axios#request-config
266
+
267
+ [5]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise