codeceptjs 4.0.0-rc.2 → 4.0.0-rc.20
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.
- package/README.md +39 -27
- package/bin/codecept.js +15 -2
- package/bin/codeceptq.js +49 -0
- package/bin/mcp-server.js +1187 -0
- package/docs/advanced.md +201 -0
- package/docs/agents.md +159 -0
- package/docs/ai.md +537 -0
- package/docs/aitrace.md +266 -0
- package/docs/api.md +332 -0
- package/docs/assertions.md +415 -0
- package/docs/auth.md +318 -0
- package/docs/basics.md +424 -0
- package/docs/bdd.md +539 -0
- package/docs/best.md +240 -0
- package/docs/bootstrap.md +132 -0
- package/docs/commands.md +352 -0
- package/docs/community-helpers.md +63 -0
- package/docs/configuration.md +230 -0
- package/docs/continuous-integration.md +497 -0
- package/docs/custom-helpers.md +297 -0
- package/docs/data.md +448 -0
- package/docs/debugging.md +332 -0
- package/docs/detox.md +235 -0
- package/docs/docker.md +136 -0
- package/docs/effects.md +179 -0
- package/docs/element-based-testing.md +295 -0
- package/docs/element-selection.md +125 -0
- package/docs/els.md +328 -0
- package/docs/examples.md +161 -0
- package/docs/heal.md +213 -0
- package/docs/helpers/ApiDataFactory.md +267 -0
- package/docs/helpers/Appium.md +1405 -0
- package/docs/helpers/Detox.md +665 -0
- package/docs/helpers/ExpectHelper.md +275 -0
- package/docs/helpers/FileSystem.md +152 -0
- package/docs/helpers/GraphQL.md +152 -0
- package/docs/helpers/GraphQLDataFactory.md +226 -0
- package/docs/helpers/JSONResponse.md +255 -0
- package/docs/helpers/Mochawesome.md +8 -0
- package/docs/helpers/MockRequest.md +377 -0
- package/docs/helpers/MockServer.md +212 -0
- package/docs/helpers/Playwright.md +2969 -0
- package/docs/helpers/Polly.md +44 -0
- package/docs/helpers/Protractor.md +1769 -0
- package/docs/helpers/Puppeteer-firefox.md +86 -0
- package/docs/helpers/Puppeteer.md +2690 -0
- package/docs/helpers/REST.md +289 -0
- package/docs/helpers/SoftExpectHelper.md +352 -0
- package/docs/helpers/WebDriver.md +2682 -0
- package/docs/hooks.md +339 -0
- package/docs/index.md +111 -0
- package/docs/installation.md +83 -0
- package/docs/internal-api.md +265 -0
- package/docs/internal-test-server.md +89 -0
- package/docs/locators.md +355 -0
- package/docs/mcp.md +485 -0
- package/docs/migration-4.md +556 -0
- package/docs/mobile.md +338 -0
- package/docs/pageobjects.md +399 -0
- package/docs/parallel.md +585 -0
- package/docs/playwright.md +714 -0
- package/docs/plugins.md +866 -0
- package/docs/puppeteer.md +314 -0
- package/docs/quickstart.md +120 -0
- package/docs/react.md +70 -0
- package/docs/reports.md +483 -0
- package/docs/retry.md +274 -0
- package/docs/secrets.md +150 -0
- package/docs/sessions.md +80 -0
- package/docs/shadow.md +68 -0
- package/docs/test-structure.md +275 -0
- package/docs/timeouts.md +183 -0
- package/docs/translation.md +247 -0
- package/docs/tutorial.md +271 -0
- package/docs/typescript.md +374 -0
- package/docs/web-element.md +251 -0
- package/docs/webdriver.md +708 -0
- package/docs/within.md +55 -0
- package/lib/ai.js +3 -2
- package/lib/aria.js +260 -0
- package/lib/assertions.js +18 -0
- package/lib/codecept.js +26 -23
- package/lib/command/check.js +2 -1
- package/lib/command/dryRun.js +24 -5
- package/lib/command/generate.js +2 -0
- package/lib/command/gherkin/snippets.js +5 -4
- package/lib/command/init.js +248 -269
- package/lib/command/list.js +150 -10
- package/lib/command/query.js +218 -0
- package/lib/command/run-multiple.js +2 -0
- package/lib/command/run-workers.js +2 -0
- package/lib/command/run.js +1 -1
- package/lib/command/workers/runTests.js +10 -10
- package/lib/config.js +77 -4
- package/lib/container.js +114 -17
- package/lib/effects.js +17 -0
- package/lib/element/WebElement.js +246 -2
- package/lib/els.js +12 -6
- package/lib/globals.js +32 -19
- package/lib/heal.js +4 -3
- package/lib/helper/ApiDataFactory.js +2 -1
- package/lib/helper/Appium.js +8 -8
- package/lib/helper/FileSystem.js +3 -2
- package/lib/helper/GraphQLDataFactory.js +2 -1
- package/lib/helper/Playwright.js +228 -162
- package/lib/helper/Puppeteer.js +208 -76
- package/lib/helper/WebDriver.js +173 -68
- package/lib/helper/errors/MultipleElementsFound.js +27 -110
- package/lib/helper/errors/NonFocusedType.js +8 -0
- package/lib/helper/extras/Download.js +45 -0
- package/lib/helper/extras/PlaywrightReactVueLocator.js +45 -36
- package/lib/helper/extras/elementSelection.js +58 -0
- package/lib/helper/extras/focusCheck.js +43 -0
- package/lib/helper/extras/richTextEditor.js +178 -0
- package/lib/helper/scripts/dropFile.js +11 -0
- package/lib/history.js +3 -2
- package/lib/html.js +103 -16
- package/lib/index.js +9 -1
- package/lib/listener/config.js +6 -4
- package/lib/listener/emptyRun.js +2 -1
- package/lib/listener/globalRetry.js +32 -6
- package/lib/listener/helpers.js +4 -1
- package/lib/listener/mocha.js +2 -1
- package/lib/listener/pageobjects.js +43 -0
- package/lib/listener/result.js +3 -2
- package/lib/locator.js +126 -3
- package/lib/mocha/cli.js +14 -2
- package/lib/mocha/factory.js +7 -2
- package/lib/mocha/inject.js +1 -1
- package/lib/mocha/scenarioConfig.js +2 -1
- package/lib/mocha/ui.js +5 -6
- package/lib/parser.js +2 -2
- package/lib/pause.js +38 -4
- package/lib/plugin/aiTrace.js +453 -0
- package/lib/plugin/analyze.js +1 -1
- package/lib/plugin/auth.js +3 -3
- package/lib/plugin/browser.js +77 -0
- package/lib/plugin/expose.js +159 -0
- package/lib/plugin/heal.js +44 -1
- package/lib/plugin/pageInfo.js +53 -49
- package/lib/plugin/pause.js +131 -0
- package/lib/plugin/pauseOnFail.js +10 -34
- package/lib/plugin/retryFailedStep.js +28 -19
- package/lib/plugin/screencast.js +287 -0
- package/lib/plugin/screenshot.js +563 -0
- package/lib/plugin/screenshotOnFail.js +8 -171
- package/lib/rerun.js +2 -1
- package/lib/result.js +2 -1
- package/lib/step/base.js +3 -2
- package/lib/step/config.js +15 -2
- package/lib/step/record.js +2 -2
- package/lib/store.js +72 -3
- package/lib/translation.js +2 -1
- package/lib/utils/mask_data.js +2 -1
- package/lib/utils/pluginParser.js +151 -0
- package/lib/utils/trace.js +297 -0
- package/lib/utils.js +77 -3
- package/lib/workers.js +52 -22
- package/package.json +19 -13
- package/typings/index.d.ts +19 -5
- package/docs/webapi/amOnPage.mustache +0 -11
- package/docs/webapi/appendField.mustache +0 -11
- package/docs/webapi/attachFile.mustache +0 -12
- package/docs/webapi/blur.mustache +0 -18
- package/docs/webapi/checkOption.mustache +0 -13
- package/docs/webapi/clearCookie.mustache +0 -9
- package/docs/webapi/clearField.mustache +0 -9
- package/docs/webapi/click.mustache +0 -29
- package/docs/webapi/clickLink.mustache +0 -8
- package/docs/webapi/closeCurrentTab.mustache +0 -7
- package/docs/webapi/closeOtherTabs.mustache +0 -8
- package/docs/webapi/dontSee.mustache +0 -11
- package/docs/webapi/dontSeeCheckboxIsChecked.mustache +0 -10
- package/docs/webapi/dontSeeCookie.mustache +0 -8
- package/docs/webapi/dontSeeCurrentPathEquals.mustache +0 -10
- package/docs/webapi/dontSeeCurrentUrlEquals.mustache +0 -10
- package/docs/webapi/dontSeeElement.mustache +0 -8
- package/docs/webapi/dontSeeElementInDOM.mustache +0 -8
- package/docs/webapi/dontSeeInCurrentUrl.mustache +0 -4
- package/docs/webapi/dontSeeInField.mustache +0 -11
- package/docs/webapi/dontSeeInSource.mustache +0 -8
- package/docs/webapi/dontSeeInTitle.mustache +0 -8
- package/docs/webapi/dontSeeTraffic.mustache +0 -13
- package/docs/webapi/doubleClick.mustache +0 -13
- package/docs/webapi/downloadFile.mustache +0 -12
- package/docs/webapi/dragAndDrop.mustache +0 -9
- package/docs/webapi/dragSlider.mustache +0 -11
- package/docs/webapi/executeAsyncScript.mustache +0 -24
- package/docs/webapi/executeScript.mustache +0 -26
- package/docs/webapi/fillField.mustache +0 -16
- package/docs/webapi/flushNetworkTraffics.mustache +0 -5
- package/docs/webapi/focus.mustache +0 -13
- package/docs/webapi/forceClick.mustache +0 -28
- package/docs/webapi/forceRightClick.mustache +0 -18
- package/docs/webapi/grabAllWindowHandles.mustache +0 -7
- package/docs/webapi/grabAttributeFrom.mustache +0 -10
- package/docs/webapi/grabAttributeFromAll.mustache +0 -9
- package/docs/webapi/grabBrowserLogs.mustache +0 -9
- package/docs/webapi/grabCookie.mustache +0 -11
- package/docs/webapi/grabCssPropertyFrom.mustache +0 -11
- package/docs/webapi/grabCssPropertyFromAll.mustache +0 -10
- package/docs/webapi/grabCurrentUrl.mustache +0 -9
- package/docs/webapi/grabCurrentWindowHandle.mustache +0 -6
- package/docs/webapi/grabDataFromPerformanceTiming.mustache +0 -20
- package/docs/webapi/grabElementBoundingRect.mustache +0 -20
- package/docs/webapi/grabGeoLocation.mustache +0 -8
- package/docs/webapi/grabHTMLFrom.mustache +0 -10
- package/docs/webapi/grabHTMLFromAll.mustache +0 -9
- package/docs/webapi/grabNumberOfOpenTabs.mustache +0 -8
- package/docs/webapi/grabNumberOfVisibleElements.mustache +0 -9
- package/docs/webapi/grabPageScrollPosition.mustache +0 -8
- package/docs/webapi/grabPopupText.mustache +0 -5
- package/docs/webapi/grabRecordedNetworkTraffics.mustache +0 -10
- package/docs/webapi/grabSource.mustache +0 -8
- package/docs/webapi/grabTextFrom.mustache +0 -10
- package/docs/webapi/grabTextFromAll.mustache +0 -9
- package/docs/webapi/grabTitle.mustache +0 -8
- package/docs/webapi/grabValueFrom.mustache +0 -9
- package/docs/webapi/grabValueFromAll.mustache +0 -8
- package/docs/webapi/grabWebElement.mustache +0 -9
- package/docs/webapi/grabWebElements.mustache +0 -9
- package/docs/webapi/moveCursorTo.mustache +0 -12
- package/docs/webapi/openNewTab.mustache +0 -7
- package/docs/webapi/pressKey.mustache +0 -12
- package/docs/webapi/pressKeyDown.mustache +0 -12
- package/docs/webapi/pressKeyUp.mustache +0 -12
- package/docs/webapi/pressKeyWithKeyNormalization.mustache +0 -60
- package/docs/webapi/refreshPage.mustache +0 -6
- package/docs/webapi/resizeWindow.mustache +0 -6
- package/docs/webapi/rightClick.mustache +0 -14
- package/docs/webapi/saveElementScreenshot.mustache +0 -10
- package/docs/webapi/saveScreenshot.mustache +0 -12
- package/docs/webapi/say.mustache +0 -10
- package/docs/webapi/scrollIntoView.mustache +0 -11
- package/docs/webapi/scrollPageToBottom.mustache +0 -6
- package/docs/webapi/scrollPageToTop.mustache +0 -6
- package/docs/webapi/scrollTo.mustache +0 -12
- package/docs/webapi/see.mustache +0 -11
- package/docs/webapi/seeAttributesOnElements.mustache +0 -9
- package/docs/webapi/seeCheckboxIsChecked.mustache +0 -10
- package/docs/webapi/seeCookie.mustache +0 -8
- package/docs/webapi/seeCssPropertiesOnElements.mustache +0 -9
- package/docs/webapi/seeCurrentPathEquals.mustache +0 -10
- package/docs/webapi/seeCurrentUrlEquals.mustache +0 -11
- package/docs/webapi/seeElement.mustache +0 -8
- package/docs/webapi/seeElementInDOM.mustache +0 -8
- package/docs/webapi/seeInCurrentUrl.mustache +0 -8
- package/docs/webapi/seeInField.mustache +0 -12
- package/docs/webapi/seeInPopup.mustache +0 -8
- package/docs/webapi/seeInSource.mustache +0 -7
- package/docs/webapi/seeInTitle.mustache +0 -8
- package/docs/webapi/seeNumberOfElements.mustache +0 -11
- package/docs/webapi/seeNumberOfVisibleElements.mustache +0 -10
- package/docs/webapi/seeTextEquals.mustache +0 -9
- package/docs/webapi/seeTitleEquals.mustache +0 -8
- package/docs/webapi/seeTraffic.mustache +0 -36
- package/docs/webapi/selectOption.mustache +0 -21
- package/docs/webapi/setCookie.mustache +0 -16
- package/docs/webapi/setGeoLocation.mustache +0 -12
- package/docs/webapi/startRecordingTraffic.mustache +0 -8
- package/docs/webapi/startRecordingWebSocketMessages.mustache +0 -8
- package/docs/webapi/stopRecordingTraffic.mustache +0 -5
- package/docs/webapi/stopRecordingWebSocketMessages.mustache +0 -7
- package/docs/webapi/switchTo.mustache +0 -9
- package/docs/webapi/switchToNextTab.mustache +0 -10
- package/docs/webapi/switchToPreviousTab.mustache +0 -10
- package/docs/webapi/type.mustache +0 -21
- package/docs/webapi/uncheckOption.mustache +0 -13
- package/docs/webapi/wait.mustache +0 -8
- package/docs/webapi/waitForClickable.mustache +0 -11
- package/docs/webapi/waitForCookie.mustache +0 -9
- package/docs/webapi/waitForDetached.mustache +0 -10
- package/docs/webapi/waitForDisabled.mustache +0 -6
- package/docs/webapi/waitForElement.mustache +0 -11
- package/docs/webapi/waitForEnabled.mustache +0 -6
- package/docs/webapi/waitForFunction.mustache +0 -17
- package/docs/webapi/waitForInvisible.mustache +0 -10
- package/docs/webapi/waitForNumberOfTabs.mustache +0 -9
- package/docs/webapi/waitForText.mustache +0 -13
- package/docs/webapi/waitForValue.mustache +0 -10
- package/docs/webapi/waitForVisible.mustache +0 -10
- package/docs/webapi/waitInUrl.mustache +0 -9
- package/docs/webapi/waitNumberOfVisibleElements.mustache +0 -10
- package/docs/webapi/waitToHide.mustache +0 -10
- package/docs/webapi/waitUrlEquals.mustache +0 -10
- package/lib/helper/AI.js +0 -214
- package/lib/listener/enhancedGlobalRetry.js +0 -110
- package/lib/plugin/enhancedRetryFailedStep.js +0 -99
- package/lib/plugin/htmlReporter.js +0 -3648
- package/lib/plugin/stepByStepReport.js +0 -427
- package/lib/plugin/subtitles.js +0 -89
- package/lib/retryCoordinator.js +0 -207
- package/typings/promiseBasedTypes.d.ts +0 -9469
- package/typings/types.d.ts +0 -11402
package/docs/els.md
ADDED
|
@@ -0,0 +1,328 @@
|
|
|
1
|
+
## Element Access
|
|
2
|
+
|
|
3
|
+
The `els` module provides low-level element manipulation functions for CodeceptJS tests, allowing for more granular control over element interactions and assertions. Elements are wrapped in a unified `WebElement` class that provides a consistent API across all helpers (Playwright, WebDriver, Puppeteer).
|
|
4
|
+
|
|
5
|
+
> **Note:** For a comprehensive guide on element-based testing patterns and best practices, see [Element-Based Testing](element-based-testing.md).
|
|
6
|
+
|
|
7
|
+
### Usage
|
|
8
|
+
|
|
9
|
+
Import the els functions in your test file:
|
|
10
|
+
|
|
11
|
+
```js
|
|
12
|
+
import { element, eachElement, expectElement, expectAnyElement, expectAllElements } from 'codeceptjs/els'
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## element
|
|
16
|
+
|
|
17
|
+
The `element` function allows you to perform custom operations on the first matching element found by a locator. It provides a low-level way to interact with elements when the built-in helper methods aren't sufficient.
|
|
18
|
+
|
|
19
|
+
### Syntax
|
|
20
|
+
|
|
21
|
+
```js
|
|
22
|
+
element(purpose, locator, fn);
|
|
23
|
+
// or
|
|
24
|
+
element(locator, fn);
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
### Parameters
|
|
28
|
+
|
|
29
|
+
- `purpose` (optional) - A string describing the operation being performed. If omitted, a default purpose will be generated from the function.
|
|
30
|
+
- `locator` - A locator string/object to find the element(s).
|
|
31
|
+
- `fn` - An async function that receives the element as its argument and performs the desired operation. `el` argument is a `WebElement` wrapper providing a consistent API across all helpers.
|
|
32
|
+
|
|
33
|
+
### Returns
|
|
34
|
+
|
|
35
|
+
Returns the result of the provided async function executed on the first matching element.
|
|
36
|
+
|
|
37
|
+
### Example
|
|
38
|
+
|
|
39
|
+
```js
|
|
40
|
+
Scenario('my test', async ({ I }) => {
|
|
41
|
+
// combine element function with standard steps:
|
|
42
|
+
I.amOnPage('/cart');
|
|
43
|
+
|
|
44
|
+
// but use await every time you use element function
|
|
45
|
+
await element(
|
|
46
|
+
// with explicit purpose
|
|
47
|
+
'check custom attribute',
|
|
48
|
+
'.button',
|
|
49
|
+
async el => await el.getAttribute('data-test'),
|
|
50
|
+
);
|
|
51
|
+
|
|
52
|
+
// or simply
|
|
53
|
+
await element('.button', async el => {
|
|
54
|
+
return await el.isEnabled();
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### Notes
|
|
60
|
+
|
|
61
|
+
- Only works with helpers that implement the `_locate` method
|
|
62
|
+
- The function will only operate on the first element found, even if multiple elements match the locator
|
|
63
|
+
- The provided callback must be an async function
|
|
64
|
+
- Throws an error if no helper with `_locate` method is enabled
|
|
65
|
+
|
|
66
|
+
## eachElement
|
|
67
|
+
|
|
68
|
+
The `eachElement` function allows you to perform operations on each element that matches a locator. It's useful for iterating through multiple elements and performing the same operation on each one.
|
|
69
|
+
|
|
70
|
+
### Syntax
|
|
71
|
+
|
|
72
|
+
```js
|
|
73
|
+
eachElement(purpose, locator, fn);
|
|
74
|
+
// or
|
|
75
|
+
eachElement(locator, fn);
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### Parameters
|
|
79
|
+
|
|
80
|
+
- `purpose` (optional) - A string describing the operation being performed. If omitted, a default purpose will be generated from the function.
|
|
81
|
+
- `locator` - A locator string/object to find the element(s).
|
|
82
|
+
- `fn` - An async function that receives two arguments:
|
|
83
|
+
- `el` - The current element being processed
|
|
84
|
+
- `index` - The index of the current element in the collection
|
|
85
|
+
|
|
86
|
+
### Returns
|
|
87
|
+
|
|
88
|
+
Returns a promise that resolves when all elements have been processed. If any element operation fails, the function will throw the first encountered error.
|
|
89
|
+
|
|
90
|
+
### Example
|
|
91
|
+
|
|
92
|
+
```js
|
|
93
|
+
Scenario('my test', async ({ I }) => {
|
|
94
|
+
// combine element function with standard steps:
|
|
95
|
+
I.click('/hotels');
|
|
96
|
+
|
|
97
|
+
// iterate over elements but don't forget to put await
|
|
98
|
+
await eachElement(
|
|
99
|
+
'validate list items', // explain your actions for future review
|
|
100
|
+
'.list-item', // locator
|
|
101
|
+
async (el, index) => {
|
|
102
|
+
const text = await el.getText();
|
|
103
|
+
console.log(`Item ${index}: ${text}`);
|
|
104
|
+
},
|
|
105
|
+
);
|
|
106
|
+
|
|
107
|
+
// Or simply check if all checkboxes are checked
|
|
108
|
+
await eachElement('input[type="checkbox"]', async el => {
|
|
109
|
+
const checked = await el.getProperty('checked');
|
|
110
|
+
if (!checked) {
|
|
111
|
+
throw new Error('Found unchecked checkbox');
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
});
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
### Notes
|
|
118
|
+
|
|
119
|
+
- Only works with helpers that implement the `_locate` method
|
|
120
|
+
- The function will process all elements that match the locator
|
|
121
|
+
- The provided callback must be an async function
|
|
122
|
+
- If an operation fails on any element, the error is logged and the function continues processing remaining elements
|
|
123
|
+
- After all elements are processed, if any errors occurred, the first error is thrown
|
|
124
|
+
- Throws an error if no helper with `_locate` method is enabled
|
|
125
|
+
|
|
126
|
+
## expectElement
|
|
127
|
+
|
|
128
|
+
The `expectElement` function allows you to perform assertions on the first element that matches a locator. It's designed for validating element properties or states and will throw an assertion error if the condition is not met.
|
|
129
|
+
|
|
130
|
+
### Syntax
|
|
131
|
+
|
|
132
|
+
```js
|
|
133
|
+
expectElement(locator, fn);
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
### Parameters
|
|
137
|
+
|
|
138
|
+
- `locator` - A locator string/object to find the element(s).
|
|
139
|
+
- `fn` - An async function that receives the element as its argument and should return a boolean value:
|
|
140
|
+
- `true` - The assertion passed
|
|
141
|
+
- `false` - The assertion failed
|
|
142
|
+
|
|
143
|
+
### Returns
|
|
144
|
+
|
|
145
|
+
Returns a promise that resolves when the assertion is complete. Throws an assertion error if the condition is not met.
|
|
146
|
+
|
|
147
|
+
### Example
|
|
148
|
+
|
|
149
|
+
```js
|
|
150
|
+
// Check if a button is enabled
|
|
151
|
+
await expectElement('.submit-button', async el => {
|
|
152
|
+
return await el.isEnabled();
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
// Verify element has specific text content
|
|
156
|
+
await expectElement('.header', async el => {
|
|
157
|
+
const text = await el.getText();
|
|
158
|
+
return text === 'Welcome';
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
// Check for specific attribute value
|
|
162
|
+
await expectElement('#user-profile', async el => {
|
|
163
|
+
const role = await el.getAttribute('role');
|
|
164
|
+
return role === 'button';
|
|
165
|
+
});
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
### Notes
|
|
169
|
+
|
|
170
|
+
- Only works with helpers that implement the `_locate` method
|
|
171
|
+
- The function will only check the first element found, even if multiple elements match the locator
|
|
172
|
+
- The provided callback must be an async function that returns a boolean
|
|
173
|
+
- The assertion message will include both the locator and the function used for validation
|
|
174
|
+
- Throws an error if no helper with `_locate` method is enabled
|
|
175
|
+
|
|
176
|
+
## expectAnyElement
|
|
177
|
+
|
|
178
|
+
The `expectAnyElement` function allows you to perform assertions where at least one element from a collection should satisfy the condition. It's useful when you need to verify that at least one element among many matches your criteria.
|
|
179
|
+
|
|
180
|
+
### Syntax
|
|
181
|
+
|
|
182
|
+
```js
|
|
183
|
+
expectAnyElement(locator, fn);
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
### Parameters
|
|
187
|
+
|
|
188
|
+
- `locator` - A locator string/object to find the element(s).
|
|
189
|
+
- `fn` - An async function that receives the element as its argument and should return a boolean value:
|
|
190
|
+
- `true` - The assertion passed for this element
|
|
191
|
+
- `false` - The assertion failed for this element
|
|
192
|
+
|
|
193
|
+
### Returns
|
|
194
|
+
|
|
195
|
+
Returns a promise that resolves when the assertion is complete. Throws an assertion error if no elements satisfy the condition.
|
|
196
|
+
|
|
197
|
+
### Example
|
|
198
|
+
|
|
199
|
+
```js
|
|
200
|
+
Scenario('validate any element matches criteria', async ({ I }) => {
|
|
201
|
+
// Navigate to the page
|
|
202
|
+
I.amOnPage('/products');
|
|
203
|
+
|
|
204
|
+
// Check if any product is marked as "in stock"
|
|
205
|
+
await expectAnyElement('.product-item', async el => {
|
|
206
|
+
const status = await el.getAttribute('data-status');
|
|
207
|
+
return status === 'in-stock';
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
// Verify at least one price is below $100
|
|
211
|
+
await expectAnyElement('.price-tag', async el => {
|
|
212
|
+
const price = await el.getText();
|
|
213
|
+
return parseFloat(price.replace('$', '')) < 100;
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
// Check if any button in the list is enabled
|
|
217
|
+
await expectAnyElement('.action-button', async el => {
|
|
218
|
+
return await el.isEnabled();
|
|
219
|
+
});
|
|
220
|
+
});
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
### Notes
|
|
224
|
+
|
|
225
|
+
- Only works with helpers that implement the `_locate` method
|
|
226
|
+
- The function will check all matching elements until it finds one that satisfies the condition
|
|
227
|
+
- Stops checking elements once the first matching condition is found
|
|
228
|
+
- The provided callback must be an async function that returns a boolean
|
|
229
|
+
- Throws an assertion error if no elements satisfy the condition
|
|
230
|
+
- Throws an error if no helper with `_locate` method is enabled
|
|
231
|
+
|
|
232
|
+
## expectAllElements
|
|
233
|
+
|
|
234
|
+
The `expectAllElements` function verifies that every element matching the locator satisfies the given condition. It's useful when you need to ensure that all elements in a collection meet specific criteria.
|
|
235
|
+
|
|
236
|
+
### Syntax
|
|
237
|
+
|
|
238
|
+
```js
|
|
239
|
+
expectAllElements(locator, fn);
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
### Parameters
|
|
243
|
+
|
|
244
|
+
- `locator` - A locator string/object to find the element(s).
|
|
245
|
+
- `fn` - An async function that receives the element as its argument and should return a boolean value:
|
|
246
|
+
- `true` - The assertion passed for this element
|
|
247
|
+
- `false` - The assertion failed for this element
|
|
248
|
+
|
|
249
|
+
### Returns
|
|
250
|
+
|
|
251
|
+
Returns a promise that resolves when all assertions are complete. Throws an assertion error as soon as any element fails the condition.
|
|
252
|
+
|
|
253
|
+
### Example
|
|
254
|
+
|
|
255
|
+
```js
|
|
256
|
+
Scenario('validate all elements meet criteria', async ({ I }) => {
|
|
257
|
+
// Navigate to the page
|
|
258
|
+
I.amOnPage('/dashboard');
|
|
259
|
+
|
|
260
|
+
// Verify all required fields have the required attribute
|
|
261
|
+
await expectAllElements('.required-field', async el => {
|
|
262
|
+
const required = await el.getAttribute('required');
|
|
263
|
+
return required !== null;
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
// Check if all checkboxes in a form are checked
|
|
267
|
+
await expectAllElements('input[type="checkbox"]', async el => {
|
|
268
|
+
const checked = await el.getProperty('checked');
|
|
269
|
+
return checked === true;
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
// Verify all items in a list have non-empty text
|
|
273
|
+
await expectAllElements('.list-item', async el => {
|
|
274
|
+
const text = await el.getText();
|
|
275
|
+
return text.trim().length > 0;
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
// Ensure all buttons in a section are enabled
|
|
279
|
+
await expectAllElements('#action-section button', async el => {
|
|
280
|
+
return await el.isEnabled();
|
|
281
|
+
});
|
|
282
|
+
});
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
### Notes
|
|
286
|
+
|
|
287
|
+
- Only works with helpers that implement the `_locate` method
|
|
288
|
+
- The function checks every element that matches the locator
|
|
289
|
+
- Fails fast: stops checking elements as soon as one fails the condition
|
|
290
|
+
- The provided callback must be an async function that returns a boolean
|
|
291
|
+
- The assertion message will include which element number failed (e.g., "element #2 of...")
|
|
292
|
+
- Throws an error if no helper with `_locate` method is enabled
|
|
293
|
+
|
|
294
|
+
## WebElement API
|
|
295
|
+
|
|
296
|
+
Elements passed to your callbacks are wrapped in a `WebElement` class that provides a consistent API across all helpers. For complete documentation of the WebElement API, see [WebElement](WebElement.md).
|
|
297
|
+
|
|
298
|
+
Quick reference of available methods:
|
|
299
|
+
|
|
300
|
+
```js
|
|
301
|
+
await element('.my-element', async el => {
|
|
302
|
+
// Get element information
|
|
303
|
+
const text = await el.getText()
|
|
304
|
+
const attr = await el.getAttribute('data-value')
|
|
305
|
+
const prop = await el.getProperty('value')
|
|
306
|
+
const html = await el.getInnerHTML()
|
|
307
|
+
|
|
308
|
+
// Check state
|
|
309
|
+
const visible = await el.isVisible()
|
|
310
|
+
const enabled = await el.isEnabled()
|
|
311
|
+
const exists = await el.exists()
|
|
312
|
+
|
|
313
|
+
// Interactions
|
|
314
|
+
await el.click()
|
|
315
|
+
await el.type('text')
|
|
316
|
+
|
|
317
|
+
// Child elements
|
|
318
|
+
const child = await el.$('.child')
|
|
319
|
+
const children = await el.$$('.child')
|
|
320
|
+
|
|
321
|
+
// Position
|
|
322
|
+
const box = await el.getBoundingBox()
|
|
323
|
+
|
|
324
|
+
// Native access
|
|
325
|
+
const helper = el.getHelper()
|
|
326
|
+
const native = el.getNativeElement()
|
|
327
|
+
})
|
|
328
|
+
```
|
package/docs/examples.md
ADDED
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
---
|
|
2
|
+
permalink: /examples
|
|
3
|
+
layout: Section
|
|
4
|
+
sidebar: false
|
|
5
|
+
title: Examples
|
|
6
|
+
editLink: false
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Examples
|
|
10
|
+
|
|
11
|
+
> Add your own examples to our [Wiki Page](https://github.com/codeceptjs/CodeceptJS/wiki/Examples)
|
|
12
|
+
|
|
13
|
+
## [TodoMVC Examples](https://github.com/codecept-js/examples)
|
|
14
|
+
|
|
15
|
+

|
|
16
|
+
|
|
17
|
+
Playground repository where you can run tests in different helpers on a basic single-page website.
|
|
18
|
+
|
|
19
|
+
Tests repository demonstrate usage of
|
|
20
|
+
|
|
21
|
+
- Playwright helper
|
|
22
|
+
- Puppeteer helper
|
|
23
|
+
- WebDriver helper
|
|
24
|
+
- Toggle headless mode with env variables
|
|
25
|
+
- PageObjects
|
|
26
|
+
- Cucumber syntax
|
|
27
|
+
|
|
28
|
+
## [Basic Examples](https://github.com/Codeception/CodeceptJS/tree/master/examples)
|
|
29
|
+
|
|
30
|
+
CodeceptJS repo contains basic tests (both failing and passing) just to show how it works.
|
|
31
|
+
Our team uses it to test new features and run simple scenarios.
|
|
32
|
+
|
|
33
|
+
## [CodeceptJS Cucumber E2E Framework](https://github.com/gkushang/codeceptjs-e2e)
|
|
34
|
+
|
|
35
|
+
This repository contains complete E2E framework for CodeceptJS with Cucumber and SauceLabs Integration
|
|
36
|
+
|
|
37
|
+
- CodecepJS-Cucumber E2E Framework
|
|
38
|
+
- Saucelabs Integration
|
|
39
|
+
- Run Cross Browser tests in Parallel on SauceLabs with a simple command
|
|
40
|
+
- Run tests on `chrome:headless`
|
|
41
|
+
- Page Objects
|
|
42
|
+
- `Should.js` Assertion Library
|
|
43
|
+
- Uses `wdio` service (selenium-standalone, sauce)
|
|
44
|
+
- Allure HTML Reports
|
|
45
|
+
- Uses shared Master configuration
|
|
46
|
+
- Sample example and feature files of GitHub Features
|
|
47
|
+
|
|
48
|
+
## [Enterprise Grade Tests](https://github.com/uc-cdis/gen3-qa)
|
|
49
|
+
|
|
50
|
+
Complex testing solution by [Gen3](https://github.com/uc-cdis/gen3-qa)
|
|
51
|
+
|
|
52
|
+
Includes
|
|
53
|
+
|
|
54
|
+
- classical CodeceptJS tests
|
|
55
|
+
- BDD tests
|
|
56
|
+
- Jenkins integration
|
|
57
|
+
- Complex Before/BeforeSuite scripts and more
|
|
58
|
+
|
|
59
|
+
## [Testing Single Page Application](https://github.com/bugiratracker/codeceptjs-demo)
|
|
60
|
+
|
|
61
|
+
End 2 end tests for Task management app (currently offline).
|
|
62
|
+
|
|
63
|
+
Tests repository demonstrate usage of
|
|
64
|
+
|
|
65
|
+
- Puppeteer helper
|
|
66
|
+
- ApiDataFactory helper
|
|
67
|
+
- autoLogin plugin
|
|
68
|
+
- Dynamic config with profiles
|
|
69
|
+
|
|
70
|
+
## [Practical E2E Tests](https://gitlab.com/paulvincent/codeceptjs-e2e-testing)
|
|
71
|
+
|
|
72
|
+
Examples from the book [Practical End 2 End Testing with CodeceptJS](https://leanpub.com/codeceptjs/) by **Paul Vincent Beigang**.
|
|
73
|
+
|
|
74
|
+
This repository demonstrates usage of:
|
|
75
|
+
|
|
76
|
+
- dynamic config with profiles
|
|
77
|
+
- testing WYSIWYG editor
|
|
78
|
+
- GitLab CI
|
|
79
|
+
|
|
80
|
+
## [Amazon Tests v2](https://gitlab.com/thanhnguyendh/codeceptjs-wdio-services)
|
|
81
|
+
|
|
82
|
+
Testing Amazon website using Selenium WebDriver.
|
|
83
|
+
|
|
84
|
+
This repository demonstrates usage of:
|
|
85
|
+
|
|
86
|
+
- WebDriver helper
|
|
87
|
+
- Page Objects
|
|
88
|
+
- wdio services (selenium-standalone)
|
|
89
|
+
- Parallel execution
|
|
90
|
+
- GitLab CI setup
|
|
91
|
+
|
|
92
|
+
## [Tests with Docker Compose](https://github.com/mathesouza/codeceptjs-docker-compose)
|
|
93
|
+
|
|
94
|
+
Running CodeceptJS tests with Docker Compose
|
|
95
|
+
|
|
96
|
+
This repository demonstrates usage of:
|
|
97
|
+
|
|
98
|
+
- CodeceptJS Docker image
|
|
99
|
+
- WebDriver helper
|
|
100
|
+
- Allure plugin
|
|
101
|
+
|
|
102
|
+
## [AngularJS Example Tests](https://github.com/armno/angular-e2e-codeceptjs-example)
|
|
103
|
+
|
|
104
|
+
Based on [Setting up End-to-End Testing in Angular Project with CodeceptJS](https://medium.com/@armno/setting-up-end-to-end-testing-in-angular-project-with-codeceptjs-ac1784de3420) post by Armno Prommarak.
|
|
105
|
+
|
|
106
|
+
This repository demonstrates usage of
|
|
107
|
+
|
|
108
|
+
- Puppeteer helper
|
|
109
|
+
- Working with Angular CLI
|
|
110
|
+
- Reports with Mochawesome helper
|
|
111
|
+
|
|
112
|
+
## [REST Example Tests](https://github.com/PeterNgTr/codeceptjs-rest-demo)
|
|
113
|
+
|
|
114
|
+
This repository demonstrates usage of
|
|
115
|
+
|
|
116
|
+
- REST helper
|
|
117
|
+
|
|
118
|
+
## [Automation Starter](https://github.com/sjorrillo/automation-starter)
|
|
119
|
+
|
|
120
|
+
The purpose of this application is for learning the basics and how to use good practices and useful tools in automation.
|
|
121
|
+
|
|
122
|
+
- Puppeteer helper
|
|
123
|
+
- Working with gherkin, also it has type definitions and to be able to use them inside when, given and then make sure you add `declare function inject(): { I: CodeceptJS.I, [key: string]: any; };`in the `steps.d.ts`file
|
|
124
|
+
- Linting `airbnb-base`, `codeceptjs/codeceptjs` and full ES6 support
|
|
125
|
+
|
|
126
|
+
## [Example for using: Puppeteer, Gherkin, Allure with parallel execution](https://github.com/SchnuckySchuster/codeceptJSExample)
|
|
127
|
+
|
|
128
|
+
This is a ready to use example that shows how to integrate CodeceptJS with Puppeteer and Allure as reporting tool.
|
|
129
|
+
|
|
130
|
+
- detailed ReadMe
|
|
131
|
+
- tests written in cucumber alongside tests written in the codeceptJS DSL
|
|
132
|
+
- puppeteer helper example
|
|
133
|
+
- test steps, pages, fragments
|
|
134
|
+
- examples for sequential and parallel execution
|
|
135
|
+
- generation of allure test results
|
|
136
|
+
|
|
137
|
+
## [Example for Advanced REST API testing: TypeScript, Axios, CodeceptJS, Jest Expect, Docker, Allure, Mock-Server, Prettier + Eslint, pre-commit, Jest Unit Tests ](https://github.com/EgorBodnar/rest-axios-codeceptjs-allure-docker-test-example)
|
|
138
|
+
|
|
139
|
+
One button example with built-in mocked backend.
|
|
140
|
+
|
|
141
|
+
If you already have a UI testing solution based on the CodeceptJS and you need to implement advanced REST API testing you can just extend your existing framework. Use this implementation as an example.
|
|
142
|
+
This is necessary if all integrations with TMS and CI/CD are already configured, and you do not want to reconnect and configure the plugins and libraries used for the new test runner. Use CodeceptJS!
|
|
143
|
+
|
|
144
|
+
- Easy run
|
|
145
|
+
- Detailed README
|
|
146
|
+
- Well documented mocked backend's REST API endpoints
|
|
147
|
+
- HTTP request client with session support and unit tests
|
|
148
|
+
- Exemplary code control
|
|
149
|
+
- Ready to launch in a CI/CD system as is
|
|
150
|
+
- OOP, Test data models and builders, endpoint decorators
|
|
151
|
+
|
|
152
|
+
## [Playwright fun with CodeceptJS](https://github.com/PeterNgTr/codeceptjs-playwright-fun)
|
|
153
|
+
|
|
154
|
+
- Tests are written in TS
|
|
155
|
+
- CI/CD with Github Actions
|
|
156
|
+
- Page Object Model is applied
|
|
157
|
+
- ReportPortal Integration
|
|
158
|
+
|
|
159
|
+
## How to
|
|
160
|
+
|
|
161
|
+
- Create a plugin with TS [link](https://github.com/reutenkoivan/codeceptjs-plugins/tree/main/packages/html-snapshot-on-fail)
|
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
|
+

|
|
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.
|