codeceptjs 4.0.0-rc.23 → 4.0.0-rc.25
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 +9 -10
- package/docs/ai.md +3 -51
- package/docs/architecture.md +16 -0
- package/docs/bootstrap.md +1 -1
- package/docs/continuous-integration.md +16 -44
- package/docs/custom-helpers.md +1 -1
- package/docs/detox.md +1 -1
- package/docs/docker.md +1 -30
- package/docs/examples.md +0 -1
- package/docs/helpers/Appium.md +16 -2
- package/docs/helpers/Playwright.md +161 -160
- package/docs/helpers/Puppeteer.md +143 -250
- package/docs/helpers/WebDriver.md +134 -177
- package/docs/hooks.md +11 -1
- package/docs/index.md +1 -1
- package/docs/installation.md +2 -19
- package/docs/locators.md +1 -1
- 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 +195 -8
- 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 +40 -862
- package/docs/reports.md +18 -4
- package/docs/retry.md +48 -18
- package/docs/store.md +94 -0
- package/docs/timeouts.md +1 -1
- package/docs/tutorial.md +207 -155
- package/docs/webdriver.md +6 -73
- package/lib/actor.js +0 -35
- package/lib/command/run-multiple.js +1 -2
- package/lib/helper/Playwright.js +1 -15
- package/lib/helper/Puppeteer.js +0 -103
- package/lib/helper/WebDriver.js +1 -28
- package/lib/helper/extras/PlaywrightLocator.js +10 -0
- package/lib/locator.js +0 -13
- package/lib/plugin/analyze.js +3 -4
- package/lib/plugin/pauseOnFail.js +3 -1
- package/lib/plugin/retryFailedStep.js +7 -7
- package/lib/plugin/screenshot.js +0 -5
- package/lib/plugin/screenshotOnFail.js +3 -1
- package/lib/plugin/stepTimeout.js +1 -1
- package/lib/recorder.js +1 -1
- package/lib/workers.js +0 -4
- package/package.json +3 -4
- package/docs/helpers/Mochawesome.md +0 -8
- package/docs/helpers/MockServer.md +0 -212
- package/docs/helpers/Polly.md +0 -44
- package/docs/helpers/Protractor.md +0 -1769
- package/docs/helpers/SoftExpectHelper.md +0 -352
- package/docs/react.md +0 -70
- package/lib/helper/Mochawesome.js +0 -96
- package/lib/helper/extras/PlaywrightReactVueLocator.js +0 -61
- package/lib/helper/extras/React.js +0 -65
package/docs/tutorial.md
CHANGED
|
@@ -3,45 +3,38 @@ permalink: /tutorial
|
|
|
3
3
|
title: CodeceptJS Complete Tutorial
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
# Tutorial:
|
|
6
|
+
# Tutorial: Testing a Checkout Page
|
|
7
7
|
|
|
8
|
-
**[CodeceptJS](https://codecept.io) is a popular open-source testing framework** for JavaScript. It is designed to
|
|
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
9
|
|
|
10
|
-
|
|
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
11
|
|
|
12
|
-
|
|
12
|
+
## Install CodeceptJS
|
|
13
|
+
|
|
14
|
+
You need Node.js (and npm) installed. Check with:
|
|
13
15
|
|
|
14
16
|
```bash
|
|
15
17
|
node --version
|
|
16
18
|
npm --version
|
|
17
19
|
```
|
|
18
20
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
To install CodeceptJS create a new folder and run command form terminal:
|
|
21
|
+
Create a new folder, then install CodeceptJS together with Playwright:
|
|
22
22
|
|
|
23
|
+
```bash
|
|
24
|
+
npm init -y
|
|
25
|
+
npm install codeceptjs playwright --save-dev
|
|
26
|
+
npx playwright install --with-deps
|
|
23
27
|
```
|
|
24
|
-
npx create-codeceptjs .
|
|
25
|
-
```
|
|
26
|
-
|
|
27
|
-
If you run the npx create-codeceptjs . command, it will install CodeceptJS with Playwright in the current directory.
|
|
28
28
|
|
|
29
|
-
|
|
29
|
+
`npx playwright install` downloads the Chromium, Firefox, and WebKit browsers; `--with-deps` also installs the system libraries they need.
|
|
30
30
|
|
|
31
|
-
|
|
31
|
+
Now scaffold the project:
|
|
32
32
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
Let's initialize a new project for that!
|
|
36
|
-
|
|
37
|
-
Run
|
|
38
|
-
|
|
39
|
-
```
|
|
33
|
+
```bash
|
|
40
34
|
npx codeceptjs init
|
|
41
35
|
```
|
|
42
|
-
Agree on defaults (press Enter for every question asked). When asked for base site URL, provide a URL of a ecommerce website you are testing. For instance, it could be: `https://myshop.com` if you test already published website or `http://localhost` if you run the website locally.
|
|
43
36
|
|
|
44
|
-
|
|
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:
|
|
45
38
|
|
|
46
39
|
```
|
|
47
40
|
.
|
|
@@ -50,9 +43,31 @@ When asked for a test name and suite name write "Checkout". It will create the f
|
|
|
50
43
|
└── Checkout_test.js
|
|
51
44
|
```
|
|
52
45
|
|
|
53
|
-
|
|
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.
|
|
54
47
|
|
|
55
|
-
|
|
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`:
|
|
56
71
|
|
|
57
72
|
```js
|
|
58
73
|
Feature('Checkout');
|
|
@@ -60,212 +75,249 @@ Feature('Checkout');
|
|
|
60
75
|
Scenario('test something', ({ I }) => {
|
|
61
76
|
});
|
|
62
77
|
```
|
|
63
|
-
Inside the Scenario block you write a test.
|
|
64
78
|
|
|
65
|
-
|
|
79
|
+
A test lives inside a `Scenario` block. Let's open the checkout page:
|
|
66
80
|
|
|
67
81
|
```js
|
|
68
82
|
Feature('Checkout');
|
|
69
83
|
|
|
70
84
|
Scenario('test something', ({ I }) => {
|
|
71
|
-
I.amOnPage('/')
|
|
85
|
+
I.amOnPage('/docs/4.0/examples/checkout/');
|
|
72
86
|
});
|
|
73
87
|
```
|
|
74
|
-
But you may want to ask...
|
|
75
|
-
|
|
76
|
-
## What is I?
|
|
77
88
|
|
|
78
|
-
|
|
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.
|
|
79
90
|
|
|
80
|
-
|
|
91
|
+
But you may be wondering...
|
|
81
92
|
|
|
82
|
-
|
|
93
|
+
### What is `I`?
|
|
83
94
|
|
|
84
|
-
|
|
85
|
-
* `I.click(locator)`: This action simulates a click on the element identified by the given locator.
|
|
86
|
-
* `I.fillField(field, value)`: This action fills the specified field with the given value.
|
|
87
|
-
* `I.see(text, context)`: This action checks that the given text is visible on the page (or in the specified context).
|
|
88
|
-
* `I.selectOption(select, option)`: This action selects the specified option from the given select dropdown.
|
|
89
|
-
* `I.waitForElement(locator, timeout)`: This action waits for the specified element to appear on the page, up to the given timeout.
|
|
90
|
-
* `I.waitForText(text, timeout, context)`: This action waits for the given text to appear on the page (or in the specified context), up to the given timeout.
|
|
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:
|
|
91
96
|
|
|
92
|
-
|
|
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
|
|
93
104
|
|
|
94
|
-
|
|
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.
|
|
95
106
|
|
|
96
|
-
|
|
107
|
+
### Locating Elements
|
|
97
108
|
|
|
98
|
-
|
|
109
|
+
Most actions accept a locator. CodeceptJS supports several strategies — prefer the readable ones:
|
|
99
110
|
|
|
100
111
|
```js
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
But sometimes elements are not as easy to locate, so you can use CSS or XPath locators to locate them.
|
|
112
|
+
// by visible text / label
|
|
113
|
+
I.click('Continue to checkout');
|
|
114
|
+
I.fillField('First name', 'John');
|
|
105
115
|
|
|
106
|
-
|
|
116
|
+
// by ARIA role and accessible name (resilient to CSS changes)
|
|
117
|
+
I.click({ role: 'button', name: 'Continue to checkout' });
|
|
107
118
|
|
|
108
|
-
|
|
109
|
-
I.
|
|
119
|
+
// by CSS or XPath, when nothing semantic is available
|
|
120
|
+
I.fillField('#email', 'john@example.com');
|
|
110
121
|
```
|
|
111
122
|
|
|
112
|
-
|
|
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.
|
|
113
124
|
|
|
114
|
-
|
|
125
|
+
## Writing the Checkout Test
|
|
115
126
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
Let's see how a regular checkout script may look in CodeceptJS:
|
|
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:
|
|
119
128
|
|
|
120
129
|
```js
|
|
121
|
-
|
|
122
|
-
// we select one product and switched to checkout project
|
|
123
|
-
I.amOnPage('/');
|
|
124
|
-
I.click('Coffee Cup');
|
|
125
|
-
I.click('Purchase');
|
|
126
|
-
I.click('Checkout');
|
|
127
|
-
|
|
128
|
-
// fill in the shipping address
|
|
129
|
-
I.fillField('First Name', 'John');
|
|
130
|
-
I.fillField('Last Name', 'Doe');
|
|
131
|
-
I.fillField('Address', '123 Main St.');
|
|
132
|
-
I.fillField('City', 'New York');
|
|
133
|
-
I.selectOption('State', 'New York');
|
|
134
|
-
I.fillField('Zip Code', '10001');
|
|
135
|
-
|
|
136
|
-
// select a payment method
|
|
137
|
-
I.click('#credit-card-option');
|
|
138
|
-
I.fillField('Card Number', '1234-5678-9012-3456');
|
|
139
|
-
I.fillField('Expiration Date', '12/22');
|
|
140
|
-
I.fillField('Security Code', '123');
|
|
130
|
+
Feature('Checkout');
|
|
141
131
|
|
|
142
|
-
|
|
143
|
-
I.
|
|
132
|
+
Scenario('fill in the checkout form', ({ I }) => {
|
|
133
|
+
I.amOnPage('/docs/4.0/examples/checkout/');
|
|
134
|
+
I.see('Checkout form');
|
|
144
135
|
|
|
145
|
-
//
|
|
146
|
-
I.
|
|
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');
|
|
147
159
|
});
|
|
148
|
-
```
|
|
149
|
-
Sure, in relaity your script might be more complicated. As you have noticed, we used CSS locator `'#credit-card-option'` to get select a payment option. However, the test is simple and you can follow user steps through it.
|
|
150
|
-
|
|
151
|
-
Please note, that you shouldn't use a real credit card number here. Good news, you don't need to. Payment providers like Strip provide dummy card numbers for testing purposes.
|
|
152
|
-
|
|
153
|
-
Run the test with next command:
|
|
154
|
-
|
|
155
|
-
```
|
|
156
|
-
npx codeceptjs run --debug -p pause
|
|
157
160
|
```
|
|
158
161
|
|
|
159
|
-
|
|
162
|
+
A few things worth noting:
|
|
160
163
|
|
|
161
|
-
|
|
162
|
-
|
|
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')`.
|
|
163
167
|
|
|
164
|
-
|
|
168
|
+
### A Negative Scenario
|
|
165
169
|
|
|
166
|
-
|
|
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:
|
|
167
171
|
|
|
168
|
-
```
|
|
169
|
-
|
|
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
|
+
});
|
|
170
178
|
```
|
|
171
179
|
|
|
172
|
-
|
|
180
|
+
### Running the Test
|
|
173
181
|
|
|
182
|
+
```bash
|
|
183
|
+
npx codeceptjs run --steps
|
|
174
184
|
```
|
|
175
|
-
HEADLESS=true codeceptjs run
|
|
176
|
-
```
|
|
177
|
-
for Windows users HEADLESS should be set in a different manner:
|
|
178
185
|
|
|
179
|
-
|
|
180
|
-
set HEADLESS=true&& codeceptjs run
|
|
181
|
-
```
|
|
182
|
-
The tests will pass but no browser is shown, so you can watch YouTube videos while it goes!
|
|
186
|
+
`--steps` prints every step as it runs. Useful flags while developing:
|
|
183
187
|
|
|
184
|
-
|
|
188
|
+
- `--steps` — print each step
|
|
189
|
+
- `--debug` — steps plus extra debug output (recommended while writing tests)
|
|
190
|
+
- `--verbose` — everything, including the promise chain
|
|
185
191
|
|
|
186
|
-
|
|
192
|
+
Set a breakpoint to inspect the page interactively by adding `pause()` to the scenario:
|
|
187
193
|
|
|
188
|
-
|
|
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
|
+
```
|
|
189
201
|
|
|
190
|
-
|
|
202
|
+
In the pause shell you can type `I.click('...')`, inspect the page, and find better locators. See [Debugging](/debugging).
|
|
191
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
|
|
192
208
|
```
|
|
193
|
-
|
|
209
|
+
|
|
210
|
+
Once the test is stable, run the whole suite:
|
|
211
|
+
|
|
212
|
+
```bash
|
|
213
|
+
npx codeceptjs run
|
|
194
214
|
```
|
|
195
215
|
|
|
196
|
-
|
|
216
|
+
## Refactoring with a Page Object
|
|
197
217
|
|
|
198
|
-
|
|
199
|
-
include: {
|
|
200
|
-
...
|
|
201
|
-
checkoutPage: './pages/Checkout.js',
|
|
202
|
-
},
|
|
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.
|
|
203
219
|
|
|
204
|
-
|
|
205
|
-
Now open this file:
|
|
220
|
+
Generate one:
|
|
206
221
|
|
|
207
|
-
```
|
|
208
|
-
|
|
222
|
+
```bash
|
|
223
|
+
npx codeceptjs gpo
|
|
224
|
+
```
|
|
209
225
|
|
|
210
|
-
|
|
226
|
+
Call it `Checkout`. It is created in `./pages/Checkout.js` and registered in `codecept.conf.js` under `include`:
|
|
211
227
|
|
|
212
|
-
|
|
228
|
+
```js
|
|
229
|
+
export const config = {
|
|
230
|
+
// ...
|
|
231
|
+
include: {
|
|
232
|
+
checkoutPage: './pages/Checkout.js',
|
|
233
|
+
},
|
|
213
234
|
}
|
|
214
235
|
```
|
|
215
236
|
|
|
216
|
-
|
|
237
|
+
Page objects are **classes**. Move the form interactions into named methods:
|
|
217
238
|
|
|
218
239
|
```js
|
|
219
240
|
const { I } = inject();
|
|
220
241
|
|
|
221
|
-
|
|
242
|
+
class CheckoutPage {
|
|
243
|
+
url = '/docs/4.0/examples/checkout/'
|
|
244
|
+
|
|
245
|
+
open() {
|
|
246
|
+
I.amOnPage(this.url);
|
|
247
|
+
I.see('Checkout form');
|
|
248
|
+
}
|
|
222
249
|
|
|
223
|
-
|
|
224
|
-
I.fillField('
|
|
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);
|
|
225
254
|
I.fillField('Address', address);
|
|
226
|
-
I.
|
|
227
|
-
I.
|
|
255
|
+
I.selectOption('Country', country);
|
|
256
|
+
I.selectOption('State', state);
|
|
228
257
|
I.fillField('Zip', zip);
|
|
229
|
-
}
|
|
258
|
+
}
|
|
230
259
|
|
|
231
|
-
|
|
232
|
-
I.click('
|
|
233
|
-
I.fillField('
|
|
234
|
-
I.fillField('
|
|
235
|
-
|
|
236
|
-
},
|
|
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
|
+
}
|
|
237
265
|
|
|
238
|
-
|
|
239
|
-
I.click('
|
|
240
|
-
}
|
|
266
|
+
submit() {
|
|
267
|
+
I.click('Continue to checkout');
|
|
268
|
+
}
|
|
241
269
|
}
|
|
270
|
+
|
|
271
|
+
export default CheckoutPage
|
|
242
272
|
```
|
|
243
273
|
|
|
244
|
-
|
|
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:
|
|
245
277
|
|
|
246
278
|
```js
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
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');
|
|
260
296
|
});
|
|
261
297
|
```
|
|
262
298
|
|
|
263
|
-
|
|
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:
|
|
264
310
|
|
|
265
|
-
|
|
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
|
|
266
316
|
|
|
267
317
|
## Summary
|
|
268
318
|
|
|
269
|
-
If you
|
|
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.
|
|
270
320
|
|
|
271
|
-
|
|
321
|
+
> [▶ Next: CodeceptJS Basics](/basics/)
|
|
322
|
+
</content>
|
|
323
|
+
</invoke>
|
package/docs/webdriver.md
CHANGED
|
@@ -9,56 +9,11 @@ How does your client, manager, or tester, or any other non-technical person, kno
|
|
|
9
9
|
|
|
10
10
|
End-to-End tests can cover standard but complex scenarios from a user's perspective. With e2e tests you can be confident that users, following all defined scenarios, won't get errors. We check **functionality of application and a user interface** (UI) as well.
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
We use [webdriverio](https://webdriver.io) library to run tests over WebDriver. To proceed you need to have [CodeceptJS installed](/quickstart#using-selenium-webdriver) and `WebDriver` helper selected.
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
🛩️ No Selenium Server, ChromeDriver, GeckoDriver, or driver services to install or start. Since WebdriverIO 9, driver management is fully automatic — WebdriverIO downloads and starts the matching driver for you. Read more [here](https://webdriver.io/blog/2023/07/31/driver-management/).
|
|
15
15
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
* Selenium - is a toolset for browser test automation
|
|
19
|
-
* WebDriver - a standard protocol for communicating between test framework and browsers
|
|
20
|
-
* JSON Wire - an older version of such protocol
|
|
21
|
-
|
|
22
|
-
We use [webdriverio](https://webdriver.io) library to run tests over WebDriver.
|
|
23
|
-
|
|
24
|
-
To proceed you need to have [CodeceptJS installed](/quickstart#using-selenium-webdriver) and `WebDriver` helper selected.
|
|
25
|
-
|
|
26
|
-
Selenium WebDriver may be complicated from start, as it requires following tools to be installed and started.
|
|
27
|
-
|
|
28
|
-
1. Selenium Server - to execute and send commands to browser
|
|
29
|
-
2. ChromeDriver or GeckoDriver - to allow browsers to run in automated mode.
|
|
30
|
-
|
|
31
|
-
> Those tools can be easily installed via NPM. Use [selenium-standalone](https://www.npmjs.com/package/selenium-standalone) to automatically install them.
|
|
32
|
-
|
|
33
|
-
You can also use `@wdio/selenium-standalone-service` package, to install and start Selenium Server for your tests automatically.
|
|
34
|
-
|
|
35
|
-
```
|
|
36
|
-
npm i @wdio/selenium-standalone-service --save-dev
|
|
37
|
-
```
|
|
38
|
-
|
|
39
|
-
Enable it in config inside plugins section:
|
|
40
|
-
|
|
41
|
-
```js
|
|
42
|
-
export const config = {
|
|
43
|
-
// ...
|
|
44
|
-
// inside condecept.conf.js
|
|
45
|
-
plugins: {
|
|
46
|
-
wdio: {
|
|
47
|
-
enabled: true,
|
|
48
|
-
services: ['selenium-standalone']
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
```
|
|
53
|
-
|
|
54
|
-
> ⚠ It is not recommended to use wdio plugin & selenium-standalone when running tests in parallel. Consider **switching to Selenoid** if you need parallel run or using cloud services.
|
|
55
|
-
|
|
56
|
-
🛩️ With the release of WebdriverIO version v8.14.0, and onwards, all driver management hassles are now a thing of the past 🙌. Read more [here](https://webdriver.io/blog/2023/07/31/driver-management/).
|
|
57
|
-
One of the significant advantages of this update is that you can now get rid of any driver services you previously had to manage, such as
|
|
58
|
-
`wdio-chromedriver-service`, `wdio-geckodriver-service`, `wdio-edgedriver-service`, `wdio-safaridriver-service`, and even `@wdio/selenium-standalone-service`.
|
|
59
|
-
|
|
60
|
-
For those who require custom driver options, fear not; WebDriver Helper allows you to pass in driver options through custom WebDriver configuration.
|
|
61
|
-
If you have a custom grid, use a cloud service, or prefer to run your own driver, there's no need to worry since WebDriver Helper will only start a driver when there are no other connection information settings like hostname or port specified.
|
|
16
|
+
WebDriver Helper only starts a driver automatically when no connection information (like `host` or `port`) is specified. If you have a custom grid, use a cloud service, or prefer to run your own driver, set those connection options and that endpoint is used instead.
|
|
62
17
|
|
|
63
18
|
Example:
|
|
64
19
|
|
|
@@ -138,26 +93,6 @@ keepCookies: true,
|
|
|
138
93
|
|
|
139
94
|
> ▶ More config options available on [WebDriver helper reference](/helpers/WebDriver#configuration)
|
|
140
95
|
|
|
141
|
-
### ChromeDriver without Selenium
|
|
142
|
-
|
|
143
|
-
If you want to run tests using raw ChromeDriver (which also supports WebDriver protocol) avoiding Selenium Server, you should provide following configuration:
|
|
144
|
-
|
|
145
|
-
```js
|
|
146
|
-
port: 9515,
|
|
147
|
-
browser: 'chrome',
|
|
148
|
-
path: '/',
|
|
149
|
-
```
|
|
150
|
-
|
|
151
|
-
> If you face issues connecting to WebDriver, please check that corresponding server is running on a specified port. If host is other than `localhost` or port is other than `4444`, update the configuration.
|
|
152
|
-
|
|
153
|
-
### Selenium in Docker (Selenoid)
|
|
154
|
-
|
|
155
|
-
Browsers can be executed in Docker containers. This is useful when testing on Continous Integration server.
|
|
156
|
-
We recommend using [Selenoid](https://aerokube.com/selenoid/) to run browsers in container.
|
|
157
|
-
|
|
158
|
-
CodeceptJS has [Selenoid plugin](/plugins#selenoid) which can automagically load browser container setup.
|
|
159
|
-
|
|
160
|
-
|
|
161
96
|
### Headless Mode
|
|
162
97
|
|
|
163
98
|
The bundled `@codeceptjs/configure` toggles headless mode for WebDriver — for chrome/firefox it injects `--headless` into the matching capability args automatically:
|
|
@@ -203,9 +138,7 @@ desiredCapabilities: {
|
|
|
203
138
|
|
|
204
139
|
### Cloud Providers
|
|
205
140
|
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
By default, those parameters are set to connect to local Selenium Server, but they should be changed if you want to run tests via [Cloud Providers](/helpers/WebDriver#cloud-providers). You may also need `user` and `key` parameters to authenticate on cloud service.
|
|
141
|
+
By default, WebdriverIO starts a local driver automatically, so no connection parameters are needed. To run tests via [Cloud Providers](/helpers/WebDriver#cloud-providers) instead, specify `host`, `port`, `protocol`, and `path` parameters pointing to the service. You may also need `user` and `key` parameters to authenticate on the cloud service.
|
|
209
142
|
|
|
210
143
|
There are also [browser and platform specific capabilities](https://github.com/SeleniumHQ/selenium/wiki/DesiredCapabilities). Services like SauceLabs, BrowserStack or browser vendors can provide their own specific capabilities for more tuning.
|
|
211
144
|
|
|
@@ -352,7 +285,7 @@ Scenario('create todo item', ({ I }) => {
|
|
|
352
285
|
|
|
353
286
|
> [▶ Working example of CodeceptJS WebDriver tests](https://github.com/DavertMik/codeceptjs-webdriver-example) for TodoMVC application.
|
|
354
287
|
|
|
355
|
-
WebDriver helper supports standard [CSS/XPath and text locators](/locators) as well as
|
|
288
|
+
WebDriver helper supports standard [CSS/XPath and text locators](/locators) as well as [Shadow DOM](/shadow) and [ARIA locators](/locators#aria-locators).
|
|
356
289
|
|
|
357
290
|
### Grabbers
|
|
358
291
|
|
|
@@ -481,7 +414,7 @@ If it's hard to define what to wait, it is recommended to use [retries](/basics/
|
|
|
481
414
|
|
|
482
415
|
## Configuring CI
|
|
483
416
|
|
|
484
|
-
|
|
417
|
+
Locally, WebdriverIO starts the driver for you and tests run in window mode. On a remote CI (Continuous Integration) server there is usually no desktop, so window mode is not available.
|
|
485
418
|
|
|
486
419
|
There are following options available:
|
|
487
420
|
|
package/lib/actor.js
CHANGED
|
@@ -1,10 +1,7 @@
|
|
|
1
1
|
import Step, { MetaStep } from './step.js'
|
|
2
2
|
import recordStep from './step/record.js'
|
|
3
|
-
import retryStep from './step/retry.js'
|
|
4
3
|
import { methodsOfObject } from './utils.js'
|
|
5
|
-
import { TIMEOUT_ORDER } from './timeout.js'
|
|
6
4
|
import event from './event.js'
|
|
7
|
-
import store from './store.js'
|
|
8
5
|
import output from './output.js'
|
|
9
6
|
import Container from './container.js'
|
|
10
7
|
|
|
@@ -30,38 +27,6 @@ class Actor {
|
|
|
30
27
|
output.say(msg, `${color}`)
|
|
31
28
|
})
|
|
32
29
|
}
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* set the maximum execution time for the next step
|
|
36
|
-
* @function
|
|
37
|
-
* @param {number} timeout - step timeout in seconds
|
|
38
|
-
* @return {this}
|
|
39
|
-
* @inner
|
|
40
|
-
*/
|
|
41
|
-
limitTime(timeout) {
|
|
42
|
-
if (!store.timeouts) return this
|
|
43
|
-
|
|
44
|
-
console.log('I.limitTime() is deprecated, use step.timeout() instead')
|
|
45
|
-
|
|
46
|
-
event.dispatcher.prependOnceListener(event.step.before, step => {
|
|
47
|
-
output.log(`Timeout to ${step}: ${timeout}s`)
|
|
48
|
-
step.setTimeout(timeout * 1000, TIMEOUT_ORDER.codeLimitTime)
|
|
49
|
-
})
|
|
50
|
-
|
|
51
|
-
return this
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
/**
|
|
55
|
-
* @function
|
|
56
|
-
* @param {*} [opts]
|
|
57
|
-
* @return {this}
|
|
58
|
-
* @inner
|
|
59
|
-
*/
|
|
60
|
-
retry(opts) {
|
|
61
|
-
console.log('I.retry() is deprecated, use step.retry() instead')
|
|
62
|
-
retryStep(opts)
|
|
63
|
-
return this
|
|
64
|
-
}
|
|
65
30
|
}
|
|
66
31
|
|
|
67
32
|
/**
|