codeceptjs 4.0.2-beta.9 → 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.
- package/README.md +39 -28
- package/bin/codecept.js +15 -2
- package/bin/codeceptq.js +49 -0
- package/bin/mcp-server.js +1189 -0
- package/docs/advanced.md +201 -0
- package/docs/agents.md +181 -0
- package/docs/ai.md +489 -0
- package/docs/aitrace.md +266 -0
- package/docs/api.md +332 -0
- package/docs/architecture.md +235 -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 +185 -0
- package/docs/continuous-integration.md +431 -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 +107 -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/environment-variables.md +131 -0
- package/docs/examples.md +160 -0
- package/docs/heal.md +213 -0
- package/docs/helpers/ApiDataFactory.md +267 -0
- package/docs/helpers/Appium.md +1419 -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/MockRequest.md +377 -0
- package/docs/helpers/Playwright.md +2970 -0
- package/docs/helpers/Puppeteer-firefox.md +86 -0
- package/docs/helpers/Puppeteer.md +2583 -0
- package/docs/helpers/REST.md +289 -0
- package/docs/helpers/WebDriver.md +2639 -0
- package/docs/hooks.md +148 -0
- package/docs/index.md +111 -0
- package/docs/installation.md +121 -0
- package/docs/internal-test-server.md +89 -0
- package/docs/locators.md +355 -0
- package/docs/mcp.md +485 -0
- package/docs/migrate-from-cypress.md +98 -0
- package/docs/migrate-from-java.md +108 -0
- package/docs/migrate-from-protractor.md +101 -0
- package/docs/migrate-from-testcafe.md +99 -0
- package/docs/migration-4.md +745 -0
- package/docs/mobile.md +338 -0
- package/docs/pageobjects.md +399 -0
- package/docs/parallel.md +187 -0
- package/docs/playwright.md +714 -0
- package/docs/plugins/aiTrace.md +49 -0
- package/docs/plugins/analyze.md +66 -0
- package/docs/plugins/auth.md +241 -0
- package/docs/plugins/autoDelay.md +48 -0
- package/docs/plugins/browser.md +41 -0
- package/docs/plugins/coverage.md +39 -0
- package/docs/plugins/customLocator.md +119 -0
- package/docs/plugins/customReporter.md +16 -0
- package/docs/plugins/expose.md +75 -0
- package/docs/plugins/heal.md +44 -0
- package/docs/plugins/junitReporter.md +51 -0
- package/docs/plugins/pageInfo.md +34 -0
- package/docs/plugins/pause.md +43 -0
- package/docs/plugins/pauseOnFail.md +18 -0
- package/docs/plugins/retryFailedStep.md +75 -0
- package/docs/plugins/screencast.md +55 -0
- package/docs/plugins/screenshot.md +58 -0
- package/docs/plugins/screenshotOnFail.md +18 -0
- package/docs/plugins/stepTimeout.md +65 -0
- package/docs/plugins.md +87 -0
- package/docs/puppeteer.md +314 -0
- package/docs/quickstart.md +120 -0
- package/docs/reports.md +195 -0
- package/docs/retry.md +311 -0
- package/docs/secrets.md +150 -0
- package/docs/sessions.md +80 -0
- package/docs/shadow.md +68 -0
- package/docs/store.md +94 -0
- package/docs/test-structure.md +275 -0
- package/docs/timeouts.md +183 -0
- package/docs/translation.md +247 -0
- package/docs/tutorial.md +323 -0
- package/docs/typescript.md +159 -0
- package/docs/web-element.md +251 -0
- package/docs/webdriver.md +641 -0
- package/docs/within.md +55 -0
- package/lib/actor.js +1 -36
- package/lib/ai.js +3 -2
- package/lib/aria.js +260 -0
- package/lib/assertions.js +18 -0
- package/lib/codecept.js +34 -25
- package/lib/command/check.js +2 -1
- package/lib/command/definitions.js +6 -7
- package/lib/command/dryRun.js +24 -5
- package/lib/command/generate.js +3 -1
- package/lib/command/gherkin/snippets.js +5 -4
- package/lib/command/init.js +249 -270
- package/lib/command/list.js +150 -10
- package/lib/command/query.js +218 -0
- package/lib/command/run-multiple.js +3 -1
- package/lib/command/run-workers.js +2 -14
- package/lib/command/run.js +3 -17
- package/lib/command/utils.js +14 -0
- package/lib/command/workers/runTests.js +84 -41
- package/lib/config.js +96 -18
- package/lib/container.js +115 -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 +7 -4
- 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 +358 -467
- package/lib/helper/Puppeteer.js +335 -192
- package/lib/helper/WebDriver.js +324 -111
- package/lib/helper/errors/ElementNotFound.js +5 -2
- package/lib/helper/errors/MultipleElementsFound.js +52 -0
- package/lib/helper/errors/NonFocusedType.js +8 -0
- package/lib/helper/extras/Download.js +45 -0
- package/lib/helper/extras/PlaywrightLocator.js +7 -107
- 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 +158 -16
- package/lib/mocha/cli.js +19 -1
- package/lib/mocha/factory.js +11 -1
- 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 +457 -0
- package/lib/plugin/analyze.js +9 -9
- package/lib/plugin/auth.js +5 -4
- package/lib/plugin/browser.js +77 -0
- package/lib/plugin/expose.js +159 -0
- package/lib/plugin/heal.js +47 -3
- package/lib/plugin/junitReporter.js +303 -0
- package/lib/plugin/pageInfo.js +54 -52
- package/lib/plugin/pause.js +131 -0
- package/lib/plugin/pauseOnFail.js +11 -33
- package/lib/plugin/retryFailedStep.js +43 -32
- package/lib/plugin/screencast.js +289 -0
- package/lib/plugin/screenshot.js +558 -0
- package/lib/plugin/screenshotOnFail.js +9 -170
- package/lib/plugin/stepTimeout.js +3 -2
- package/lib/recorder.js +1 -1
- package/lib/rerun.js +2 -1
- package/lib/result.js +2 -1
- package/lib/step/base.js +10 -9
- package/lib/step/comment.js +2 -2
- package/lib/step/config.js +15 -2
- package/lib/step/helper.js +4 -4
- package/lib/step/meta.js +3 -3
- package/lib/step/record.js +5 -5
- package/lib/store.js +72 -3
- package/lib/translation.js +2 -1
- package/lib/utils/loaderCheck.js +28 -0
- 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/typescript.js +188 -23
- package/lib/utils.js +77 -3
- package/lib/workers.js +65 -40
- package/package.json +35 -30
- package/typings/index.d.ts +119 -8
- package/typings/promiseBasedTypes.d.ts +3158 -6065
- package/typings/types.d.ts +3453 -6494
- 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/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/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/helper/Mochawesome.js +0 -96
- package/lib/helper/extras/PlaywrightReactVueLocator.js +0 -52
- package/lib/helper/extras/React.js +0 -65
- 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/docs/retry.md
ADDED
|
@@ -0,0 +1,311 @@
|
|
|
1
|
+
---
|
|
2
|
+
permalink: /retry
|
|
3
|
+
title: Retry Mechanisms
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Retry Mechanisms
|
|
7
|
+
|
|
8
|
+
CodeceptJS provides flexible retry mechanisms to handle flaky tests. Use retries when dealing with unstable environments, network delays, or timing issues — not to mask bugs in your code.
|
|
9
|
+
|
|
10
|
+
## Retry Levels
|
|
11
|
+
|
|
12
|
+
* [helper retries](#helper-retries) — happen low level on browser interaction (resolves rendering of elements)
|
|
13
|
+
* [failed step retries](#failed-step-retries) — performed by CodeceptJS on step fail
|
|
14
|
+
* [manual step retries](#manual-step-retries) — on known flaky steps
|
|
15
|
+
* [multiple steps retry](#multiple-steps-retry) — retry a group of steps together as a single operation
|
|
16
|
+
* [self-healing steps](#self-healing-steps) — AI-powered recovery that continues tests without changing test code
|
|
17
|
+
* [scenario retry](#scenario-retry) — retry entire test scenarios on failure
|
|
18
|
+
* [feature retry](#feature-retry) — retry all scenarios within a feature
|
|
19
|
+
* [hook retries](#hook-retries) — retry `Before`/`After` hooks on failure
|
|
20
|
+
|
|
21
|
+
## Helper Retries
|
|
22
|
+
|
|
23
|
+
Plawright has a built-in retry mechanism for element interactions. When you call `I.click('Button')`, after the element is located Playwright keeps retrying until it is actionable — up to `timeout` (default 5s).
|
|
24
|
+
|
|
25
|
+
> WebDriver has a different auto-retry option: [smartWait](/webdriver#smartwait)
|
|
26
|
+
|
|
27
|
+
Even though the handle exists (from `.all()`), Playwright still waits for it to become visible, stable (not mid-animation), enabled, not covered by an overlay/modal, and not rerendering.
|
|
28
|
+
|
|
29
|
+
```js
|
|
30
|
+
helpers: {
|
|
31
|
+
Playwright: {
|
|
32
|
+
timeout: 5000, // retry the action until the element is actionable
|
|
33
|
+
waitForAction: 100 // fixed pause AFTER click/doubleClick/pressKey
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
What each setting does:
|
|
39
|
+
|
|
40
|
+
```
|
|
41
|
+
find element (no wait — fails instantly if locator matches nothing)
|
|
42
|
+
→ wait up to `timeout` for it to become actionable ← timeout
|
|
43
|
+
→ perform action
|
|
44
|
+
→ sleep `waitForAction` ms ← waitForAction (settle pause, not a wait)
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
`timeout` covers the action. If the locator matches nothing yet, the step fails immediately. Use [Failed Step Retries](#failed-step-retries) to cover that gap.
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
## Failed Step Retries
|
|
51
|
+
|
|
52
|
+
CodeceptJS retries all failed steps by default by using the `retryFailedStep` plugin.
|
|
53
|
+
|
|
54
|
+
```js
|
|
55
|
+
plugins: {
|
|
56
|
+
retryFailedStep: {
|
|
57
|
+
enabled: true,
|
|
58
|
+
retries: 3
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Steps matching `amOnPage`, `wait*`, `send*`, `execute*`, `run*`, `have*` are skipped by default.
|
|
64
|
+
|
|
65
|
+
When a scenario has its own retries, step retries are disabled by default (`deferToScenarioRetries: true`). This prevents excessive execution time:
|
|
66
|
+
|
|
67
|
+
```js
|
|
68
|
+
Scenario('test', { retries: 2 }, ({ I }) => {
|
|
69
|
+
I.click('Button') // step retries disabled; scenario retries run instead
|
|
70
|
+
})
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
To disable step retries for a specific test:
|
|
74
|
+
|
|
75
|
+
```js
|
|
76
|
+
Scenario('manual retries only', { disableRetryFailedStep: true }, ({ I }) => {
|
|
77
|
+
I.click('Button', step.retry(5))
|
|
78
|
+
})
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
Defaults: `minTimeout: 150`, `factor: 1.5`, `maxTimeout: 10000`.
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
> See [plugin reference](/plugins/retry-failed-step) for more options
|
|
85
|
+
|
|
86
|
+
Retries are calculated via this formula:
|
|
87
|
+
|
|
88
|
+
```
|
|
89
|
+
gap(N) = min(minTimeout × factor^(N-1), maxTimeout)
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
Practically if step fails it will trigger a retry with increasing delay until `maxTimeout` is reached:
|
|
93
|
+
|
|
94
|
+
```
|
|
95
|
+
retries: 2 => 0.15s-0.4s (150,225ms)
|
|
96
|
+
retries: 3 => 0.15s-0.7s (150,225,338ms)
|
|
97
|
+
retries: 3, minTimeout: 1000 => 1s-4.75s (1s,1.5s,2.25s)
|
|
98
|
+
retries: 3, minTimeout: 1000, factor: 2 => 1s-7s (1s,2s,4s)
|
|
99
|
+
retries: 5, minTimeout: 1000, factor: 2 => 1s-25s (1s,2s,4s,8s,10s)
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
Playwright `timeout` adds to each attempt only when the element is found:
|
|
103
|
+
|
|
104
|
+
- `Playwright.timeout: 5000`
|
|
105
|
+
- `retries: 2, minTimeout: 1000`
|
|
106
|
+
|
|
107
|
+
```
|
|
108
|
+
element not found => 0 + (1s+1s) = 2s
|
|
109
|
+
element found but not interactable => 3×5s + (1s+1s) = 17s
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
## Manual Step Retries
|
|
113
|
+
|
|
114
|
+
Retry a specific step known to be flaky:
|
|
115
|
+
|
|
116
|
+
```js
|
|
117
|
+
import step from 'codeceptjs/steps'
|
|
118
|
+
|
|
119
|
+
Scenario('checkout', ({ I }) => {
|
|
120
|
+
I.amOnPage('/cart')
|
|
121
|
+
I.click('Proceed to Checkout', step.retry(5)) // retry up to 5 times
|
|
122
|
+
I.see('Payment')
|
|
123
|
+
})
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
Configure timing with exponential backoff:
|
|
127
|
+
|
|
128
|
+
```js
|
|
129
|
+
I.click('Submit', step.retry({
|
|
130
|
+
retries: 3,
|
|
131
|
+
minTimeout: 1000, // wait 1 second before first retry
|
|
132
|
+
maxTimeout: 5000, // max 5 seconds between retries
|
|
133
|
+
factor: 1.5 // exponential backoff multiplier
|
|
134
|
+
}))
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
Pass `0` for infinite retries.
|
|
138
|
+
|
|
139
|
+
## Multiple Steps Retry
|
|
140
|
+
|
|
141
|
+
Retry a group of steps together as a single operation:
|
|
142
|
+
|
|
143
|
+
```js
|
|
144
|
+
import { retryTo } from 'codeceptjs/effects'
|
|
145
|
+
|
|
146
|
+
await retryTo(() => {
|
|
147
|
+
I.click('Load More')
|
|
148
|
+
I.see('New Content')
|
|
149
|
+
}, 3)
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
If any step inside fails, the entire block retries. Use this for sequences that must succeed together — switching into an iframe and filling a form, for example.
|
|
153
|
+
|
|
154
|
+
**Learn more:** [Effects](/effects#retryto)
|
|
155
|
+
|
|
156
|
+
## Self-Healing Steps
|
|
157
|
+
|
|
158
|
+
When a step fails, a healing recipe runs recovery actions and continues the test — without touching test code. With AI healing enabled:
|
|
159
|
+
|
|
160
|
+
```js
|
|
161
|
+
Scenario('checkout', ({ I }) => {
|
|
162
|
+
I.click('Proceed to Checkout')
|
|
163
|
+
I.see('Payment')
|
|
164
|
+
})
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
- `I.click('Proceed to Checkout')` fails — button was renamed or moved
|
|
168
|
+
- failed step, error message, and page HTML are sent to an LLM
|
|
169
|
+
- AI scans page elements and suggests valid replacement actions
|
|
170
|
+
- CodeceptJS executes the suggestions until one succeeds
|
|
171
|
+
- test continues with `I.see('Payment')`
|
|
172
|
+
|
|
173
|
+
Run with `--ai` to activate:
|
|
174
|
+
|
|
175
|
+
```bash
|
|
176
|
+
npx codeceptjs run --ai
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
You can also write custom recipes for non-UI failures — network errors, data glitches, UI migrations.
|
|
180
|
+
|
|
181
|
+
**Learn more:** [Self-Healing Tests](/heal), [AI Configuration](/ai)
|
|
182
|
+
|
|
183
|
+
## Scenario Retry
|
|
184
|
+
|
|
185
|
+
Retry an entire test when it fails:
|
|
186
|
+
|
|
187
|
+
```js
|
|
188
|
+
Scenario('API integration', { retries: 3 }, ({ I }) => {
|
|
189
|
+
I.sendGetRequest('/api/users')
|
|
190
|
+
I.seeResponseCodeIs(200)
|
|
191
|
+
})
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
Retry all scenarios globally, or by grep pattern:
|
|
195
|
+
|
|
196
|
+
```js
|
|
197
|
+
export const config = {
|
|
198
|
+
retry: [
|
|
199
|
+
{ Scenario: 3, grep: 'API' }, // retry scenarios containing "API" 3 times
|
|
200
|
+
{ Scenario: 5, grep: '@flaky' } // retry @flaky-tagged scenarios 5 times
|
|
201
|
+
]
|
|
202
|
+
}
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
## Feature Retry
|
|
206
|
+
|
|
207
|
+
Retry all scenarios within a feature:
|
|
208
|
+
|
|
209
|
+
```js
|
|
210
|
+
Feature('Payment Processing', { retries: 2 })
|
|
211
|
+
|
|
212
|
+
Scenario('credit card payment', ({ I }) => { ... }) // retries 2 times
|
|
213
|
+
Scenario('paypal payment', ({ I }) => { ... }) // retries 2 times
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
Or target features by pattern in config:
|
|
217
|
+
|
|
218
|
+
```js
|
|
219
|
+
export const config = {
|
|
220
|
+
retry: [
|
|
221
|
+
{ Feature: 3, grep: 'Integration' }
|
|
222
|
+
]
|
|
223
|
+
}
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
## Hook Retries
|
|
227
|
+
|
|
228
|
+
Retry `Before`/`After` hooks when they fail:
|
|
229
|
+
|
|
230
|
+
```js
|
|
231
|
+
Before(({ I }) => {
|
|
232
|
+
I.amOnPage('/')
|
|
233
|
+
}).retry(2)
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
Set per feature:
|
|
237
|
+
|
|
238
|
+
```js
|
|
239
|
+
Feature('My Suite', {
|
|
240
|
+
retryBefore: 2,
|
|
241
|
+
retryAfter: 1,
|
|
242
|
+
retryBeforeSuite: 3,
|
|
243
|
+
retryAfterSuite: 1
|
|
244
|
+
})
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
Or globally:
|
|
248
|
+
|
|
249
|
+
```js
|
|
250
|
+
export const config = {
|
|
251
|
+
retry: [
|
|
252
|
+
{ BeforeSuite: 2, Before: 1, After: 1 }
|
|
253
|
+
]
|
|
254
|
+
}
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
## Retry Priority
|
|
258
|
+
|
|
259
|
+
When multiple retry configurations exist, higher-priority retries take precedence:
|
|
260
|
+
|
|
261
|
+
| Priority | Type | Description |
|
|
262
|
+
|----------|------|-------------|
|
|
263
|
+
| **Highest** | Manual Step (`step.retry()`) | Explicit retries in test code |
|
|
264
|
+
| | Automatic Step | `retryFailedStep` plugin |
|
|
265
|
+
| | Multiple Steps (`retryTo`) | Retry groups of steps together |
|
|
266
|
+
| | Scenario Config | Retry entire scenarios |
|
|
267
|
+
| | Feature Config | Retry all scenarios in a feature |
|
|
268
|
+
| **Lowest** | Hook Config | Retry failed hooks |
|
|
269
|
+
|
|
270
|
+
`retryTo` operates independently from step-level retries. If a step inside `retryTo` fails, the entire block retries.
|
|
271
|
+
|
|
272
|
+
## Best Practices
|
|
273
|
+
|
|
274
|
+
1. **Understand helper retries first** — Playwright/Puppeteer/WebDriver already retry actions internally
|
|
275
|
+
2. **Start with scenario retries** — simpler and less likely to cause issues
|
|
276
|
+
3. **Use manual retries for known flaky steps** — most predictable behavior
|
|
277
|
+
4. **Enable `deferToScenarioRetries`** — prevents excessive retries (default)
|
|
278
|
+
5. **Don't over-retry** — if tests fail consistently, fix the root cause
|
|
279
|
+
6. **Use grep patterns** — apply retries only where needed
|
|
280
|
+
7. **Retry timeouts, not bugs** — retries handle environmental issues, not code defects
|
|
281
|
+
8. **Consider healing for complex recovery** — see [Self-Healing Tests](/heal)
|
|
282
|
+
|
|
283
|
+
## Troubleshooting
|
|
284
|
+
|
|
285
|
+
### Tests running too long
|
|
286
|
+
|
|
287
|
+
- Confirm `deferToScenarioRetries: true` (the default)
|
|
288
|
+
- Reduce retry counts
|
|
289
|
+
- Use `grep` patterns to target specific tests
|
|
290
|
+
- Add problematic steps to `ignoredSteps`
|
|
291
|
+
|
|
292
|
+
### Retries not working
|
|
293
|
+
|
|
294
|
+
1. Check configuration syntax
|
|
295
|
+
2. Check the priority table — a higher-priority retry may be overriding
|
|
296
|
+
3. Confirm `disableRetryFailedStep: true` is not set on the scenario
|
|
297
|
+
4. Confirm the step isn't in `ignoredSteps`
|
|
298
|
+
|
|
299
|
+
Debug with:
|
|
300
|
+
|
|
301
|
+
```bash
|
|
302
|
+
DEBUG_RETRY_PLUGIN=1 npx codeceptjs run
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
### `step.retry()` is undefined
|
|
306
|
+
|
|
307
|
+
Import `step` from codeceptjs:
|
|
308
|
+
|
|
309
|
+
```js
|
|
310
|
+
import step from 'codeceptjs/steps'
|
|
311
|
+
```
|
package/docs/secrets.md
ADDED
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
# Secrets
|
|
2
|
+
|
|
3
|
+
It is possible to **mask out sensitive data** when passing it to steps. This is important when filling password fields, or sending secure keys to API endpoint. CodeceptJS provides two approaches for masking sensitive data:
|
|
4
|
+
|
|
5
|
+
## 1. Using the `secret()` Function
|
|
6
|
+
|
|
7
|
+
Wrap data in `secret` function to mask sensitive values in output and logs.
|
|
8
|
+
|
|
9
|
+
For basic string `secret` just wrap a value into a string:
|
|
10
|
+
|
|
11
|
+
```js
|
|
12
|
+
I.fillField('password', secret('123456'))
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
When executed it will be printed like this:
|
|
16
|
+
|
|
17
|
+
```
|
|
18
|
+
I fill field "password" "*****"
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
**Other Examples**
|
|
22
|
+
|
|
23
|
+
```js
|
|
24
|
+
I.fillField('password', secret('123456'))
|
|
25
|
+
I.append('password', secret('123456'))
|
|
26
|
+
I.type('password', secret('123456'))
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
For an object, which can be a payload to POST request, specify which fields should be masked:
|
|
30
|
+
|
|
31
|
+
```js
|
|
32
|
+
I.sendPostRequest(
|
|
33
|
+
'/login',
|
|
34
|
+
secret(
|
|
35
|
+
{
|
|
36
|
+
name: 'davert',
|
|
37
|
+
password: '123456',
|
|
38
|
+
},
|
|
39
|
+
'password',
|
|
40
|
+
),
|
|
41
|
+
)
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
The object created from `secret` is as Proxy to the object passed in. When printed password will be replaced with \*\*\*\*.
|
|
45
|
+
|
|
46
|
+
> ⚠️ Only direct properties of the object can be masked via `secret`
|
|
47
|
+
|
|
48
|
+
## 2. Global Sensitive Data Masking
|
|
49
|
+
|
|
50
|
+
CodeceptJS can automatically mask sensitive data in all output (logs, steps, debug messages, errors) using configurable patterns. This feature uses the `maskSensitiveData` configuration option.
|
|
51
|
+
|
|
52
|
+
### Basic Usage (Boolean)
|
|
53
|
+
|
|
54
|
+
Enable basic masking with predefined patterns:
|
|
55
|
+
|
|
56
|
+
```js
|
|
57
|
+
// codecept.conf.js
|
|
58
|
+
export const config = {
|
|
59
|
+
// ... other config
|
|
60
|
+
maskSensitiveData: true,
|
|
61
|
+
}
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
This will mask common sensitive data patterns like:
|
|
65
|
+
|
|
66
|
+
- Authorization headers
|
|
67
|
+
- API keys
|
|
68
|
+
- Passwords
|
|
69
|
+
- Tokens
|
|
70
|
+
- Client secrets
|
|
71
|
+
|
|
72
|
+
### Advanced Usage (Custom Patterns)
|
|
73
|
+
|
|
74
|
+
Define your own masking patterns:
|
|
75
|
+
|
|
76
|
+
```js
|
|
77
|
+
// codecept.conf.js
|
|
78
|
+
export const config = {
|
|
79
|
+
// ... other config
|
|
80
|
+
maskSensitiveData: {
|
|
81
|
+
enabled: true,
|
|
82
|
+
patterns: [
|
|
83
|
+
{
|
|
84
|
+
name: 'Email',
|
|
85
|
+
regex: /(\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b)/gi,
|
|
86
|
+
mask: '[MASKED_EMAIL]',
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
name: 'Credit Card',
|
|
90
|
+
regex: /\b(?:\d{4}[- ]?){3}\d{4}\b/g,
|
|
91
|
+
mask: '[MASKED_CARD]',
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
name: 'Phone Number',
|
|
95
|
+
regex: /(\+?1[-.\s]?)?\(?([0-9]{3})\)?[-.\s]?([0-9]{3})[-.\s]?([0-9]{4})/g,
|
|
96
|
+
mask: '[MASKED_PHONE]',
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
name: 'SSN',
|
|
100
|
+
regex: /\b\d{3}-\d{2}-\d{4}\b/g,
|
|
101
|
+
mask: '[MASKED_SSN]',
|
|
102
|
+
},
|
|
103
|
+
],
|
|
104
|
+
},
|
|
105
|
+
}
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
### Pattern Configuration
|
|
109
|
+
|
|
110
|
+
Each custom pattern object should have:
|
|
111
|
+
|
|
112
|
+
- `name`: A descriptive name for the pattern
|
|
113
|
+
- `regex`: A JavaScript regular expression to match the sensitive data
|
|
114
|
+
- `mask`: The replacement string to show instead of the sensitive data
|
|
115
|
+
|
|
116
|
+
### Examples
|
|
117
|
+
|
|
118
|
+
With the above configuration:
|
|
119
|
+
|
|
120
|
+
**Input:**
|
|
121
|
+
|
|
122
|
+
```
|
|
123
|
+
User email: john.doe@company.com
|
|
124
|
+
Credit card: 4111 1111 1111 1111
|
|
125
|
+
Phone: +1-555-123-4567
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
**Output:**
|
|
129
|
+
|
|
130
|
+
```
|
|
131
|
+
User email: [MASKED_EMAIL]
|
|
132
|
+
Credit card: [MASKED_CARD]
|
|
133
|
+
Phone: [MASKED_PHONE]
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
### Where Masking Applies
|
|
137
|
+
|
|
138
|
+
Global sensitive data masking is applied to:
|
|
139
|
+
|
|
140
|
+
- Step descriptions and output
|
|
141
|
+
- Debug messages (`--debug` mode)
|
|
142
|
+
- Log messages (`--verbose` mode)
|
|
143
|
+
- Error messages
|
|
144
|
+
- Success messages
|
|
145
|
+
|
|
146
|
+
> ⚠️ Direct `console.log()` calls in helper functions are not masked. Use CodeceptJS output functions instead.
|
|
147
|
+
|
|
148
|
+
### Combining Both Approaches
|
|
149
|
+
|
|
150
|
+
You can use both `secret()` function and global masking together. The `secret()` function is applied first, then global patterns are applied to the remaining output.
|
package/docs/sessions.md
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
---
|
|
2
|
+
permalink: /sessions
|
|
3
|
+
title: Multiple Sessions
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Multiple Sessions
|
|
7
|
+
|
|
8
|
+
CodeceptJS can run several browser sessions in a single test. This is useful for testing real-time features like chat, notifications, or multi-user workflows.
|
|
9
|
+
|
|
10
|
+
```js
|
|
11
|
+
Scenario('two users chat', ({ I }) => {
|
|
12
|
+
I.amOnPage('/chat')
|
|
13
|
+
I.fillField('name', 'davert')
|
|
14
|
+
I.click('Sign In')
|
|
15
|
+
I.see('Hello, davert')
|
|
16
|
+
|
|
17
|
+
session('john', () => {
|
|
18
|
+
I.amOnPage('/chat')
|
|
19
|
+
I.fillField('name', 'john')
|
|
20
|
+
I.click('Sign In')
|
|
21
|
+
I.see('Hello, john')
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
// back in default session (davert)
|
|
25
|
+
I.fillField('message', 'Hi, john')
|
|
26
|
+
I.see('me: Hi, john', '.messages')
|
|
27
|
+
|
|
28
|
+
session('john', () => {
|
|
29
|
+
I.see('davert: Hi, john', '.messages')
|
|
30
|
+
})
|
|
31
|
+
})
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Switch back to a session at any point by using the same name.
|
|
35
|
+
|
|
36
|
+
## Session Configuration
|
|
37
|
+
|
|
38
|
+
Override the browser config for a specific session:
|
|
39
|
+
|
|
40
|
+
```js
|
|
41
|
+
session('mobile', { browser: 'webkit', viewport: { width: 375, height: 812 } }, () => {
|
|
42
|
+
I.amOnPage('/')
|
|
43
|
+
I.see('Mobile View')
|
|
44
|
+
})
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Opening Sessions Without Switching
|
|
48
|
+
|
|
49
|
+
Open sessions in the background without switching to them immediately:
|
|
50
|
+
|
|
51
|
+
```js
|
|
52
|
+
Scenario('multi-user test', ({ I }) => {
|
|
53
|
+
session('john')
|
|
54
|
+
session('mary')
|
|
55
|
+
session('jane')
|
|
56
|
+
|
|
57
|
+
I.amOnPage('/')
|
|
58
|
+
|
|
59
|
+
session('mary', () => {
|
|
60
|
+
I.amOnPage('/login')
|
|
61
|
+
})
|
|
62
|
+
})
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## Returning Values
|
|
66
|
+
|
|
67
|
+
`session` can return a value for use in the main scenario:
|
|
68
|
+
|
|
69
|
+
```js
|
|
70
|
+
const profileName = await session('john', () => {
|
|
71
|
+
I.amOnPage('/profile')
|
|
72
|
+
return I.grabTextFrom({ css: 'h1' })
|
|
73
|
+
})
|
|
74
|
+
I.fillField('Recipient', profileName)
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## Notes
|
|
78
|
+
|
|
79
|
+
- Sessions can use the `I` object, page objects, and any other objects declared for the scenario
|
|
80
|
+
- `within` can be used inside a session, but `session` cannot be called from inside `within`
|
package/docs/shadow.md
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
---
|
|
2
|
+
permalink: /shadow
|
|
3
|
+
title: Locating Shadow Dom Elements
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Locating Shadow Dom Elements
|
|
7
|
+
|
|
8
|
+
> ℹ Shadow DOM locators is supported only in WebDriver helper
|
|
9
|
+
|
|
10
|
+
Shadow DOM is one of the key browser features that make up web components. Web components are a really great way to build reusable elements, and are able to scale all the way up to complete web applications. Style encapsulation, the feature that gives shadow DOM it's power, has been a bit of a pain when it comes to E2E or UI testing. Things just got a little easier though, as CodeceptJS introduced built-in support for shadow DOM via locators of type `shadow`. Let's dig into what they're all about.
|
|
11
|
+
|
|
12
|
+
Generated HTML code may often look like this (ref: [Salesforce's Lighting Web Components](https://github.com/salesforce/lwc)):
|
|
13
|
+
|
|
14
|
+
```js
|
|
15
|
+
<body>
|
|
16
|
+
<my-app>
|
|
17
|
+
<recipe-hello>
|
|
18
|
+
<button>Click Me!</button>
|
|
19
|
+
</recipe-hello>
|
|
20
|
+
<recipe-hello-binding>
|
|
21
|
+
<ui-input>
|
|
22
|
+
<input type="text" class="input">
|
|
23
|
+
</ui-input>
|
|
24
|
+
</recipe-hello-binding>
|
|
25
|
+
</my-app>
|
|
26
|
+
</body>
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
This uses custom elements, `my-app`, `recipe-hello`, `recipe-hello-binding` and `ui-input`. It's quite common that clickable elements are not actual `a` or `button` elements but custom elements. This way `I.click('Click Me!');` won't work, as well as `fillField('.input', 'value)`. Finding a correct locator for such cases turns to be almost impossible until `shadow` element support is added to CodeceptJS.
|
|
30
|
+
|
|
31
|
+
## Locate Shadow Dom
|
|
32
|
+
|
|
33
|
+
For Web Components or [Salesforce's Lighting Web Components](https://github.com/salesforce/lwc) with Shadow DOM's, a special `shadow` locator is available. It allows to select an element by its shadow dom sequences and sequences are defined as an Array of `elements`. Elements defined in the array of `elements` must be in the ordered the shadow elements appear in the DOM.
|
|
34
|
+
|
|
35
|
+
```js
|
|
36
|
+
{ shadow: ['my-app', 'recipe-hello', 'button'] }
|
|
37
|
+
{ shadow: ['my-app', 'recipe-hello-binding', 'ui-input', 'input.input'] }
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
In WebDriver, you can use shadow locators in any method where locator is required.
|
|
41
|
+
|
|
42
|
+
For example, to fill value in `input` field or to click the `Click Me!` button, in above HTML code:
|
|
43
|
+
|
|
44
|
+
```js
|
|
45
|
+
I.fillField({ shadow: ['my-app', 'recipe-hello-binding', 'ui-input', 'input.input'] }, 'value');
|
|
46
|
+
I.click({ shadow: ['my-app', 'recipe-hello', 'button'] });
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Example
|
|
50
|
+
|
|
51
|
+
```js
|
|
52
|
+
Feature('Shadow Dom Locators');
|
|
53
|
+
|
|
54
|
+
Scenario('should fill input field within shadow elements', ({I}) => {
|
|
55
|
+
|
|
56
|
+
// navigate to LWC webpage containing shadow dom
|
|
57
|
+
I.amOnPage('https://recipes.lwc.dev/');
|
|
58
|
+
|
|
59
|
+
// click Click Me! button
|
|
60
|
+
I.click({ shadow: ['my-app', 'recipe-hello', 'button'] });
|
|
61
|
+
|
|
62
|
+
// fill the input field
|
|
63
|
+
I.fillField({ shadow: ['my-app', 'recipe-hello-binding', 'ui-input', 'input.input'] }, 'value');
|
|
64
|
+
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
```
|
package/docs/store.md
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
---
|
|
2
|
+
permalink: /store
|
|
3
|
+
title: Store
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Store
|
|
7
|
+
|
|
8
|
+
`store` is a global object that holds the state of the current run — which test and step are executing, which modes are enabled, and where the project lives on disk. Listeners, plugins, and helpers read it to find out *where in the lifecycle* they are without threading that information through every call.
|
|
9
|
+
|
|
10
|
+
```js
|
|
11
|
+
import { store } from 'codeceptjs'
|
|
12
|
+
|
|
13
|
+
event.dispatcher.on(event.step.before, () => {
|
|
14
|
+
if (store.dryRun) return // skip side effects on a dry run
|
|
15
|
+
console.log(`running ${store.currentTest?.title}`)
|
|
16
|
+
})
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
It is a single shared object for the whole process. Read from it freely; write to it only if you are deliberately driving execution state (see [Writing to the store](#writing-to-the-store)).
|
|
20
|
+
|
|
21
|
+
## Fields
|
|
22
|
+
|
|
23
|
+
### Required
|
|
24
|
+
|
|
25
|
+
Set once when the runner starts and immutable afterwards. Resolved from the test root, falling back to the legacy `global.codecept_dir` / `global.output_dir`.
|
|
26
|
+
|
|
27
|
+
| Field | Type | What it is |
|
|
28
|
+
| --- | --- | --- |
|
|
29
|
+
| `store.codeceptDir` | `string` | root directory of the tests |
|
|
30
|
+
| `store.outputDir` | `string` | resolved output directory for artifacts |
|
|
31
|
+
| `store.workerMode` | `boolean` | `true` when running inside a [parallel](/parallel) worker |
|
|
32
|
+
|
|
33
|
+
### Session config
|
|
34
|
+
|
|
35
|
+
Set at session start and stable for that session. They reflect how the run was invoked.
|
|
36
|
+
|
|
37
|
+
| Field | Type | What it is |
|
|
38
|
+
| --- | --- | --- |
|
|
39
|
+
| `store.debugMode` | `boolean` | run started with `--debug` |
|
|
40
|
+
| `store.timeouts` | `boolean` | [timeouts](/timeouts) are enabled |
|
|
41
|
+
| `store.autoRetries` | `boolean` | step auto-retries are active (the `tryTo` [effect](/effects) turns this off while it runs) |
|
|
42
|
+
| `store.dryRun` | `boolean` | run is a `--dry-run`; steps must not cause side effects |
|
|
43
|
+
| `store.featureOnly` | `boolean` | a `Feature.only()` was used |
|
|
44
|
+
| `store.scenarioOnly` | `boolean` | a `Scenario.only()` was used |
|
|
45
|
+
| `store.maskSensitiveData` | `boolean \| object` | mask [secret](/secrets) values in output |
|
|
46
|
+
| `store.noGlobals` | `boolean` | `noGlobals` mode — the user imports everything instead of relying on globals |
|
|
47
|
+
|
|
48
|
+
### State
|
|
49
|
+
|
|
50
|
+
Changes constantly as the run progresses. The most useful fields for plugins and listeners:
|
|
51
|
+
|
|
52
|
+
| Field | Type | What it is |
|
|
53
|
+
| --- | --- | --- |
|
|
54
|
+
| `store.onPause` | `boolean` | execution is paused inside [`pause()`](/basics#pause) |
|
|
55
|
+
| `store.currentTest` | `Test \| null` | the running test, or `null` between tests |
|
|
56
|
+
| `store.currentStep` | `Step \| null` | the running step, or `null` |
|
|
57
|
+
| `store.currentSuite` | `Suite \| null` | the running suite, or `null` between suites |
|
|
58
|
+
| `store.tsFileMapping` | `Map \| null` | TypeScript source-map lookup, when running `.ts` tests |
|
|
59
|
+
|
|
60
|
+
`currentTest`, `currentStep`, and `currentSuite` carry the same objects passed to lifecycle events — see the [test and step object](/architecture#test-object) fields.
|
|
61
|
+
|
|
62
|
+
## Reading the store
|
|
63
|
+
|
|
64
|
+
A typical use is gating expensive or unsafe work by run mode:
|
|
65
|
+
|
|
66
|
+
```js
|
|
67
|
+
import { store, recorder } from 'codeceptjs'
|
|
68
|
+
|
|
69
|
+
event.dispatcher.on(event.test.before, () => {
|
|
70
|
+
if (store.dryRun || store.workerMode) return
|
|
71
|
+
|
|
72
|
+
recorder.add('seed fixture data', async () => {
|
|
73
|
+
await api.post('/users', { name: 'john' })
|
|
74
|
+
})
|
|
75
|
+
})
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
`store.currentTest` is the simplest way to attach context to the current test from anywhere — for example tagging an artifact:
|
|
79
|
+
|
|
80
|
+
```js
|
|
81
|
+
import { store } from 'codeceptjs'
|
|
82
|
+
|
|
83
|
+
store.currentTest?.artifacts.push({ name: 'log', path: '/tmp/run.log' })
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## Writing to the store
|
|
87
|
+
|
|
88
|
+
CodeceptJS keeps the state fields current for you — the built-in [`store` listener](https://github.com/codeceptjs/CodeceptJS/blob/master/lib/listener/store.js) sets `currentSuite` and `currentTest` around each suite and test. You rarely need to write to it.
|
|
89
|
+
|
|
90
|
+
Write only when you are deliberately driving execution — for example, a tool that opens `pause()` sets `store.onPause`. The required fields (`codeceptDir`, `outputDir`) are locked after `store.initialize()` and cannot be reassigned.
|
|
91
|
+
|
|
92
|
+
---
|
|
93
|
+
|
|
94
|
+
**See also:** [Architecture](/architecture) · [Extending CodeceptJS](/hooks) · [Events](/architecture#events)
|