playwright-cucumber-ts-steps 1.0.0 โ 1.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 +195 -256
- package/dist/backend/actions/index.js +4 -0
- package/dist/backend/actions/interactions.js +23 -0
- package/dist/backend/actions/navigation.js +19 -0
- package/dist/backend/api/assertions.js +26 -0
- package/dist/backend/api/index.js +4 -0
- package/dist/backend/api/requests.js +24 -0
- package/dist/backend/api/state.js +15 -0
- package/dist/backend/assertions/expectVisible.js +8 -0
- package/dist/backend/assertions/index.js +5 -0
- package/dist/backend/assertions/pageState.js +25 -0
- package/dist/backend/assertions/text.js +20 -0
- package/dist/backend/assertions/visibility.js +20 -0
- package/dist/backend/auth/index.js +71 -0
- package/dist/backend/elements/alerts.js +21 -0
- package/dist/backend/elements/forms.js +59 -0
- package/dist/backend/elements/frames.js +25 -0
- package/dist/backend/elements/index.js +5 -0
- package/dist/core/registry.js +20 -0
- package/dist/core/runner.js +136 -0
- package/dist/index.js +10 -0
- package/dist/reporting/index.js +43 -0
- package/package.json +19 -101
- package/LICENSE +0 -21
- package/src/actions/clickSteps.ts +0 -429
- package/src/actions/cookieSteps.ts +0 -95
- package/src/actions/debugSteps.ts +0 -21
- package/src/actions/elementFindSteps.ts +0 -961
- package/src/actions/fillFormSteps.ts +0 -270
- package/src/actions/index.ts +0 -12
- package/src/actions/inputSteps.ts +0 -354
- package/src/actions/interceptionSteps.ts +0 -325
- package/src/actions/miscSteps.ts +0 -1144
- package/src/actions/mouseSteps.ts +0 -256
- package/src/actions/scrollSteps.ts +0 -122
- package/src/actions/storageSteps.ts +0 -308
- package/src/assertions/buttonAndTextVisibilitySteps.ts +0 -436
- package/src/assertions/cookieSteps.ts +0 -131
- package/src/assertions/elementSteps.ts +0 -432
- package/src/assertions/formInputSteps.ts +0 -377
- package/src/assertions/index.ts +0 -11
- package/src/assertions/interceptionRequestsSteps.ts +0 -640
- package/src/assertions/locationSteps.ts +0 -315
- package/src/assertions/roleTestIdSteps.ts +0 -254
- package/src/assertions/semanticSteps.ts +0 -267
- package/src/assertions/storageSteps.ts +0 -250
- package/src/assertions/visualSteps.ts +0 -275
- package/src/custom_setups/loginHooks.ts +0 -154
- package/src/helpers/checkPeerDeps.ts +0 -19
- package/src/helpers/compareSnapshots.ts +0 -35
- package/src/helpers/hooks.ts +0 -212
- package/src/helpers/utils/fakerUtils.ts +0 -64
- package/src/helpers/utils/index.ts +0 -4
- package/src/helpers/utils/optionsUtils.ts +0 -104
- package/src/helpers/utils/resolveUtils.ts +0 -74
- package/src/helpers/utils/sessionUtils.ts +0 -36
- package/src/helpers/world.ts +0 -119
- package/src/iframes/frames.ts +0 -15
- package/src/index.ts +0 -18
- package/src/register.ts +0 -4
package/README.md
CHANGED
|
@@ -1,343 +1,282 @@
|
|
|
1
|
-
# ๐ญ
|
|
1
|
+
# ๐ญ Playwright Cucumber TS Steps
|
|
2
2
|
|
|
3
|
-
[](https://www.npmjs.com/package/playwright-cucumber-ts-steps)
|
|
4
|
+
[](https://www.npmjs.com/package/playwright-cucumber-ts-steps)
|
|
5
|
+
[](https://opensource.org/licenses/MIT)
|
|
6
|
+
[](https://playwright.dev)
|
|
7
|
+
[](https://www.typescriptlang.org/)
|
|
4
8
|
|
|
5
|
-
|
|
6
|
-
<img src="https://github.com/qaPaschalE/k6-cucumber-steps/blob/main/assets/paschal%20logo%20(2).png?raw=true" alt="paschal Logo" style="margin-top:25px;" align="center"/>
|
|
7
|
-
</td></tr></table>
|
|
9
|
+
**The "Low-Code" BDD Framework for Playwright.**
|
|
8
10
|
|
|
9
|
-
|
|
10
|
-
[](https://github.com/qaPaschalE/playwright-cucumber-ts-steps/blob/main/LICENSE)
|
|
11
|
-
[](https://nodejs.org/)
|
|
12
|
-
[](https://github.com/qaPaschalE/playwright-cucumber-ts-steps/actions)
|
|
13
|
-
[](https://www.npmjs.com/package/playwright-cucumber-ts-steps)
|
|
14
|
-
[](https://github.com/qaPaschalE/playwright-cucumber-ts-steps/issues)
|
|
15
|
-
[](https://github.com/qaPaschalE/playwright-cucumber-ts-steps/stargazers)
|
|
11
|
+
Write powerful end-to-end tests in plain English using Gherkin syntax, without managing complex glue code. `playwright-cucumber-ts-steps` provides a pre-built library of robust steps for UI, API, and Authentication testing, running natively inside Playwright.
|
|
16
12
|
|
|
17
|
-
|
|
13
|
+
---
|
|
18
14
|
|
|
19
|
-
|
|
15
|
+
## ๐ Features
|
|
20
16
|
|
|
21
|
-
|
|
17
|
+
- **Zero Boilerplate:** Install and start writing `.feature` files immediately.
|
|
18
|
+
- **Hybrid Testing:** Mix **UI**, **API**, and **Database** steps in a single scenario.
|
|
19
|
+
- **Smart Auth:** Login once, save the session, and reuse it across thousands of tests.
|
|
20
|
+
- **Native Performance:** Runs purely on Playwright (no heavy Cucumber-JS wrapper), giving you Parallelism, Tracing, and Retries out of the box.
|
|
21
|
+
- **Tag Filtering:** Run specific tests like `@smoke` or `@regression` easily.
|
|
22
|
+
- **Auto-Reporting:** Built-in helper for HTML and Slack reports with **screenshots on failure**.
|
|
22
23
|
|
|
23
|
-
|
|
24
|
-
- ๐ฏ Support for **UI**, **API**, **mobile**, **iframe**, **hybrid login**, and **visual testing**
|
|
25
|
-
- ๐ง Smart **session management** via storageState, `localStorage`, `sessionStorage`, and alias reuse
|
|
26
|
-
- ๐๏ธ **Alias**, **Faker**, `.env`, and dynamic JSON fixture support
|
|
27
|
-
- ๐ธ **Screenshot** on failure, ๐ฅ **video recording**, and ๐ผ๏ธ **visual diff** with baseline comparison
|
|
28
|
-
- ๐ค Supports **file upload**, **drag-and-drop**, and **multi-user session flows**
|
|
29
|
-
- ๐ Fully supports **API requests with inline assertions**, payload from custom folders, and session injection
|
|
30
|
-
- โ
Compatible with both `Page` and `FrameLocator` contexts (iframe-aware)
|
|
24
|
+
---
|
|
31
25
|
|
|
32
26
|
## ๐ฆ Installation
|
|
33
27
|
|
|
34
28
|
```bash
|
|
35
|
-
npm install playwright-cucumber-ts-steps
|
|
29
|
+
npm install playwright-cucumber-ts-steps @playwright/test
|
|
36
30
|
```
|
|
37
31
|
|
|
38
|
-
or
|
|
39
|
-
|
|
40
32
|
```bash
|
|
41
|
-
|
|
33
|
+
npx playwright install
|
|
42
34
|
```
|
|
43
35
|
|
|
44
36
|
---
|
|
45
37
|
|
|
46
|
-
##
|
|
38
|
+
## โก Quick Start Guide
|
|
47
39
|
|
|
48
|
-
|
|
40
|
+
### 1. Configure Playwright
|
|
49
41
|
|
|
50
|
-
|
|
51
|
-
- [`@cucumber/cucumber`](https://github.com/cucumber/cucumber-js)
|
|
52
|
-
- TypeScript
|
|
53
|
-
- Cucumber IDE plugin (Optional), but Highly recommended
|
|
42
|
+
Update `playwright.config.ts` to use the built-in reporting helper.
|
|
54
43
|
|
|
55
|
-
|
|
44
|
+
```typescript
|
|
45
|
+
import { defineConfig } from "@playwright/test";
|
|
46
|
+
import { getReporters } from "playwright-cucumber-ts-steps";
|
|
56
47
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
48
|
+
export default defineConfig({
|
|
49
|
+
testDir: "./tests",
|
|
50
|
+
// Sets up HTML report + Console list automatically
|
|
51
|
+
reporter: getReporters({ on: ["html"] }),
|
|
52
|
+
use: {
|
|
53
|
+
// We handle screenshots manually in the runner,
|
|
54
|
+
// but this is good as a backup
|
|
55
|
+
screenshot: "only-on-failure",
|
|
56
|
+
},
|
|
57
|
+
});
|
|
60
58
|
```
|
|
61
59
|
|
|
62
|
-
|
|
60
|
+
### 2. Create the Runner
|
|
63
61
|
|
|
64
|
-
|
|
62
|
+
Create a file at `tests/bdd.spec.ts`. This is the entry point.
|
|
65
63
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
## ๐ ๏ธ Usage
|
|
69
|
-
|
|
70
|
-
1. **Load step definitions** from the package:
|
|
64
|
+
```typescript
|
|
65
|
+
import { runTests } from "playwright-cucumber-ts-steps";
|
|
71
66
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
```
|
|
67
|
+
// Runs all feature files in the 'features' folder
|
|
68
|
+
runTests("features/*.feature");
|
|
69
|
+
```
|
|
76
70
|
|
|
77
|
-
|
|
71
|
+
### 3. Write Your Feature
|
|
78
72
|
|
|
79
|
-
|
|
80
|
-
Feature: Login
|
|
73
|
+
Create `features/login.feature`:
|
|
81
74
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
When I find input by name "Email"
|
|
85
|
-
And I type "user@example.com"
|
|
86
|
-
And I click button "Login"
|
|
87
|
-
Then I see visible text "Welcome"
|
|
88
|
-
Then I do not see URL contains "/login"
|
|
75
|
+
```gherkin
|
|
76
|
+
Feature: User Authentication
|
|
89
77
|
|
|
90
|
-
|
|
78
|
+
@smoke
|
|
79
|
+
Scenario: Successful Login
|
|
80
|
+
Given I visit "[https://the-internet.herokuapp.com/login](https://the-internet.herokuapp.com/login)"
|
|
81
|
+
When I fill "#username" with "tomsmith"
|
|
82
|
+
And I fill "#password" with "SuperSecretPassword!"
|
|
83
|
+
And I click "button[type='submit']"
|
|
84
|
+
Then I expect "#flash" to contain text "You logged into a secure area!"
|
|
91
85
|
|
|
92
|
-
|
|
86
|
+
```
|
|
93
87
|
|
|
94
|
-
|
|
88
|
+
### 4. Run Tests
|
|
95
89
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
- ๐ **API**: Request mocking, assertions, response validation
|
|
100
|
-
- ๐ฑ **Mobile support**: Enable with `@mobile` tag (iPhone 13 emulation)
|
|
101
|
-
- ๐๏ธ **Visual testing**: Enable with `@visual` tag (pixelmatch diff)
|
|
90
|
+
```bash
|
|
91
|
+
npx playwright test demo.spec.ts --headed
|
|
92
|
+
```
|
|
102
93
|
|
|
103
94
|
---
|
|
104
95
|
|
|
105
|
-
##
|
|
96
|
+
## ๐ท๏ธ Tags & Filtering
|
|
106
97
|
|
|
107
|
-
You can
|
|
98
|
+
You can use tags to organize your tests and run specific subsets (e.g., only Smoke tests).
|
|
108
99
|
|
|
109
|
-
|
|
110
|
-
// custom-world.ts
|
|
111
|
-
import { CustomWorld as BaseWorld } from "playwright-cucumber-ts-steps";
|
|
100
|
+
**In your Feature file:**
|
|
112
101
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
}
|
|
116
|
-
```
|
|
102
|
+
```gherkin
|
|
103
|
+
Feature: Checkout
|
|
117
104
|
|
|
118
|
-
|
|
105
|
+
@smoke @critical
|
|
106
|
+
Scenario: Guest Checkout
|
|
107
|
+
...
|
|
119
108
|
|
|
120
|
-
|
|
109
|
+
@regression
|
|
110
|
+
Scenario: Registered User Checkout
|
|
111
|
+
...
|
|
121
112
|
|
|
122
|
-
|
|
113
|
+
```
|
|
123
114
|
|
|
124
|
-
|
|
125
|
-
Given I get element by selector "[type='text_selector']"
|
|
126
|
-
And I store element text as "welcomeText"
|
|
127
|
-
Then I see "@welcomeText" in the element
|
|
128
|
-
```
|
|
115
|
+
**In your Test Runner (`tests/bdd.spec.ts`):**
|
|
129
116
|
|
|
130
|
-
|
|
117
|
+
```typescript
|
|
118
|
+
import { runTests } from "playwright-cucumber-ts-steps";
|
|
131
119
|
|
|
132
|
-
|
|
133
|
-
|
|
120
|
+
// OPTION 1: Run Everything
|
|
121
|
+
// runTests('features/*.feature');
|
|
134
122
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
| Continue | Click |
|
|
139
|
-
```
|
|
123
|
+
// OPTION 2: Run only Smoke tests
|
|
124
|
+
runTests("features/*.feature", { tags: "@smoke" });
|
|
125
|
+
```
|
|
140
126
|
|
|
141
127
|
---
|
|
142
128
|
|
|
143
|
-
##
|
|
129
|
+
## ๐ก Various Usage Examples
|
|
144
130
|
|
|
145
|
-
|
|
131
|
+
### 1. API Testing (Backend Validation)
|
|
146
132
|
|
|
147
|
-
|
|
148
|
-
- ๐ฅ **Video recording per scenario**
|
|
149
|
-
- ๐ **Session reuse** using `storageState`
|
|
133
|
+
You can validate your backend directly without opening a browser, or mix it with UI tests.
|
|
150
134
|
|
|
151
|
-
|
|
135
|
+
```gherkin
|
|
136
|
+
Feature: User API
|
|
152
137
|
|
|
153
|
-
|
|
138
|
+
@api
|
|
139
|
+
Scenario: Create and Verify User
|
|
140
|
+
When I make a POST request to "[https://reqres.in/api/users](https://reqres.in/api/users)" with body '{"name": "Morpheus", "job": "Leader"}'
|
|
141
|
+
Then I expect the response status to be 201
|
|
142
|
+
And I expect the response property "name" to be "Morpheus"
|
|
154
143
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
| Feature Type | Description |
|
|
158
|
-
| ---------------- | ------------------------------------------------------------------------------------- |
|
|
159
|
-
| `fill form data` | Use `When I fill the following "Login" form data:` to perform actions like: |
|
|
160
|
-
| | - `fill`, `click`, `check`, `select`, `upload:<file>` |
|
|
161
|
-
| | - `drag:<targetSelector>` for drag-and-drop |
|
|
162
|
-
| | - `assert:visible`, `assert:text:<value>` |
|
|
163
|
-
| | - `request:<METHOD>:<URL>:<payload>.json` |
|
|
164
|
-
| | - `set:localStorage:<key>`, `set:sessionStorage:<key>` |
|
|
165
|
-
| | - `wait:<ms>`, `reload`, and use alias `@aliasName` |
|
|
166
|
-
| Session Handling | Steps like `I login with a session data "admin.json"`, `I save session as "customer"` |
|
|
167
|
-
| Session Restore | `I restore session cookies "customer" [with reload] [using localStorage]` |
|
|
168
|
-
| API Assertions | Validate JSON response with `assert:json:key=expectedValue` |
|
|
144
|
+
```
|
|
169
145
|
|
|
170
|
-
|
|
146
|
+
### 2. Advanced Elements (Iframes & Uploads)
|
|
171
147
|
|
|
172
|
-
|
|
148
|
+
Handling complex HTML elements is built-in.
|
|
173
149
|
|
|
174
150
|
```gherkin
|
|
175
|
-
|
|
176
|
-
When I fill the following "Login" form data:
|
|
177
|
-
| input[name='email'] | test@example.com |
|
|
178
|
-
| input[name='password'] | @userPassword |
|
|
179
|
-
| input[type='checkbox'] | check |
|
|
180
|
-
| button:has-text("Sign In") | click |
|
|
181
|
-
| .welcome | assert:visible |
|
|
182
|
-
| .role | assert:text:Admin |
|
|
183
|
-
And I save session as "admin"
|
|
184
|
-
|
|
185
|
-
Scenario: Restore user session
|
|
186
|
-
Given I restore session cookies "admin" with reload using localStorage
|
|
187
|
-
When I visit "/dashboard"
|
|
188
|
-
Then I see text "Welcome back"
|
|
189
|
-
|
|
190
|
-
Scenario: API login + inject session
|
|
191
|
-
When I fill the following "Login" form data:
|
|
192
|
-
| request:POST:/api/login:adminPayload.json | saveAs:loginData |
|
|
193
|
-
| set:localStorage:token | @loginData.token |
|
|
194
|
-
| save session as | adminViaAPI |
|
|
195
|
-
```
|
|
151
|
+
Feature: File Upload and Iframes
|
|
196
152
|
|
|
197
|
-
|
|
153
|
+
Scenario: Upload Document inside Iframe
|
|
154
|
+
Given I visit "[https://example.com/upload](https://example.com/upload)"
|
|
155
|
+
# Switch context to the iframe
|
|
156
|
+
When I upload file "data/resume.pdf" to "#file-input" inside frame "#upload-iframe"
|
|
157
|
+
And I click "#submit-btn" inside frame "#upload-iframe"
|
|
158
|
+
Then I expect "div.success" inside frame "#upload-iframe" to be visible
|
|
198
159
|
|
|
199
|
-
### โ
Folder Structure Suggestion
|
|
200
|
-
|
|
201
|
-
```text
|
|
202
|
-
e2e/
|
|
203
|
-
โโโ features/ # .feature files
|
|
204
|
-
โโโ step_definitions/
|
|
205
|
-
โ โโโ index.ts # import from this package
|
|
206
|
-
โโโ support/
|
|
207
|
-
โ โโโ world.ts # CustomWorld extends with iframe support
|
|
208
|
-
โ โโโ hooks.ts # artifact & session manager
|
|
209
|
-
โ โโโ helpers/
|
|
210
|
-
โ โโโ resolveUtils.ts # alias/env/json resolver
|
|
211
|
-
โโโ test-data/ # JSON fixtures
|
|
212
|
-
โโโ payload/ # API request payloads
|
|
213
160
|
```
|
|
214
161
|
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
Hereโs a full `cucumber.js` config file that includes **all the configurable options** integrated so far. This supports:
|
|
218
|
-
|
|
219
|
-
- โ
CLI/env override for artifact directories
|
|
220
|
-
- โ
Visual testing toggle
|
|
221
|
-
- โ
Screenshot and video toggle
|
|
222
|
-
- โ
Device emulation via `MOBILE_DEVICE` env or world param
|
|
223
|
-
- โ
Multiple profiles (`default`, `mobile`, `visual`, `ci`)
|
|
224
|
-
- โ
Parallel test execution and ts-node for TypeScript support
|
|
162
|
+
### 3. The "Login Once" Pattern (Authentication)
|
|
225
163
|
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
### โ
`cucumber.js`
|
|
229
|
-
|
|
230
|
-
```js
|
|
231
|
-
import path from "path";
|
|
232
|
-
import dotenv from "dotenv";
|
|
233
|
-
|
|
234
|
-
dotenv.config();
|
|
235
|
-
|
|
236
|
-
const ARTIFACT_DIR = process.env.TEST_ARTIFACT_DIR || "test-artifacts";
|
|
237
|
-
|
|
238
|
-
const defaultWorldParams = {
|
|
239
|
-
artifactDir: ARTIFACT_DIR,
|
|
240
|
-
payloadDir: "payloads",
|
|
241
|
-
enableTrace: process.env.ENABLE_TRACE || "fail", // "false" | "fail" | "all"
|
|
242
|
-
enableScreenshots: process.env.ENABLE_SCREENSHOTS || "fail", // "false" | "fail" | "all"
|
|
243
|
-
enableVideos: process.env.ENABLE_VIDEOS || "all", // "false" | "fail" | "all"
|
|
244
|
-
enableVisualTest: process.env.ENABLE_VISUAL_TEST === "true",
|
|
245
|
-
device: process.env.MOBILE_DEVICE || undefined, // e.g., "Pixel 5"
|
|
246
|
-
};
|
|
247
|
-
|
|
248
|
-
export default {
|
|
249
|
-
default: {
|
|
250
|
-
require: ["ts-node/register", "src/test/steps/**/*.ts", "src/test/support/**/*.ts"],
|
|
251
|
-
format: ["progress", `html:${path.join(ARTIFACT_DIR, "report.html")}`],
|
|
252
|
-
publishQuiet: true,
|
|
253
|
-
paths: ["src/test/features/**/*.feature"],
|
|
254
|
-
parallel: 2,
|
|
255
|
-
worldParameters: defaultWorldParams,
|
|
256
|
-
},
|
|
164
|
+
Speed up your suite by 10x. Login once, save the cookies, and reuse them.
|
|
257
165
|
|
|
258
|
-
|
|
259
|
-
require: ["ts-node/register", "src/test/steps/**/*.ts", "src/test/support/**/*.ts"],
|
|
260
|
-
format: ["progress"],
|
|
261
|
-
publishQuiet: true,
|
|
262
|
-
paths: ["src/test/features/**/*.feature"],
|
|
263
|
-
parallel: 1,
|
|
264
|
-
tags: "@mobile",
|
|
265
|
-
worldParameters: {
|
|
266
|
-
...defaultWorldParams,
|
|
267
|
-
device: process.env.MOBILE_DEVICE || "iPhone 13 Pro",
|
|
268
|
-
},
|
|
269
|
-
},
|
|
166
|
+
**Step 1: Create a Setup Feature (`features/setup.feature`)**
|
|
270
167
|
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
168
|
+
```gherkin
|
|
169
|
+
Feature: Setup
|
|
170
|
+
|
|
171
|
+
@setup
|
|
172
|
+
Scenario: Admin Login
|
|
173
|
+
Given I visit "/login"
|
|
174
|
+
When I fill "#user" with "admin"
|
|
175
|
+
And I fill "#pass" with "1234"
|
|
176
|
+
And I click "#login-btn"
|
|
177
|
+
And I expect "#dashboard" to be visible
|
|
178
|
+
# Saves session to ./auth/admin.json
|
|
179
|
+
And I save the browser state to "admin.json"
|
|
282
180
|
|
|
283
|
-
ci: {
|
|
284
|
-
require: ["ts-node/register", "src/test/steps/**/*.ts", "src/test/support/**/*.ts"],
|
|
285
|
-
format: ["progress", `json:${path.join(ARTIFACT_DIR, "report.json")}`],
|
|
286
|
-
publishQuiet: true,
|
|
287
|
-
paths: ["src/test/features/**/*.feature"],
|
|
288
|
-
parallel: 4,
|
|
289
|
-
worldParameters: {
|
|
290
|
-
...defaultWorldParams,
|
|
291
|
-
enableScreenshots: true,
|
|
292
|
-
enableVideos: true,
|
|
293
|
-
enableVisualTest: false,
|
|
294
|
-
},
|
|
295
|
-
},
|
|
296
|
-
};
|
|
297
181
|
```
|
|
298
182
|
|
|
299
|
-
|
|
183
|
+
**Step 2: Use in Daily Tests (`features/admin.feature`)**
|
|
300
184
|
|
|
301
|
-
|
|
185
|
+
```gherkin
|
|
186
|
+
Feature: Admin Panel
|
|
302
187
|
|
|
303
|
-
|
|
304
|
-
#
|
|
305
|
-
|
|
188
|
+
Scenario: Check Reports
|
|
189
|
+
# Loads cookies instantly - No login UI needed!
|
|
190
|
+
Given I load the browser state from "admin.json"
|
|
191
|
+
When I visit "/admin/reports"
|
|
192
|
+
Then I expect "h1" to have text "Weekly Reports"
|
|
306
193
|
|
|
307
|
-
|
|
308
|
-
MOBILE_DEVICE="Pixel 5" npx cucumber-js --config cucumber.js --profile mobile
|
|
194
|
+
```
|
|
309
195
|
|
|
310
|
-
|
|
311
|
-
ENABLE_VISUAL_TEST=true npx cucumber-js --config cucumber.js --profile visual
|
|
196
|
+
---
|
|
312
197
|
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
198
|
+
## ๐ Step Glossary (Cheat Sheet)
|
|
199
|
+
|
|
200
|
+
### ๐ฑ๏ธ Actions
|
|
201
|
+
|
|
202
|
+
| Step | Usage Example |
|
|
203
|
+
| ---------------- | -------------------------------------- |
|
|
204
|
+
| **Visit** | `I visit "https://google.com"` |
|
|
205
|
+
| **Click** | `I click "#submit-btn"` |
|
|
206
|
+
| **Force Click** | `I force click "#hidden-btn"` |
|
|
207
|
+
| **Double Click** | `I double click ".icon"` |
|
|
208
|
+
| **Fill Input** | `I fill "#email" with "user@test.com"` |
|
|
209
|
+
| **Press Key** | `I press "Enter"` (or "Tab", "Escape") |
|
|
210
|
+
| **Wait** | `I wait for 2000 milliseconds` |
|
|
211
|
+
| **Reload** | `I reload the page` |
|
|
212
|
+
| **Go Back** | `I go back` |
|
|
213
|
+
|
|
214
|
+
### โ
Assertions
|
|
215
|
+
|
|
216
|
+
| Step | Usage Example |
|
|
217
|
+
| ---------------- | -------------------------------------------------------------- |
|
|
218
|
+
| **Visibility** | `I expect "#modal" to be visible` |
|
|
219
|
+
| **Hidden** | `I expect "#loader" to be hidden` |
|
|
220
|
+
| **Exact Text** | `I expect "#header" to have text "Welcome"` |
|
|
221
|
+
| **Partial Text** | `I expect ".error" to contain text "Failed"` |
|
|
222
|
+
| **Input Value** | `I expect "#username" to have value "admin"` |
|
|
223
|
+
| **Exact URL** | `I expect the url to be "https://site.com/home"` |
|
|
224
|
+
| **Partial URL** | `I expect the url to contain "/dashboard"` |
|
|
225
|
+
| **Title** | `I expect the title to contain "Home Page"` |
|
|
226
|
+
| **Attribute** | `I expect "img" to have attribute "src" with value "logo.png"` |
|
|
227
|
+
| **Screenshot** | `I expect the page screenshot to match "home.png"` |
|
|
228
|
+
|
|
229
|
+
### ๐งฉ Forms & Elements
|
|
230
|
+
|
|
231
|
+
| Step | Usage Example |
|
|
232
|
+
| --------------------- | ---------------------------------------------- |
|
|
233
|
+
| **Select (Dropdown)** | `I select option "Canada" from "#country"` |
|
|
234
|
+
| **Check Box** | `I check "#terms-checkbox"` |
|
|
235
|
+
| **Uncheck** | `I uncheck "#newsletter"` |
|
|
236
|
+
| **Upload File** | `I upload file "data.csv" to "#upload"` |
|
|
237
|
+
| **Handle Alert** | `I accept the next dialog` |
|
|
238
|
+
| **Frame Click** | `I click "#btn" inside frame "#payment-frame"` |
|
|
239
|
+
|
|
240
|
+
### ๐ API
|
|
241
|
+
|
|
242
|
+
| Step | Usage Example |
|
|
243
|
+
| ---------------- | ------------------------------------------------------------- |
|
|
244
|
+
| **GET** | `I make a GET request to "/api/users"` |
|
|
245
|
+
| **DELETE** | `I make a DELETE request to "/api/users/1"` |
|
|
246
|
+
| **POST** | `I make a POST request to "/api/login" with body '{"u":"1"}'` |
|
|
247
|
+
| **Status Check** | `I expect the response status to be 200` |
|
|
248
|
+
| **JSON Check** | `I expect the response property "data.id" to be "99"` |
|
|
316
249
|
|
|
317
250
|
---
|
|
318
251
|
|
|
319
|
-
|
|
252
|
+
## ๐ ๏ธ Extending (Custom Steps)
|
|
320
253
|
|
|
321
|
-
|
|
322
|
-
| ------------------ | -------------------- | --------------------- | ------------------------------------------- |
|
|
323
|
-
| Screenshot toggle | `ENABLE_SCREENSHOTS` | `enableScreenshots` | Capture screenshots on failure |
|
|
324
|
-
| Video toggle | `ENABLE_VIDEOS` | `enableVideos` | Enable/disable video recording |
|
|
325
|
-
| Visual testing | `ENABLE_VISUAL_TEST` | `enableVisualTest` | Capture and compare visual snapshots |
|
|
326
|
-
| Artifact directory | `TEST_ARTIFACT_DIR` | `artifactDir` | Where to save screenshots, videos, etc. |
|
|
327
|
-
| Mobile device emu | `MOBILE_DEVICE` | `device` | Device name for Playwright mobile emulation |
|
|
254
|
+
Need a step that isn't included? You can easily register your own in your spec file.
|
|
328
255
|
|
|
329
|
-
|
|
256
|
+
```typescript
|
|
257
|
+
// tests/bdd.spec.ts
|
|
258
|
+
import { runTests, Step } from "playwright-cucumber-ts-steps";
|
|
330
259
|
|
|
331
|
-
|
|
260
|
+
// 1. Define custom step
|
|
261
|
+
Step("I scroll to the bottom of the page", async (page) => {
|
|
262
|
+
await page.evaluate(() => window.scrollTo(0, document.body.scrollHeight));
|
|
263
|
+
});
|
|
332
264
|
|
|
333
|
-
|
|
265
|
+
// 2. Run tests
|
|
266
|
+
runTests("features/*.feature");
|
|
267
|
+
```
|
|
334
268
|
|
|
335
|
-
|
|
269
|
+
Then use it in your feature:
|
|
336
270
|
|
|
337
|
-
|
|
271
|
+
```gherkin
|
|
272
|
+
Scenario: Scroll Test
|
|
273
|
+
Given I visit "[https://infinite-scroll.com](https://infinite-scroll.com)"
|
|
274
|
+
When I scroll to the bottom of the page
|
|
275
|
+
|
|
276
|
+
```
|
|
338
277
|
|
|
339
278
|
---
|
|
340
279
|
|
|
341
|
-
##
|
|
280
|
+
## ๐ License
|
|
342
281
|
|
|
343
|
-
|
|
282
|
+
MIT ยฉ 2024
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const registry_1 = require("../../core/registry");
|
|
4
|
+
// Click
|
|
5
|
+
(0, registry_1.Step)("I click {string}", async (page, selector) => {
|
|
6
|
+
await page.click(selector);
|
|
7
|
+
});
|
|
8
|
+
// Force Click (sometimes needed for stubborn elements)
|
|
9
|
+
(0, registry_1.Step)("I force click {string}", async (page, selector) => {
|
|
10
|
+
await page.click(selector, { force: true });
|
|
11
|
+
});
|
|
12
|
+
// Type/Fill
|
|
13
|
+
(0, registry_1.Step)("I fill {string} with {string}", async (page, selector, value) => {
|
|
14
|
+
await page.fill(selector, value);
|
|
15
|
+
});
|
|
16
|
+
// Press Key (e.g., "Enter")
|
|
17
|
+
(0, registry_1.Step)("I press {string}", async (page, key) => {
|
|
18
|
+
await page.keyboard.press(key);
|
|
19
|
+
});
|
|
20
|
+
// Wait (Hard wait - use sparingly!)
|
|
21
|
+
(0, registry_1.Step)("I wait for {int} milliseconds", async (page, ms) => {
|
|
22
|
+
await page.waitForTimeout(ms);
|
|
23
|
+
});
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const registry_1 = require("../../core/registry");
|
|
4
|
+
// Basic visit
|
|
5
|
+
(0, registry_1.Step)("I visit {string}", async (page, url) => {
|
|
6
|
+
await page.goto(url);
|
|
7
|
+
});
|
|
8
|
+
// Visit and wait for network (useful for slow apps)
|
|
9
|
+
(0, registry_1.Step)("I visit {string} and wait for network idle", async (page, url) => {
|
|
10
|
+
await page.goto(url, { waitUntil: "networkidle" });
|
|
11
|
+
});
|
|
12
|
+
// Reload page
|
|
13
|
+
(0, registry_1.Step)("I reload the page", async (page) => {
|
|
14
|
+
await page.reload();
|
|
15
|
+
});
|
|
16
|
+
// Go back
|
|
17
|
+
(0, registry_1.Step)("I go back", async (page) => {
|
|
18
|
+
await page.goBack();
|
|
19
|
+
});
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const test_1 = require("@playwright/test");
|
|
4
|
+
const registry_1 = require("../../core/registry");
|
|
5
|
+
const state_1 = require("./state");
|
|
6
|
+
// Check Status Code
|
|
7
|
+
(0, registry_1.Step)("I expect the response status to be {int}", async (page, statusCode) => {
|
|
8
|
+
const response = state_1.apiState.getResponse();
|
|
9
|
+
(0, test_1.expect)(response.status()).toBe(statusCode);
|
|
10
|
+
});
|
|
11
|
+
// Check entire Body Text
|
|
12
|
+
(0, registry_1.Step)("I expect the response body to contain {string}", async (page, text) => {
|
|
13
|
+
const response = state_1.apiState.getResponse();
|
|
14
|
+
const body = await response.text();
|
|
15
|
+
(0, test_1.expect)(body).toContain(text);
|
|
16
|
+
});
|
|
17
|
+
// Check JSON Property
|
|
18
|
+
(0, registry_1.Step)("I expect the response property {string} to be {string}", async (page, jsonPath, value) => {
|
|
19
|
+
const response = state_1.apiState.getResponse();
|
|
20
|
+
const json = await response.json();
|
|
21
|
+
// FIX: Added ': string' to the 'i' parameter
|
|
22
|
+
const actualValue = jsonPath
|
|
23
|
+
.split(".")
|
|
24
|
+
.reduce((o, i) => o[i], json);
|
|
25
|
+
(0, test_1.expect)(String(actualValue)).toBe(value);
|
|
26
|
+
});
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const registry_1 = require("../../core/registry");
|
|
4
|
+
const state_1 = require("./state");
|
|
5
|
+
// GET Request
|
|
6
|
+
(0, registry_1.Step)("I make a GET request to {string}", async (page, url) => {
|
|
7
|
+
const response = await page.request.get(url);
|
|
8
|
+
state_1.apiState.setResponse(response);
|
|
9
|
+
console.log(`GET ${url} - Status: ${response.status()}`);
|
|
10
|
+
});
|
|
11
|
+
// DELETE Request
|
|
12
|
+
(0, registry_1.Step)("I make a DELETE request to {string}", async (page, url) => {
|
|
13
|
+
const response = await page.request.delete(url);
|
|
14
|
+
state_1.apiState.setResponse(response);
|
|
15
|
+
});
|
|
16
|
+
// POST Request with JSON Body
|
|
17
|
+
// Usage: I make a POST request to "/api/login" with body '{"user": "admin"}'
|
|
18
|
+
(0, registry_1.Step)("I make a POST request to {string} with body {string}", async (page, url, bodyString) => {
|
|
19
|
+
const response = await page.request.post(url, {
|
|
20
|
+
data: JSON.parse(bodyString), // Parse the string input into a JSON object
|
|
21
|
+
headers: { "Content-Type": "application/json" },
|
|
22
|
+
});
|
|
23
|
+
state_1.apiState.setResponse(response);
|
|
24
|
+
});
|