start-vibing 2.0.11 → 2.0.13
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 +177 -177
- package/dist/cli.js +19 -2
- package/package.json +42 -42
- package/template/.claude/CLAUDE.md +174 -174
- package/template/.claude/agents/01-orchestration/agent-selector.md +130 -130
- package/template/.claude/agents/01-orchestration/checkpoint-manager.md +142 -142
- package/template/.claude/agents/01-orchestration/context-manager.md +138 -138
- package/template/.claude/agents/01-orchestration/error-recovery.md +182 -182
- package/template/.claude/agents/01-orchestration/orchestrator.md +114 -114
- package/template/.claude/agents/01-orchestration/parallel-coordinator.md +141 -141
- package/template/.claude/agents/01-orchestration/task-decomposer.md +121 -121
- package/template/.claude/agents/01-orchestration/workflow-router.md +114 -114
- package/template/.claude/agents/02-typescript/bun-runtime-expert.md +197 -197
- package/template/.claude/agents/02-typescript/esm-resolver.md +193 -193
- package/template/.claude/agents/02-typescript/import-alias-enforcer.md +158 -158
- package/template/.claude/agents/02-typescript/ts-generics-helper.md +183 -183
- package/template/.claude/agents/02-typescript/ts-migration-helper.md +238 -238
- package/template/.claude/agents/02-typescript/ts-strict-checker.md +180 -180
- package/template/.claude/agents/02-typescript/ts-types-analyzer.md +199 -199
- package/template/.claude/agents/02-typescript/type-definition-writer.md +187 -187
- package/template/.claude/agents/02-typescript/zod-schema-designer.md +212 -212
- package/template/.claude/agents/02-typescript/zod-validator.md +158 -158
- package/template/.claude/agents/03-testing/playwright-assertions.md +265 -265
- package/template/.claude/agents/03-testing/playwright-e2e.md +247 -247
- package/template/.claude/agents/03-testing/playwright-fixtures.md +234 -234
- package/template/.claude/agents/03-testing/playwright-multi-viewport.md +256 -256
- package/template/.claude/agents/03-testing/playwright-page-objects.md +247 -247
- package/template/.claude/agents/03-testing/test-cleanup-manager.md +248 -248
- package/template/.claude/agents/03-testing/test-data-generator.md +254 -254
- package/template/.claude/agents/03-testing/tester-integration.md +278 -278
- package/template/.claude/agents/03-testing/tester-unit.md +207 -207
- package/template/.claude/agents/03-testing/vitest-config.md +287 -287
- package/template/.claude/agents/04-docker/container-health.md +255 -255
- package/template/.claude/agents/04-docker/deployment-validator.md +225 -225
- package/template/.claude/agents/04-docker/docker-compose-designer.md +281 -281
- package/template/.claude/agents/04-docker/docker-env-manager.md +235 -235
- package/template/.claude/agents/04-docker/docker-multi-stage.md +241 -241
- package/template/.claude/agents/04-docker/dockerfile-optimizer.md +208 -208
- package/template/.claude/agents/05-database/database-seeder.md +273 -273
- package/template/.claude/agents/05-database/mongodb-query-optimizer.md +230 -230
- package/template/.claude/agents/05-database/mongoose-aggregation.md +306 -306
- package/template/.claude/agents/05-database/mongoose-index-optimizer.md +182 -182
- package/template/.claude/agents/05-database/mongoose-schema-designer.md +267 -267
- package/template/.claude/agents/06-security/auth-session-validator.md +68 -68
- package/template/.claude/agents/06-security/input-sanitizer.md +80 -80
- package/template/.claude/agents/06-security/owasp-checker.md +97 -97
- package/template/.claude/agents/06-security/permission-auditor.md +100 -100
- package/template/.claude/agents/06-security/security-auditor.md +84 -84
- package/template/.claude/agents/06-security/sensitive-data-scanner.md +83 -83
- package/template/.claude/agents/07-documentation/api-documenter.md +136 -136
- package/template/.claude/agents/07-documentation/changelog-manager.md +105 -105
- package/template/.claude/agents/07-documentation/documenter.md +76 -76
- package/template/.claude/agents/07-documentation/domain-updater.md +81 -81
- package/template/.claude/agents/07-documentation/jsdoc-generator.md +114 -114
- package/template/.claude/agents/07-documentation/readme-generator.md +135 -135
- package/template/.claude/agents/08-git/branch-manager.md +58 -58
- package/template/.claude/agents/08-git/commit-manager.md +63 -63
- package/template/.claude/agents/08-git/pr-creator.md +76 -76
- package/template/.claude/agents/09-quality/code-reviewer.md +71 -71
- package/template/.claude/agents/09-quality/quality-checker.md +67 -67
- package/template/.claude/agents/10-research/best-practices-finder.md +89 -89
- package/template/.claude/agents/10-research/competitor-analyzer.md +106 -106
- package/template/.claude/agents/10-research/pattern-researcher.md +93 -93
- package/template/.claude/agents/10-research/research-cache-manager.md +76 -76
- package/template/.claude/agents/10-research/research-web.md +98 -98
- package/template/.claude/agents/10-research/tech-evaluator.md +101 -101
- package/template/.claude/agents/11-ui-ux/accessibility-auditor.md +136 -136
- package/template/.claude/agents/11-ui-ux/design-system-enforcer.md +125 -125
- package/template/.claude/agents/11-ui-ux/skeleton-generator.md +118 -118
- package/template/.claude/agents/11-ui-ux/ui-desktop.md +132 -132
- package/template/.claude/agents/11-ui-ux/ui-mobile.md +98 -98
- package/template/.claude/agents/11-ui-ux/ui-tablet.md +110 -110
- package/template/.claude/agents/12-performance/api-latency-analyzer.md +156 -156
- package/template/.claude/agents/12-performance/bundle-analyzer.md +113 -113
- package/template/.claude/agents/12-performance/memory-leak-detector.md +137 -137
- package/template/.claude/agents/12-performance/performance-profiler.md +115 -115
- package/template/.claude/agents/12-performance/query-optimizer.md +124 -124
- package/template/.claude/agents/12-performance/render-optimizer.md +154 -154
- package/template/.claude/agents/13-debugging/build-error-fixer.md +207 -207
- package/template/.claude/agents/13-debugging/debugger.md +149 -149
- package/template/.claude/agents/13-debugging/error-stack-analyzer.md +141 -141
- package/template/.claude/agents/13-debugging/network-debugger.md +208 -208
- package/template/.claude/agents/13-debugging/runtime-error-fixer.md +181 -181
- package/template/.claude/agents/13-debugging/type-error-resolver.md +185 -185
- package/template/.claude/agents/14-validation/final-validator.md +93 -93
- package/template/.claude/agents/_backup/analyzer.md +134 -134
- package/template/.claude/agents/_backup/code-reviewer.md +279 -279
- package/template/.claude/agents/_backup/commit-manager.md +219 -219
- package/template/.claude/agents/_backup/debugger.md +280 -280
- package/template/.claude/agents/_backup/documenter.md +237 -237
- package/template/.claude/agents/_backup/domain-updater.md +197 -197
- package/template/.claude/agents/_backup/final-validator.md +169 -169
- package/template/.claude/agents/_backup/orchestrator.md +149 -149
- package/template/.claude/agents/_backup/performance.md +232 -232
- package/template/.claude/agents/_backup/quality-checker.md +240 -240
- package/template/.claude/agents/_backup/research.md +315 -315
- package/template/.claude/agents/_backup/security-auditor.md +192 -192
- package/template/.claude/agents/_backup/tester.md +566 -566
- package/template/.claude/agents/_backup/ui-ux-reviewer.md +247 -247
- package/template/.claude/config/README.md +30 -30
- package/template/.claude/config/mcp-config.json +344 -344
- package/template/.claude/config/project-config.json +53 -53
- package/template/.claude/config/quality-gates.json +46 -46
- package/template/.claude/config/security-rules.json +45 -45
- package/template/.claude/config/testing-config.json +164 -164
- package/template/.claude/hooks/SETUP.md +126 -126
- package/template/.claude/hooks/run-hook.ts +176 -176
- package/template/.claude/hooks/stop-validator.ts +914 -824
- package/template/.claude/hooks/user-prompt-submit.ts +886 -886
- package/template/.claude/scripts/mcp-quick-install.ts +151 -151
- package/template/.claude/scripts/setup-mcps.ts +651 -651
- package/template/.claude/settings.json +275 -275
- package/template/.claude/skills/bun-runtime/SKILL.md +430 -430
- package/template/.claude/skills/codebase-knowledge/domains/claude-system.md +431 -431
- package/template/.claude/skills/codebase-knowledge/domains/mcp-integration.md +295 -295
- package/template/.claude/skills/debugging-patterns/SKILL.md +485 -485
- package/template/.claude/skills/docker-patterns/SKILL.md +555 -555
- package/template/.claude/skills/git-workflow/SKILL.md +454 -454
- package/template/.claude/skills/mongoose-patterns/SKILL.md +499 -499
- package/template/.claude/skills/nextjs-app-router/SKILL.md +327 -327
- package/template/.claude/skills/performance-patterns/SKILL.md +547 -547
- package/template/.claude/skills/playwright-automation/SKILL.md +438 -438
- package/template/.claude/skills/react-patterns/SKILL.md +389 -389
- package/template/.claude/skills/research-cache/SKILL.md +222 -222
- package/template/.claude/skills/shadcn-ui/SKILL.md +511 -511
- package/template/.claude/skills/tailwind-patterns/SKILL.md +465 -465
- package/template/.claude/skills/test-coverage/SKILL.md +467 -467
- package/template/.claude/skills/trpc-api/SKILL.md +434 -434
- package/template/.claude/skills/typescript-strict/SKILL.md +367 -367
- package/template/.claude/skills/zod-validation/SKILL.md +403 -403
- package/template/CLAUDE.md +117 -117
|
@@ -1,247 +1,247 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: playwright-page-objects
|
|
3
|
-
description: 'AUTOMATICALLY invoke when creating E2E tests for new pages. Triggers: new page test, reusable page interactions, complex UI testing. Creates Page Object Model classes. PROACTIVELY encapsulates page structure.'
|
|
4
|
-
model: haiku
|
|
5
|
-
tools: Read, Write, Edit, Grep, Glob
|
|
6
|
-
skills: test-coverage, playwright-automation
|
|
7
|
-
---
|
|
8
|
-
|
|
9
|
-
# Playwright Page Objects Agent
|
|
10
|
-
|
|
11
|
-
You create Page Object Model (POM) classes for reusable page interactions.
|
|
12
|
-
|
|
13
|
-
## Page Object Structure
|
|
14
|
-
|
|
15
|
-
```
|
|
16
|
-
tests/e2e/pages/
|
|
17
|
-
├── base.page.ts # Base class with common methods
|
|
18
|
-
├── login.page.ts
|
|
19
|
-
├── register.page.ts
|
|
20
|
-
├── dashboard.page.ts
|
|
21
|
-
├── settings.page.ts
|
|
22
|
-
└── components/ # Reusable component POMs
|
|
23
|
-
├── navbar.component.ts
|
|
24
|
-
├── modal.component.ts
|
|
25
|
-
└── form.component.ts
|
|
26
|
-
```
|
|
27
|
-
|
|
28
|
-
## Base Page Class
|
|
29
|
-
|
|
30
|
-
```typescript
|
|
31
|
-
// tests/e2e/pages/base.page.ts
|
|
32
|
-
import { Page, Locator, expect } from '@playwright/test';
|
|
33
|
-
|
|
34
|
-
export abstract class BasePage {
|
|
35
|
-
readonly page: Page;
|
|
36
|
-
readonly navbar: Locator;
|
|
37
|
-
readonly loadingSpinner: Locator;
|
|
38
|
-
|
|
39
|
-
constructor(page: Page) {
|
|
40
|
-
this.page = page;
|
|
41
|
-
this.navbar = page.getByTestId('navbar');
|
|
42
|
-
this.loadingSpinner = page.getByTestId('loading-spinner');
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
abstract readonly url: string;
|
|
46
|
-
|
|
47
|
-
async goto() {
|
|
48
|
-
await this.page.goto(this.url);
|
|
49
|
-
await this.waitForLoad();
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
async waitForLoad() {
|
|
53
|
-
await this.loadingSpinner.waitFor({ state: 'hidden' });
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
async expectUrl() {
|
|
57
|
-
await expect(this.page).toHaveURL(new RegExp(this.url));
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
async getPageTitle() {
|
|
61
|
-
return this.page.title();
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
```
|
|
65
|
-
|
|
66
|
-
## Page Object Template
|
|
67
|
-
|
|
68
|
-
```typescript
|
|
69
|
-
// tests/e2e/pages/[page].page.ts
|
|
70
|
-
import { Page, Locator, expect } from '@playwright/test';
|
|
71
|
-
import { BasePage } from './base.page';
|
|
72
|
-
|
|
73
|
-
export class [PageName]Page extends BasePage {
|
|
74
|
-
readonly url = '/[path]';
|
|
75
|
-
|
|
76
|
-
// Locators
|
|
77
|
-
readonly heading: Locator;
|
|
78
|
-
readonly form: Locator;
|
|
79
|
-
readonly submitButton: Locator;
|
|
80
|
-
readonly errorMessage: Locator;
|
|
81
|
-
|
|
82
|
-
constructor(page: Page) {
|
|
83
|
-
super(page);
|
|
84
|
-
this.heading = page.getByRole('heading', { name: '[Title]' });
|
|
85
|
-
this.form = page.getByTestId('[page]-form');
|
|
86
|
-
this.submitButton = page.getByRole('button', { name: 'Submit' });
|
|
87
|
-
this.errorMessage = page.getByTestId('error-message');
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
// Actions
|
|
91
|
-
async fillForm(data: FormData) {
|
|
92
|
-
// Fill form fields
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
async submit() {
|
|
96
|
-
await this.submitButton.click();
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
// Assertions
|
|
100
|
-
async expectSuccess() {
|
|
101
|
-
await expect(this.errorMessage).not.toBeVisible();
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
async expectError(message: string) {
|
|
105
|
-
await expect(this.errorMessage).toContainText(message);
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
```
|
|
109
|
-
|
|
110
|
-
## Login Page Example
|
|
111
|
-
|
|
112
|
-
```typescript
|
|
113
|
-
// tests/e2e/pages/login.page.ts
|
|
114
|
-
import { Page, Locator, expect } from '@playwright/test';
|
|
115
|
-
import { BasePage } from './base.page';
|
|
116
|
-
|
|
117
|
-
export class LoginPage extends BasePage {
|
|
118
|
-
readonly url = '/login';
|
|
119
|
-
|
|
120
|
-
readonly emailInput: Locator;
|
|
121
|
-
readonly passwordInput: Locator;
|
|
122
|
-
readonly loginButton: Locator;
|
|
123
|
-
readonly forgotPasswordLink: Locator;
|
|
124
|
-
readonly registerLink: Locator;
|
|
125
|
-
readonly errorMessage: Locator;
|
|
126
|
-
readonly rememberMeCheckbox: Locator;
|
|
127
|
-
|
|
128
|
-
constructor(page: Page) {
|
|
129
|
-
super(page);
|
|
130
|
-
this.emailInput = page.getByLabel('Email');
|
|
131
|
-
this.passwordInput = page.getByLabel('Password');
|
|
132
|
-
this.loginButton = page.getByRole('button', { name: 'Login' });
|
|
133
|
-
this.forgotPasswordLink = page.getByRole('link', { name: 'Forgot password' });
|
|
134
|
-
this.registerLink = page.getByRole('link', { name: 'Register' });
|
|
135
|
-
this.errorMessage = page.getByTestId('login-error');
|
|
136
|
-
this.rememberMeCheckbox = page.getByLabel('Remember me');
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
async login(email: string, password: string, rememberMe = false) {
|
|
140
|
-
await this.emailInput.fill(email);
|
|
141
|
-
await this.passwordInput.fill(password);
|
|
142
|
-
if (rememberMe) {
|
|
143
|
-
await this.rememberMeCheckbox.check();
|
|
144
|
-
}
|
|
145
|
-
await this.loginButton.click();
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
async loginAndExpectDashboard(email: string, password: string) {
|
|
149
|
-
await this.login(email, password);
|
|
150
|
-
await expect(this.page).toHaveURL('/dashboard');
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
async expectLoginError(message: string) {
|
|
154
|
-
await expect(this.errorMessage).toBeVisible();
|
|
155
|
-
await expect(this.errorMessage).toContainText(message);
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
async goToRegister() {
|
|
159
|
-
await this.registerLink.click();
|
|
160
|
-
await expect(this.page).toHaveURL('/register');
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
async goToForgotPassword() {
|
|
164
|
-
await this.forgotPasswordLink.click();
|
|
165
|
-
await expect(this.page).toHaveURL('/forgot-password');
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
```
|
|
169
|
-
|
|
170
|
-
## Component Page Objects
|
|
171
|
-
|
|
172
|
-
```typescript
|
|
173
|
-
// tests/e2e/pages/components/navbar.component.ts
|
|
174
|
-
import { Page, Locator } from '@playwright/test';
|
|
175
|
-
|
|
176
|
-
export class NavbarComponent {
|
|
177
|
-
readonly page: Page;
|
|
178
|
-
readonly container: Locator;
|
|
179
|
-
readonly logo: Locator;
|
|
180
|
-
readonly searchInput: Locator;
|
|
181
|
-
readonly userMenu: Locator;
|
|
182
|
-
readonly logoutButton: Locator;
|
|
183
|
-
readonly notificationBell: Locator;
|
|
184
|
-
|
|
185
|
-
constructor(page: Page) {
|
|
186
|
-
this.page = page;
|
|
187
|
-
this.container = page.getByTestId('navbar');
|
|
188
|
-
this.logo = this.container.getByTestId('logo');
|
|
189
|
-
this.searchInput = this.container.getByPlaceholder('Search');
|
|
190
|
-
this.userMenu = this.container.getByTestId('user-menu');
|
|
191
|
-
this.logoutButton = this.container.getByRole('button', { name: 'Logout' });
|
|
192
|
-
this.notificationBell = this.container.getByTestId('notifications');
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
async search(query: string) {
|
|
196
|
-
await this.searchInput.fill(query);
|
|
197
|
-
await this.searchInput.press('Enter');
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
async openUserMenu() {
|
|
201
|
-
await this.userMenu.click();
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
async logout() {
|
|
205
|
-
await this.openUserMenu();
|
|
206
|
-
await this.logoutButton.click();
|
|
207
|
-
}
|
|
208
|
-
}
|
|
209
|
-
```
|
|
210
|
-
|
|
211
|
-
## Using Page Objects in Tests
|
|
212
|
-
|
|
213
|
-
```typescript
|
|
214
|
-
// tests/e2e/flows/auth.spec.ts
|
|
215
|
-
import { test, expect } from '../fixtures';
|
|
216
|
-
import { LoginPage } from '../pages/login.page';
|
|
217
|
-
import { DashboardPage } from '../pages/dashboard.page';
|
|
218
|
-
|
|
219
|
-
test.describe('Login Flow', () => {
|
|
220
|
-
let loginPage: LoginPage;
|
|
221
|
-
|
|
222
|
-
test.beforeEach(async ({ page }) => {
|
|
223
|
-
loginPage = new LoginPage(page);
|
|
224
|
-
await loginPage.goto();
|
|
225
|
-
});
|
|
226
|
-
|
|
227
|
-
test('should login successfully', async ({ page }) => {
|
|
228
|
-
await loginPage.loginAndExpectDashboard('user@test.com', 'password123');
|
|
229
|
-
|
|
230
|
-
const dashboardPage = new DashboardPage(page);
|
|
231
|
-
await expect(dashboardPage.welcomeMessage).toBeVisible();
|
|
232
|
-
});
|
|
233
|
-
|
|
234
|
-
test('should show error on invalid credentials', async () => {
|
|
235
|
-
await loginPage.login('wrong@email.com', 'wrongpassword');
|
|
236
|
-
await loginPage.expectLoginError('Invalid credentials');
|
|
237
|
-
});
|
|
238
|
-
});
|
|
239
|
-
```
|
|
240
|
-
|
|
241
|
-
## Critical Rules
|
|
242
|
-
|
|
243
|
-
1. **SINGLE RESPONSIBILITY** - One page = one class
|
|
244
|
-
2. **ENCAPSULATION** - Selectors private, actions public
|
|
245
|
-
3. **REUSABLE METHODS** - Common flows as methods
|
|
246
|
-
4. **SEMANTIC SELECTORS** - Use getByRole, getByLabel, getByTestId
|
|
247
|
-
5. **COMPOSITION** - Compose pages with component POMs
|
|
1
|
+
---
|
|
2
|
+
name: playwright-page-objects
|
|
3
|
+
description: 'AUTOMATICALLY invoke when creating E2E tests for new pages. Triggers: new page test, reusable page interactions, complex UI testing. Creates Page Object Model classes. PROACTIVELY encapsulates page structure.'
|
|
4
|
+
model: haiku
|
|
5
|
+
tools: Read, Write, Edit, Grep, Glob
|
|
6
|
+
skills: test-coverage, playwright-automation
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Playwright Page Objects Agent
|
|
10
|
+
|
|
11
|
+
You create Page Object Model (POM) classes for reusable page interactions.
|
|
12
|
+
|
|
13
|
+
## Page Object Structure
|
|
14
|
+
|
|
15
|
+
```
|
|
16
|
+
tests/e2e/pages/
|
|
17
|
+
├── base.page.ts # Base class with common methods
|
|
18
|
+
├── login.page.ts
|
|
19
|
+
├── register.page.ts
|
|
20
|
+
├── dashboard.page.ts
|
|
21
|
+
├── settings.page.ts
|
|
22
|
+
└── components/ # Reusable component POMs
|
|
23
|
+
├── navbar.component.ts
|
|
24
|
+
├── modal.component.ts
|
|
25
|
+
└── form.component.ts
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Base Page Class
|
|
29
|
+
|
|
30
|
+
```typescript
|
|
31
|
+
// tests/e2e/pages/base.page.ts
|
|
32
|
+
import { Page, Locator, expect } from '@playwright/test';
|
|
33
|
+
|
|
34
|
+
export abstract class BasePage {
|
|
35
|
+
readonly page: Page;
|
|
36
|
+
readonly navbar: Locator;
|
|
37
|
+
readonly loadingSpinner: Locator;
|
|
38
|
+
|
|
39
|
+
constructor(page: Page) {
|
|
40
|
+
this.page = page;
|
|
41
|
+
this.navbar = page.getByTestId('navbar');
|
|
42
|
+
this.loadingSpinner = page.getByTestId('loading-spinner');
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
abstract readonly url: string;
|
|
46
|
+
|
|
47
|
+
async goto() {
|
|
48
|
+
await this.page.goto(this.url);
|
|
49
|
+
await this.waitForLoad();
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
async waitForLoad() {
|
|
53
|
+
await this.loadingSpinner.waitFor({ state: 'hidden' });
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
async expectUrl() {
|
|
57
|
+
await expect(this.page).toHaveURL(new RegExp(this.url));
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
async getPageTitle() {
|
|
61
|
+
return this.page.title();
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## Page Object Template
|
|
67
|
+
|
|
68
|
+
```typescript
|
|
69
|
+
// tests/e2e/pages/[page].page.ts
|
|
70
|
+
import { Page, Locator, expect } from '@playwright/test';
|
|
71
|
+
import { BasePage } from './base.page';
|
|
72
|
+
|
|
73
|
+
export class [PageName]Page extends BasePage {
|
|
74
|
+
readonly url = '/[path]';
|
|
75
|
+
|
|
76
|
+
// Locators
|
|
77
|
+
readonly heading: Locator;
|
|
78
|
+
readonly form: Locator;
|
|
79
|
+
readonly submitButton: Locator;
|
|
80
|
+
readonly errorMessage: Locator;
|
|
81
|
+
|
|
82
|
+
constructor(page: Page) {
|
|
83
|
+
super(page);
|
|
84
|
+
this.heading = page.getByRole('heading', { name: '[Title]' });
|
|
85
|
+
this.form = page.getByTestId('[page]-form');
|
|
86
|
+
this.submitButton = page.getByRole('button', { name: 'Submit' });
|
|
87
|
+
this.errorMessage = page.getByTestId('error-message');
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Actions
|
|
91
|
+
async fillForm(data: FormData) {
|
|
92
|
+
// Fill form fields
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
async submit() {
|
|
96
|
+
await this.submitButton.click();
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Assertions
|
|
100
|
+
async expectSuccess() {
|
|
101
|
+
await expect(this.errorMessage).not.toBeVisible();
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
async expectError(message: string) {
|
|
105
|
+
await expect(this.errorMessage).toContainText(message);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
## Login Page Example
|
|
111
|
+
|
|
112
|
+
```typescript
|
|
113
|
+
// tests/e2e/pages/login.page.ts
|
|
114
|
+
import { Page, Locator, expect } from '@playwright/test';
|
|
115
|
+
import { BasePage } from './base.page';
|
|
116
|
+
|
|
117
|
+
export class LoginPage extends BasePage {
|
|
118
|
+
readonly url = '/login';
|
|
119
|
+
|
|
120
|
+
readonly emailInput: Locator;
|
|
121
|
+
readonly passwordInput: Locator;
|
|
122
|
+
readonly loginButton: Locator;
|
|
123
|
+
readonly forgotPasswordLink: Locator;
|
|
124
|
+
readonly registerLink: Locator;
|
|
125
|
+
readonly errorMessage: Locator;
|
|
126
|
+
readonly rememberMeCheckbox: Locator;
|
|
127
|
+
|
|
128
|
+
constructor(page: Page) {
|
|
129
|
+
super(page);
|
|
130
|
+
this.emailInput = page.getByLabel('Email');
|
|
131
|
+
this.passwordInput = page.getByLabel('Password');
|
|
132
|
+
this.loginButton = page.getByRole('button', { name: 'Login' });
|
|
133
|
+
this.forgotPasswordLink = page.getByRole('link', { name: 'Forgot password' });
|
|
134
|
+
this.registerLink = page.getByRole('link', { name: 'Register' });
|
|
135
|
+
this.errorMessage = page.getByTestId('login-error');
|
|
136
|
+
this.rememberMeCheckbox = page.getByLabel('Remember me');
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
async login(email: string, password: string, rememberMe = false) {
|
|
140
|
+
await this.emailInput.fill(email);
|
|
141
|
+
await this.passwordInput.fill(password);
|
|
142
|
+
if (rememberMe) {
|
|
143
|
+
await this.rememberMeCheckbox.check();
|
|
144
|
+
}
|
|
145
|
+
await this.loginButton.click();
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
async loginAndExpectDashboard(email: string, password: string) {
|
|
149
|
+
await this.login(email, password);
|
|
150
|
+
await expect(this.page).toHaveURL('/dashboard');
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
async expectLoginError(message: string) {
|
|
154
|
+
await expect(this.errorMessage).toBeVisible();
|
|
155
|
+
await expect(this.errorMessage).toContainText(message);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
async goToRegister() {
|
|
159
|
+
await this.registerLink.click();
|
|
160
|
+
await expect(this.page).toHaveURL('/register');
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
async goToForgotPassword() {
|
|
164
|
+
await this.forgotPasswordLink.click();
|
|
165
|
+
await expect(this.page).toHaveURL('/forgot-password');
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
## Component Page Objects
|
|
171
|
+
|
|
172
|
+
```typescript
|
|
173
|
+
// tests/e2e/pages/components/navbar.component.ts
|
|
174
|
+
import { Page, Locator } from '@playwright/test';
|
|
175
|
+
|
|
176
|
+
export class NavbarComponent {
|
|
177
|
+
readonly page: Page;
|
|
178
|
+
readonly container: Locator;
|
|
179
|
+
readonly logo: Locator;
|
|
180
|
+
readonly searchInput: Locator;
|
|
181
|
+
readonly userMenu: Locator;
|
|
182
|
+
readonly logoutButton: Locator;
|
|
183
|
+
readonly notificationBell: Locator;
|
|
184
|
+
|
|
185
|
+
constructor(page: Page) {
|
|
186
|
+
this.page = page;
|
|
187
|
+
this.container = page.getByTestId('navbar');
|
|
188
|
+
this.logo = this.container.getByTestId('logo');
|
|
189
|
+
this.searchInput = this.container.getByPlaceholder('Search');
|
|
190
|
+
this.userMenu = this.container.getByTestId('user-menu');
|
|
191
|
+
this.logoutButton = this.container.getByRole('button', { name: 'Logout' });
|
|
192
|
+
this.notificationBell = this.container.getByTestId('notifications');
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
async search(query: string) {
|
|
196
|
+
await this.searchInput.fill(query);
|
|
197
|
+
await this.searchInput.press('Enter');
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
async openUserMenu() {
|
|
201
|
+
await this.userMenu.click();
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
async logout() {
|
|
205
|
+
await this.openUserMenu();
|
|
206
|
+
await this.logoutButton.click();
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
## Using Page Objects in Tests
|
|
212
|
+
|
|
213
|
+
```typescript
|
|
214
|
+
// tests/e2e/flows/auth.spec.ts
|
|
215
|
+
import { test, expect } from '../fixtures';
|
|
216
|
+
import { LoginPage } from '../pages/login.page';
|
|
217
|
+
import { DashboardPage } from '../pages/dashboard.page';
|
|
218
|
+
|
|
219
|
+
test.describe('Login Flow', () => {
|
|
220
|
+
let loginPage: LoginPage;
|
|
221
|
+
|
|
222
|
+
test.beforeEach(async ({ page }) => {
|
|
223
|
+
loginPage = new LoginPage(page);
|
|
224
|
+
await loginPage.goto();
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
test('should login successfully', async ({ page }) => {
|
|
228
|
+
await loginPage.loginAndExpectDashboard('user@test.com', 'password123');
|
|
229
|
+
|
|
230
|
+
const dashboardPage = new DashboardPage(page);
|
|
231
|
+
await expect(dashboardPage.welcomeMessage).toBeVisible();
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
test('should show error on invalid credentials', async () => {
|
|
235
|
+
await loginPage.login('wrong@email.com', 'wrongpassword');
|
|
236
|
+
await loginPage.expectLoginError('Invalid credentials');
|
|
237
|
+
});
|
|
238
|
+
});
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
## Critical Rules
|
|
242
|
+
|
|
243
|
+
1. **SINGLE RESPONSIBILITY** - One page = one class
|
|
244
|
+
2. **ENCAPSULATION** - Selectors private, actions public
|
|
245
|
+
3. **REUSABLE METHODS** - Common flows as methods
|
|
246
|
+
4. **SEMANTIC SELECTORS** - Use getByRole, getByLabel, getByTestId
|
|
247
|
+
5. **COMPOSITION** - Compose pages with component POMs
|