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/tutorial.md
ADDED
|
@@ -0,0 +1,323 @@
|
|
|
1
|
+
---
|
|
2
|
+
permalink: /tutorial
|
|
3
|
+
title: CodeceptJS Complete Tutorial
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Tutorial: Testing a Checkout Page
|
|
7
|
+
|
|
8
|
+
**[CodeceptJS](https://codecept.io) is a popular open-source end-to-end testing framework** for JavaScript. It is designed to make web tests readable and easy to maintain by writing them as a linear scenario of user actions. By default it drives the browser with **[Playwright](https://playwright.dev)**, but the same tests can run via WebDriver, Puppeteer, or Appium without changes.
|
|
9
|
+
|
|
10
|
+
In this tutorial we write a real, runnable test for the **[Bootstrap checkout example](https://getbootstrap.com/docs/4.0/examples/checkout/)** — a public page with a billing and payment form. By the end you will have a clean test and a reusable page object.
|
|
11
|
+
|
|
12
|
+
## Install CodeceptJS
|
|
13
|
+
|
|
14
|
+
You need Node.js (and npm) installed. Check with:
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
node --version
|
|
18
|
+
npm --version
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Create a new folder, then install CodeceptJS together with Playwright:
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
npm init -y
|
|
25
|
+
npm install codeceptjs playwright --save-dev
|
|
26
|
+
npx playwright install --with-deps
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
`npx playwright install` downloads the Chromium, Firefox, and WebKit browsers; `--with-deps` also installs the system libraries they need.
|
|
30
|
+
|
|
31
|
+
Now scaffold the project:
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
npx codeceptjs init
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
`init` runs a short wizard. Accept the defaults — when asked for the **base URL** enter `https://getbootstrap.com`, and name the first test **Checkout**. This creates:
|
|
38
|
+
|
|
39
|
+
```
|
|
40
|
+
.
|
|
41
|
+
├── codecept.conf.js
|
|
42
|
+
├── package.json
|
|
43
|
+
└── Checkout_test.js
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
`codecept.conf.js` holds the project configuration. Because CodeceptJS 4.x uses **ES modules**, the config and tests use `import`/`export` syntax — `init` sets `"type": "module"` in `package.json` for you.
|
|
47
|
+
|
|
48
|
+
Open `codecept.conf.js`. The two settings that matter here are the helper and the base URL:
|
|
49
|
+
|
|
50
|
+
```js
|
|
51
|
+
import { setHeadlessWhen } from '@codeceptjs/configure'
|
|
52
|
+
|
|
53
|
+
// show the browser locally, run headless on CI
|
|
54
|
+
setHeadlessWhen(process.env.CI)
|
|
55
|
+
|
|
56
|
+
export const config = {
|
|
57
|
+
tests: './*_test.js',
|
|
58
|
+
output: './output',
|
|
59
|
+
helpers: {
|
|
60
|
+
Playwright: {
|
|
61
|
+
url: 'https://getbootstrap.com',
|
|
62
|
+
browser: 'chromium',
|
|
63
|
+
},
|
|
64
|
+
},
|
|
65
|
+
}
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## Your First Test
|
|
69
|
+
|
|
70
|
+
Open `Checkout_test.js`:
|
|
71
|
+
|
|
72
|
+
```js
|
|
73
|
+
Feature('Checkout');
|
|
74
|
+
|
|
75
|
+
Scenario('test something', ({ I }) => {
|
|
76
|
+
});
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
A test lives inside a `Scenario` block. Let's open the checkout page:
|
|
80
|
+
|
|
81
|
+
```js
|
|
82
|
+
Feature('Checkout');
|
|
83
|
+
|
|
84
|
+
Scenario('test something', ({ I }) => {
|
|
85
|
+
I.amOnPage('/docs/4.0/examples/checkout/');
|
|
86
|
+
});
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
`I.amOnPage()` navigates the browser. Because the path is relative, it is appended to the base URL from the config — keep the base URL in config so you can switch between staging and production without touching tests.
|
|
90
|
+
|
|
91
|
+
But you may be wondering...
|
|
92
|
+
|
|
93
|
+
### What is `I`?
|
|
94
|
+
|
|
95
|
+
In CodeceptJS the `I` object is the **actor** — it represents the user performing actions. It exposes methods (called *actions*) that simulate interactions with the app:
|
|
96
|
+
|
|
97
|
+
- `I.amOnPage(url)` — navigate to a URL
|
|
98
|
+
- `I.click(locator)` — click an element
|
|
99
|
+
- `I.fillField(field, value)` — type into an input
|
|
100
|
+
- `I.selectOption(select, option)` — choose an option in a dropdown
|
|
101
|
+
- `I.checkOption(locator)` — tick a checkbox or radio
|
|
102
|
+
- `I.see(text)` — assert that text is visible
|
|
103
|
+
- `I.seeInField(field, value)` — assert an input holds a value
|
|
104
|
+
|
|
105
|
+
CodeceptJS **waits automatically** before clicking, filling, and most other actions, so you rarely need explicit waits. Steps also write themselves into a promise chain, so you usually **don't need `await`** for regular actions — only for `grab*` actions and page object methods that return data.
|
|
106
|
+
|
|
107
|
+
### Locating Elements
|
|
108
|
+
|
|
109
|
+
Most actions accept a locator. CodeceptJS supports several strategies — prefer the readable ones:
|
|
110
|
+
|
|
111
|
+
```js
|
|
112
|
+
// by visible text / label
|
|
113
|
+
I.click('Continue to checkout');
|
|
114
|
+
I.fillField('First name', 'John');
|
|
115
|
+
|
|
116
|
+
// by ARIA role and accessible name (resilient to CSS changes)
|
|
117
|
+
I.click({ role: 'button', name: 'Continue to checkout' });
|
|
118
|
+
|
|
119
|
+
// by CSS or XPath, when nothing semantic is available
|
|
120
|
+
I.fillField('#email', 'john@example.com');
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
> **Best practice:** prefer labels and ARIA locators (`{ role, name }`). They survive styling changes and document intent. Fall back to CSS/XPath only when needed.
|
|
124
|
+
|
|
125
|
+
## Writing the Checkout Test
|
|
126
|
+
|
|
127
|
+
The Bootstrap checkout form has billing fields, country/state selects, and a payment section. CodeceptJS finds inputs by their visible `<label>`, so the test reads like the form:
|
|
128
|
+
|
|
129
|
+
```js
|
|
130
|
+
Feature('Checkout');
|
|
131
|
+
|
|
132
|
+
Scenario('fill in the checkout form', ({ I }) => {
|
|
133
|
+
I.amOnPage('/docs/4.0/examples/checkout/');
|
|
134
|
+
I.see('Checkout form');
|
|
135
|
+
|
|
136
|
+
// billing address — fields located by their labels
|
|
137
|
+
I.fillField('First name', 'John');
|
|
138
|
+
I.fillField('Last name', 'Doe');
|
|
139
|
+
I.fillField('Username', 'johndoe');
|
|
140
|
+
I.fillField('#email', 'john@example.com'); // label has "(Optional)", use CSS
|
|
141
|
+
I.fillField('Address', '123 Main St.');
|
|
142
|
+
I.selectOption('Country', 'United States');
|
|
143
|
+
I.selectOption('State', 'California');
|
|
144
|
+
I.fillField('Zip', '10001');
|
|
145
|
+
|
|
146
|
+
// shipping / preferences
|
|
147
|
+
I.checkOption('Shipping address is the same as my billing address');
|
|
148
|
+
I.checkOption('Save this information for next time');
|
|
149
|
+
|
|
150
|
+
// payment — "Credit card" is selected by default
|
|
151
|
+
I.click('Credit card');
|
|
152
|
+
I.fillField('Name on card', 'John Doe');
|
|
153
|
+
I.fillField('Credit card number', secret('4111 1111 1111 1111'));
|
|
154
|
+
|
|
155
|
+
// verify the form holds what we entered
|
|
156
|
+
I.seeInField('First name', 'John');
|
|
157
|
+
I.seeInField('Address', '123 Main St.');
|
|
158
|
+
I.click('Continue to checkout');
|
|
159
|
+
});
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
A few things worth noting:
|
|
163
|
+
|
|
164
|
+
- **`secret()`** wraps the card number so it is masked (`****`) in logs and reports. Use it for any sensitive value — see [Secrets](/secrets).
|
|
165
|
+
- Never use a real card number. Payment providers like Stripe publish [test card numbers](https://docs.stripe.com/testing) for exactly this.
|
|
166
|
+
- This is a static demo page with no backend, so we verify by reading field values back with `I.seeInField`. On a real shop you would assert a confirmation, e.g. `I.see('Your order has been placed')`.
|
|
167
|
+
|
|
168
|
+
### A Negative Scenario
|
|
169
|
+
|
|
170
|
+
Good test suites cover failures too. The form validates on submit — submitting it empty shows error messages. CodeceptJS doesn't allow multiple scenarios in one file's suite to nest, but you can add as many `Scenario` blocks as you like:
|
|
171
|
+
|
|
172
|
+
```js
|
|
173
|
+
Scenario('shows validation errors on empty submit', ({ I }) => {
|
|
174
|
+
I.amOnPage('/docs/4.0/examples/checkout/');
|
|
175
|
+
I.click('Continue to checkout');
|
|
176
|
+
I.see('Valid first name is required.');
|
|
177
|
+
});
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
### Running the Test
|
|
181
|
+
|
|
182
|
+
```bash
|
|
183
|
+
npx codeceptjs run --steps
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
`--steps` prints every step as it runs. Useful flags while developing:
|
|
187
|
+
|
|
188
|
+
- `--steps` — print each step
|
|
189
|
+
- `--debug` — steps plus extra debug output (recommended while writing tests)
|
|
190
|
+
- `--verbose` — everything, including the promise chain
|
|
191
|
+
|
|
192
|
+
Set a breakpoint to inspect the page interactively by adding `pause()` to the scenario:
|
|
193
|
+
|
|
194
|
+
```js
|
|
195
|
+
Scenario('fill in the checkout form', ({ I }) => {
|
|
196
|
+
I.amOnPage('/docs/4.0/examples/checkout/');
|
|
197
|
+
I.fillField('First name', 'John');
|
|
198
|
+
pause(); // test stops here; type steps live in the browser
|
|
199
|
+
});
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
In the pause shell you can type `I.click('...')`, inspect the page, and find better locators. See [Debugging](/debugging).
|
|
203
|
+
|
|
204
|
+
The browser is shown locally and runs headless on CI thanks to `setHeadlessWhen(process.env.CI)`. To force it either way for one run:
|
|
205
|
+
|
|
206
|
+
```bash
|
|
207
|
+
npx codeceptjs run --headless
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
Once the test is stable, run the whole suite:
|
|
211
|
+
|
|
212
|
+
```bash
|
|
213
|
+
npx codeceptjs run
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
## Refactoring with a Page Object
|
|
217
|
+
|
|
218
|
+
What if more tests need to fill this form? Copy-pasting steps doesn't scale. The **Page Object** pattern keeps locators and interactions in one reusable place.
|
|
219
|
+
|
|
220
|
+
Generate one:
|
|
221
|
+
|
|
222
|
+
```bash
|
|
223
|
+
npx codeceptjs gpo
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
Call it `Checkout`. It is created in `./pages/Checkout.js` and registered in `codecept.conf.js` under `include`:
|
|
227
|
+
|
|
228
|
+
```js
|
|
229
|
+
export const config = {
|
|
230
|
+
// ...
|
|
231
|
+
include: {
|
|
232
|
+
checkoutPage: './pages/Checkout.js',
|
|
233
|
+
},
|
|
234
|
+
}
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
Page objects are **classes**. Move the form interactions into named methods:
|
|
238
|
+
|
|
239
|
+
```js
|
|
240
|
+
const { I } = inject();
|
|
241
|
+
|
|
242
|
+
class CheckoutPage {
|
|
243
|
+
url = '/docs/4.0/examples/checkout/'
|
|
244
|
+
|
|
245
|
+
open() {
|
|
246
|
+
I.amOnPage(this.url);
|
|
247
|
+
I.see('Checkout form');
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
fillBillingAddress({ firstName, lastName, username, address, country, state, zip }) {
|
|
251
|
+
I.fillField('First name', firstName);
|
|
252
|
+
I.fillField('Last name', lastName);
|
|
253
|
+
I.fillField('Username', username);
|
|
254
|
+
I.fillField('Address', address);
|
|
255
|
+
I.selectOption('Country', country);
|
|
256
|
+
I.selectOption('State', state);
|
|
257
|
+
I.fillField('Zip', zip);
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
payWithCard(name, number) {
|
|
261
|
+
I.click('Credit card');
|
|
262
|
+
I.fillField('Name on card', name);
|
|
263
|
+
I.fillField('Credit card number', secret(number));
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
submit() {
|
|
267
|
+
I.click('Continue to checkout');
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
export default CheckoutPage
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
> `inject()` returns a lazy proxy, so it's safe to destructure `I` before the class. Export the **class** — CodeceptJS auto-instantiates it. (Plain-object page objects still work but classes support lifecycle hooks and inheritance.)
|
|
275
|
+
|
|
276
|
+
The test now reads at the business level. Inject `checkoutPage` by the name you set in the config:
|
|
277
|
+
|
|
278
|
+
```js
|
|
279
|
+
Feature('Checkout');
|
|
280
|
+
|
|
281
|
+
Scenario('complete a checkout', ({ I, checkoutPage }) => {
|
|
282
|
+
checkoutPage.open();
|
|
283
|
+
checkoutPage.fillBillingAddress({
|
|
284
|
+
firstName: 'John',
|
|
285
|
+
lastName: 'Doe',
|
|
286
|
+
username: 'johndoe',
|
|
287
|
+
address: '123 Main St.',
|
|
288
|
+
country: 'United States',
|
|
289
|
+
state: 'California',
|
|
290
|
+
zip: '10001',
|
|
291
|
+
});
|
|
292
|
+
checkoutPage.payWithCard('John Doe', '4111 1111 1111 1111');
|
|
293
|
+
checkoutPage.submit();
|
|
294
|
+
|
|
295
|
+
I.seeInField('First name', 'John');
|
|
296
|
+
});
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
Shorter, intention-revealing, and every other checkout test can reuse the same methods. As coverage grows, add methods to the page object instead of duplicating steps.
|
|
300
|
+
|
|
301
|
+
## Going Further
|
|
302
|
+
|
|
303
|
+
When you have many tests, run them in parallel using Node workers:
|
|
304
|
+
|
|
305
|
+
```bash
|
|
306
|
+
npx codeceptjs run-workers 3
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
From here, explore:
|
|
310
|
+
|
|
311
|
+
- [Locators](/locators) — every locating strategy in depth
|
|
312
|
+
- [Page Objects](/pageobjects) — fragments, step objects, lifecycle hooks
|
|
313
|
+
- [Data-driven tests](/data) — run one scenario over many inputs
|
|
314
|
+
- [Debugging](/debugging) — `pause()`, the interactive shell, and AI-assisted debugging
|
|
315
|
+
- [Continuous Integration](/continuous-integration) — running the suite on CI
|
|
316
|
+
|
|
317
|
+
## Summary
|
|
318
|
+
|
|
319
|
+
If you are just starting with test automation, CodeceptJS lets you describe tests in near-natural language and handles waiting and retries for you. If you already know JavaScript, page objects and dependency injection keep your suite focused on business behavior — which is what keeps tests stable and maintainable as the app grows.
|
|
320
|
+
|
|
321
|
+
> [▶ Next: CodeceptJS Basics](/basics/)
|
|
322
|
+
</content>
|
|
323
|
+
</invoke>
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
---
|
|
2
|
+
permalink: /typescript
|
|
3
|
+
title: TypeScript
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# TypeScript
|
|
7
|
+
|
|
8
|
+
CodeceptJS ships [type declarations](https://github.com/codeceptjs/CodeceptJS/tree/master/typings), so you can write tests, page objects, and custom helpers in TypeScript and get autocomplete and type checking in your editor.
|
|
9
|
+
|
|
10
|
+
## Getting started
|
|
11
|
+
|
|
12
|
+
`npx codeceptjs init` scaffolds a TypeScript project when you answer **Yes** to:
|
|
13
|
+
|
|
14
|
+
```
|
|
15
|
+
? Do you plan to write tests in TypeScript? Yes
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
It writes `codecept.conf.ts` and `*_test.ts` files. The **config file** and helpers are transpiled automatically. **Test files** need a loader — CodeceptJS 4.x is ESM, and Mocha loads test files through CommonJS hooks, so use [`tsx`](https://tsx.is) (fast, esbuild-based, no `tsconfig.json` required):
|
|
19
|
+
|
|
20
|
+
```sh
|
|
21
|
+
npm i tsx --save-dev
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
```ts
|
|
25
|
+
// codecept.conf.ts
|
|
26
|
+
export const config = {
|
|
27
|
+
tests: './**/*_test.ts',
|
|
28
|
+
require: ['tsx/cjs'], // loads the *_test.ts files
|
|
29
|
+
helpers: {
|
|
30
|
+
Playwright: { url: 'http://localhost', browser: 'chromium' },
|
|
31
|
+
},
|
|
32
|
+
}
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Run the tests with `npx codeceptjs run`.
|
|
36
|
+
|
|
37
|
+
> Adding TypeScript to an existing project: set `"type": "module"` in `package.json`, rename the config to `codecept.conf.ts` with `export const config = {}`, install `tsx`, and add `require: ['tsx/cjs']`.
|
|
38
|
+
|
|
39
|
+
## Writing tests
|
|
40
|
+
|
|
41
|
+
Test files use the full TypeScript syntax — imports, enums, interfaces, types:
|
|
42
|
+
|
|
43
|
+
```ts
|
|
44
|
+
// fixtures.ts
|
|
45
|
+
export interface User { email: string; password: string }
|
|
46
|
+
export const admin: User = { email: 'admin@example.com', password: 's3cret' }
|
|
47
|
+
|
|
48
|
+
// login_test.ts
|
|
49
|
+
import { admin } from './fixtures'
|
|
50
|
+
|
|
51
|
+
Feature('Login')
|
|
52
|
+
|
|
53
|
+
Scenario('admin signs in', ({ I }) => {
|
|
54
|
+
I.amOnPage('/login')
|
|
55
|
+
I.fillField('email', admin.email)
|
|
56
|
+
I.fillField('password', admin.password)
|
|
57
|
+
I.click('Login')
|
|
58
|
+
I.see('Welcome')
|
|
59
|
+
})
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
> **Cannot find module** or **Unexpected token** while running tests means the loader isn't wired up — check that `tsx` is installed and `require: ['tsx/cjs']` is in the config.
|
|
63
|
+
|
|
64
|
+
## Promise-based typings
|
|
65
|
+
|
|
66
|
+
CodeceptJS tests read synchronously even though every `I.*` call returns a promise:
|
|
67
|
+
|
|
68
|
+
```ts
|
|
69
|
+
I.amOnPage('/')
|
|
70
|
+
I.click('Login')
|
|
71
|
+
I.see('Hello')
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
The default typings declare these methods as returning `void`, so a linter won't demand `await` on every line. To follow TypeScript conventions and `await` each command instead — some teams find explicit flow control improves stability — enable promise-based typings in `codecept.conf.ts`:
|
|
75
|
+
|
|
76
|
+
```ts
|
|
77
|
+
export const config = {
|
|
78
|
+
fullPromiseBased: true,
|
|
79
|
+
// ...
|
|
80
|
+
}
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Rebuild the type definitions:
|
|
84
|
+
|
|
85
|
+
```sh
|
|
86
|
+
npx codeceptjs def
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
Now the typings return promises:
|
|
90
|
+
|
|
91
|
+
```ts
|
|
92
|
+
await I.amOnPage('/')
|
|
93
|
+
await I.click('Login')
|
|
94
|
+
await I.see('Hello')
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## Types for page objects and custom helpers
|
|
98
|
+
|
|
99
|
+
`npx codeceptjs def` regenerates `steps.d.ts` from your config — run it after adding a page object or a custom helper so autocomplete picks them up.
|
|
100
|
+
|
|
101
|
+
For a custom helper:
|
|
102
|
+
|
|
103
|
+
```ts
|
|
104
|
+
// CustomHelper.ts
|
|
105
|
+
export class CustomHelper extends Helper {
|
|
106
|
+
printMessage(msg: string) {
|
|
107
|
+
console.log(msg)
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
Register it in `codecept.conf.ts` ([helper configuration](/helpers#configuration)), run `npx codeceptjs def`, and `steps.d.ts` becomes:
|
|
113
|
+
|
|
114
|
+
```ts
|
|
115
|
+
/// <reference types='codeceptjs' />
|
|
116
|
+
type CustomHelper = import('./CustomHelper')
|
|
117
|
+
|
|
118
|
+
declare namespace CodeceptJS {
|
|
119
|
+
interface SupportObject { I: I }
|
|
120
|
+
interface Methods extends Playwright, CustomHelper {}
|
|
121
|
+
interface I extends WithTranslation<Methods> {}
|
|
122
|
+
}
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
Page objects appear the same way — `def` adds a `type` for each and lists them in `SupportObject`:
|
|
126
|
+
|
|
127
|
+
```ts
|
|
128
|
+
type loginPage = typeof import('./loginPage')
|
|
129
|
+
type homePage = typeof import('./homePage')
|
|
130
|
+
|
|
131
|
+
declare namespace CodeceptJS {
|
|
132
|
+
interface SupportObject { I: I, loginPage: loginPage, homePage: homePage }
|
|
133
|
+
// ...
|
|
134
|
+
}
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
## Types for custom locators
|
|
138
|
+
|
|
139
|
+
If you use [custom locators](/locators#custom-locators) — for example `I.click({ data: 'user-login' })` — declare their shape in the `CustomLocators` interface in `steps.d.ts` so they're accepted wherever a locator is expected:
|
|
140
|
+
|
|
141
|
+
```ts
|
|
142
|
+
/// <reference types='codeceptjs' />
|
|
143
|
+
|
|
144
|
+
declare namespace CodeceptJS {
|
|
145
|
+
interface CustomLocators {
|
|
146
|
+
data: { data: string }
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
Only the property *types* matter, not the keys. Locators with several (optional) properties work too:
|
|
152
|
+
|
|
153
|
+
```ts
|
|
154
|
+
declare namespace CodeceptJS {
|
|
155
|
+
interface CustomLocators {
|
|
156
|
+
data: { data: string; value?: number; flag?: boolean }
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
```
|