specweave 0.23.18 → 0.24.0

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.
Files changed (167) hide show
  1. package/.claude-plugin/marketplace.json +93 -49
  2. package/CLAUDE.md +137 -4
  3. package/dist/src/cli/helpers/ado-area-path-mapper.d.ts +89 -0
  4. package/dist/src/cli/helpers/ado-area-path-mapper.d.ts.map +1 -0
  5. package/dist/src/cli/helpers/ado-area-path-mapper.js +213 -0
  6. package/dist/src/cli/helpers/ado-area-path-mapper.js.map +1 -0
  7. package/dist/src/cli/helpers/issue-tracker/ado-auto-discover.d.ts +29 -0
  8. package/dist/src/cli/helpers/issue-tracker/ado-auto-discover.d.ts.map +1 -0
  9. package/dist/src/cli/helpers/issue-tracker/ado-auto-discover.js +109 -0
  10. package/dist/src/cli/helpers/issue-tracker/ado-auto-discover.js.map +1 -0
  11. package/dist/src/cli/helpers/issue-tracker/ado.d.ts +1 -0
  12. package/dist/src/cli/helpers/issue-tracker/ado.d.ts.map +1 -1
  13. package/dist/src/cli/helpers/issue-tracker/ado.js +2 -0
  14. package/dist/src/cli/helpers/issue-tracker/ado.js.map +1 -1
  15. package/dist/src/cli/helpers/smart-filter.d.ts +83 -0
  16. package/dist/src/cli/helpers/smart-filter.d.ts.map +1 -0
  17. package/dist/src/cli/helpers/smart-filter.js +265 -0
  18. package/dist/src/cli/helpers/smart-filter.js.map +1 -0
  19. package/dist/src/core/qa/quality-gate-decider.d.ts +1 -1
  20. package/dist/src/core/qa/quality-gate-decider.js +2 -2
  21. package/dist/src/core/qa/quality-gate-decider.js.map +1 -1
  22. package/dist/src/core/qa/risk-calculator.d.ts +2 -2
  23. package/dist/src/core/qa/risk-calculator.js +2 -2
  24. package/dist/src/core/validators/ac-presence-validator.d.ts +56 -0
  25. package/dist/src/core/validators/ac-presence-validator.d.ts.map +1 -0
  26. package/dist/src/core/validators/ac-presence-validator.js +149 -0
  27. package/dist/src/core/validators/ac-presence-validator.js.map +1 -0
  28. package/dist/src/integrations/ado/area-path-mapper.d.ts +137 -0
  29. package/dist/src/integrations/ado/area-path-mapper.d.ts.map +1 -0
  30. package/dist/src/integrations/ado/area-path-mapper.js +267 -0
  31. package/dist/src/integrations/ado/area-path-mapper.js.map +1 -0
  32. package/dist/src/integrations/jira/filter-processor.d.ts +126 -0
  33. package/dist/src/integrations/jira/filter-processor.d.ts.map +1 -0
  34. package/dist/src/integrations/jira/filter-processor.js +207 -0
  35. package/dist/src/integrations/jira/filter-processor.js.map +1 -0
  36. package/dist/src/integrations/jira/jira-client.d.ts +13 -0
  37. package/dist/src/integrations/jira/jira-client.d.ts.map +1 -1
  38. package/dist/src/integrations/jira/jira-client.js +33 -0
  39. package/dist/src/integrations/jira/jira-client.js.map +1 -1
  40. package/dist/src/utils/ac-embedder.d.ts +63 -0
  41. package/dist/src/utils/ac-embedder.d.ts.map +1 -0
  42. package/dist/src/utils/ac-embedder.js +217 -0
  43. package/dist/src/utils/ac-embedder.js.map +1 -0
  44. package/dist/src/utils/env-manager.d.ts +86 -0
  45. package/dist/src/utils/env-manager.d.ts.map +1 -0
  46. package/dist/src/utils/env-manager.js +188 -0
  47. package/dist/src/utils/env-manager.js.map +1 -0
  48. package/package.json +1 -1
  49. package/plugins/specweave/.claude-plugin/plugin.json +1 -1
  50. package/plugins/specweave/agents/AGENTS-INDEX.md +1 -1
  51. package/plugins/specweave/agents/increment-quality-judge-v2/AGENT.md +9 -9
  52. package/plugins/specweave/commands/specweave-do.md +37 -0
  53. package/plugins/specweave/commands/specweave-done.md +159 -0
  54. package/plugins/specweave/commands/specweave-embed-acs.md +446 -0
  55. package/plugins/specweave/commands/specweave-next.md +148 -3
  56. package/plugins/specweave/commands/specweave-qa.md +2 -2
  57. package/plugins/specweave/hooks/pre-increment-start.sh +168 -0
  58. package/plugins/specweave/skills/SKILLS-INDEX.md +1 -1
  59. package/plugins/specweave-ado/.claude-plugin/plugin.json +1 -1
  60. package/plugins/specweave-ado/commands/specweave-ado-import-projects.md +331 -0
  61. package/plugins/specweave-alternatives/.claude-plugin/plugin.json +10 -0
  62. package/plugins/specweave-alternatives/commands/alternatives-analyze.md +336 -0
  63. package/plugins/specweave-alternatives/skills/architecture-alternatives/SKILL.md +651 -0
  64. package/plugins/specweave-alternatives/skills/bmad-method/SKILL.md +420 -0
  65. package/plugins/specweave-alternatives/skills/spec-kit-expert/SKILL.md +487 -0
  66. package/plugins/specweave-backend/commands/api-scaffold.md +80 -0
  67. package/plugins/specweave-backend/commands/crud-generate.md +109 -0
  68. package/plugins/specweave-backend/commands/migration-generate.md +139 -0
  69. package/plugins/specweave-confluent/commands/connector-deploy.md +154 -0
  70. package/plugins/specweave-confluent/commands/ksqldb-query.md +179 -0
  71. package/plugins/specweave-confluent/commands/schema-register.md +123 -0
  72. package/plugins/specweave-core/.claude-plugin/plugin.json +21 -0
  73. package/plugins/specweave-core/commands/architecture-review.md +288 -0
  74. package/plugins/specweave-core/commands/code-review.md +213 -0
  75. package/plugins/specweave-core/commands/refactor-plan.md +249 -0
  76. package/plugins/specweave-core/skills/code-quality/SKILL.md +157 -0
  77. package/plugins/specweave-core/skills/design-patterns/SKILL.md +244 -0
  78. package/plugins/specweave-core/skills/software-architecture/SKILL.md +83 -0
  79. package/plugins/specweave-cost-optimizer/.claude-plugin/plugin.json +22 -0
  80. package/plugins/specweave-cost-optimizer/commands/cost-analyze.md +360 -0
  81. package/plugins/specweave-cost-optimizer/commands/cost-optimize.md +480 -0
  82. package/plugins/specweave-cost-optimizer/skills/aws-cost-expert/SKILL.md +416 -0
  83. package/plugins/specweave-cost-optimizer/skills/cloud-pricing/SKILL.md +325 -0
  84. package/plugins/specweave-cost-optimizer/skills/cost-optimization/SKILL.md +337 -0
  85. package/plugins/specweave-diagrams/.claude-plugin/plugin.json +1 -1
  86. package/plugins/specweave-diagrams/commands/diagrams-generate.md +168 -0
  87. package/plugins/specweave-docs/.claude-plugin/plugin.json +10 -0
  88. package/plugins/specweave-docs/commands/docs-generate.md +441 -0
  89. package/plugins/specweave-docs/commands/docs-init.md +334 -0
  90. package/plugins/specweave-docs/skills/docusaurus/SKILL.md +581 -0
  91. package/plugins/specweave-docs/skills/spec-driven-brainstorming/SKILL.md +689 -0
  92. package/plugins/specweave-docs/skills/technical-writing/SKILL.md +1039 -0
  93. package/plugins/specweave-docs-preview/.claude-plugin/plugin.json +1 -1
  94. package/plugins/specweave-figma/.claude-plugin/plugin.json +23 -0
  95. package/plugins/specweave-figma/commands/figma-import.md +690 -0
  96. package/plugins/specweave-figma/commands/figma-to-react.md +834 -0
  97. package/plugins/specweave-figma/commands/figma-tokens.md +815 -0
  98. package/plugins/specweave-frontend/.claude-plugin/plugin.json +21 -0
  99. package/plugins/specweave-frontend/agents/frontend-architect/AGENT.md +387 -0
  100. package/plugins/specweave-frontend/agents/frontend-architect/README.md +385 -0
  101. package/plugins/specweave-frontend/agents/frontend-architect/examples.md +590 -0
  102. package/plugins/specweave-frontend/agents/frontend-architect/templates/component-template.tsx +152 -0
  103. package/plugins/specweave-frontend/agents/frontend-architect/templates/hook-template.ts +311 -0
  104. package/plugins/specweave-frontend/agents/frontend-architect/templates/page-template.tsx +228 -0
  105. package/plugins/specweave-frontend/commands/component-generate.md +510 -0
  106. package/plugins/specweave-frontend/commands/design-system-init.md +494 -0
  107. package/plugins/specweave-frontend/commands/frontend-scaffold.md +207 -0
  108. package/plugins/specweave-frontend/commands/nextjs-setup.md +396 -0
  109. package/plugins/specweave-frontend/skills/design-system-architect/SKILL.md +278 -0
  110. package/plugins/specweave-frontend/skills/frontend/SKILL.md +420 -0
  111. package/plugins/specweave-frontend/skills/nextjs/SKILL.md +546 -0
  112. package/plugins/specweave-github/.claude-plugin/plugin.json +1 -1
  113. package/plugins/specweave-github/hooks/.specweave/logs/hooks-debug.log +194 -0
  114. package/plugins/specweave-infrastructure/.claude-plugin/plugin.json +1 -1
  115. package/plugins/specweave-jira/.claude-plugin/plugin.json +1 -1
  116. package/plugins/specweave-jira/commands/import-projects.js +183 -0
  117. package/plugins/specweave-jira/commands/import-projects.md +97 -0
  118. package/plugins/specweave-jira/commands/import-projects.ts +288 -0
  119. package/plugins/specweave-jira/commands/specweave-jira-import-projects.md +298 -0
  120. package/plugins/specweave-kafka/.claude-plugin/plugin.json +1 -1
  121. package/plugins/specweave-kafka-streams/.claude-plugin/plugin.json +1 -1
  122. package/plugins/specweave-kubernetes/commands/cluster-setup.md +262 -0
  123. package/plugins/specweave-kubernetes/commands/deployment-generate.md +242 -0
  124. package/plugins/specweave-kubernetes/commands/helm-scaffold.md +333 -0
  125. package/plugins/specweave-ml/.claude-plugin/plugin.json +1 -1
  126. package/plugins/specweave-mobile/commands/app-scaffold.md +233 -0
  127. package/plugins/specweave-mobile/commands/build-config.md +256 -0
  128. package/plugins/specweave-mobile/commands/screen-generate.md +289 -0
  129. package/plugins/specweave-n8n/.claude-plugin/plugin.json +1 -1
  130. package/plugins/specweave-plugin-dev/.claude-plugin/plugin.json +13 -12
  131. package/plugins/specweave-plugin-dev/commands/plugin-create.md +333 -0
  132. package/plugins/specweave-plugin-dev/commands/plugin-publish.md +339 -0
  133. package/plugins/specweave-plugin-dev/commands/plugin-test.md +293 -0
  134. package/plugins/specweave-plugin-dev/skills/claude-sdk/SKILL.md +162 -0
  135. package/plugins/specweave-plugin-dev/skills/marketplace-publishing/SKILL.md +263 -0
  136. package/plugins/specweave-plugin-dev/skills/plugin-development/SKILL.md +316 -0
  137. package/plugins/specweave-release/.claude-plugin/plugin.json +1 -1
  138. package/plugins/specweave-release/commands/specweave-release-npm.md +110 -0
  139. package/plugins/specweave-release/hooks/.specweave/logs/dora-tracking.log +168 -0
  140. package/plugins/specweave-testing/.claude-plugin/plugin.json +21 -0
  141. package/plugins/specweave-testing/agents/qa-engineer/AGENT.md +797 -0
  142. package/plugins/specweave-testing/agents/qa-engineer/README.md +443 -0
  143. package/plugins/specweave-testing/agents/qa-engineer/templates/playwright-e2e-test.ts +470 -0
  144. package/plugins/specweave-testing/agents/qa-engineer/templates/test-data-factory.ts +507 -0
  145. package/plugins/specweave-testing/agents/qa-engineer/templates/vitest-unit-test.ts +400 -0
  146. package/plugins/specweave-testing/agents/qa-engineer/test-strategies.md +726 -0
  147. package/plugins/specweave-testing/commands/e2e-setup.md +1081 -0
  148. package/plugins/specweave-testing/commands/test-coverage.md +979 -0
  149. package/plugins/specweave-testing/commands/test-generate.md +1156 -0
  150. package/plugins/specweave-testing/commands/test-init.md +409 -0
  151. package/plugins/specweave-testing/skills/e2e-playwright/SKILL.md +769 -0
  152. package/plugins/specweave-testing/skills/tdd-expert/SKILL.md +934 -0
  153. package/plugins/specweave-testing/skills/unit-testing-expert/SKILL.md +1011 -0
  154. package/plugins/specweave-tooling/.claude-plugin/plugin.json +22 -0
  155. package/plugins/specweave-tooling/commands/specweave-tooling-skill-create.md +691 -0
  156. package/plugins/specweave-tooling/commands/specweave-tooling-skill-package.md +751 -0
  157. package/plugins/specweave-tooling/commands/specweave-tooling-skill-validate.md +858 -0
  158. package/plugins/specweave-ui/.claude-plugin/plugin.json +10 -0
  159. package/plugins/specweave-ui/commands/ui-automate.md +199 -0
  160. package/plugins/specweave-ui/commands/ui-inspect.md +70 -0
  161. package/plugins/specweave-ui/skills/browser-automation/SKILL.md +314 -0
  162. package/plugins/specweave-ui/skills/ui-testing/SKILL.md +716 -0
  163. package/plugins/specweave-ui/skills/visual-regression/SKILL.md +728 -0
  164. package/plugins/specweave/commands/check-hooks.md +0 -257
  165. package/plugins/specweave/commands/specweave-archive-increments.md +0 -82
  166. package/plugins/specweave-plugin-dev/skills/plugin-expert/SKILL.md +0 -1231
  167. /package/plugins/specweave/{agents/code-reviewer.md → skills/code-reviewer/SKILL.md} +0 -0
@@ -0,0 +1,1081 @@
1
+ # /specweave-testing:e2e-setup
2
+
3
+ Set up comprehensive Playwright E2E testing with best practices, page objects, and CI/CD integration.
4
+
5
+ You are an expert E2E testing engineer who implements production-ready Playwright test suites.
6
+
7
+ ## Your Task
8
+
9
+ Set up a complete Playwright E2E testing framework with page objects, fixtures, and testing patterns.
10
+
11
+ ### 1. Playwright Stack Features
12
+
13
+ **Cross-Browser Testing**:
14
+ - Chromium, Firefox, WebKit support
15
+ - Mobile viewport emulation
16
+ - Device-specific testing (iPhone, Pixel, etc.)
17
+ - Browser context isolation
18
+ - Persistent state management
19
+
20
+ **Reliability Features**:
21
+ - Auto-wait for elements
22
+ - Network idle detection
23
+ - Retry mechanisms
24
+ - Screenshot/video on failure
25
+ - Trace recording for debugging
26
+
27
+ **Performance**:
28
+ - Parallel test execution
29
+ - Sharding for CI/CD
30
+ - Browser reuse
31
+ - Worker threads
32
+ - Test isolation
33
+
34
+ ### 2. Advanced Playwright Configuration
35
+
36
+ **playwright.config.ts** (Production-Grade):
37
+ ```typescript
38
+ import { defineConfig, devices } from '@playwright/test';
39
+ import dotenv from 'dotenv';
40
+
41
+ dotenv.config();
42
+
43
+ export default defineConfig({
44
+ testDir: './tests/e2e',
45
+ fullyParallel: true,
46
+ forbidOnly: !!process.env.CI,
47
+ retries: process.env.CI ? 2 : 0,
48
+ workers: process.env.CI ? 1 : undefined,
49
+
50
+ // Reporter configuration
51
+ reporter: [
52
+ ['html', { outputFolder: 'playwright-report' }],
53
+ ['json', { outputFile: 'test-results/results.json' }],
54
+ ['junit', { outputFile: 'test-results/junit.xml' }],
55
+ ['github'], // GitHub Actions annotations
56
+ ],
57
+
58
+ use: {
59
+ // Base URL for navigation
60
+ baseURL: process.env.BASE_URL || 'http://localhost:3000',
61
+
62
+ // Tracing and debugging
63
+ trace: 'on-first-retry',
64
+ screenshot: 'only-on-failure',
65
+ video: 'retain-on-failure',
66
+
67
+ // Timeouts
68
+ actionTimeout: 10000,
69
+ navigationTimeout: 30000,
70
+
71
+ // Browser options
72
+ viewport: { width: 1280, height: 720 },
73
+ ignoreHTTPSErrors: true,
74
+
75
+ // Collect HTTP Archive (HAR) files
76
+ recordHar: process.env.CI ? undefined : { path: 'test-results/har' },
77
+ },
78
+
79
+ // Test timeout
80
+ timeout: 30000,
81
+
82
+ // Global setup/teardown
83
+ globalSetup: require.resolve('./tests/e2e/global-setup.ts'),
84
+ globalTeardown: require.resolve('./tests/e2e/global-teardown.ts'),
85
+
86
+ projects: [
87
+ // Setup project for authentication
88
+ {
89
+ name: 'setup',
90
+ testMatch: /.*\.setup\.ts/,
91
+ },
92
+
93
+ // Desktop browsers
94
+ {
95
+ name: 'chromium',
96
+ use: {
97
+ ...devices['Desktop Chrome'],
98
+ storageState: 'playwright/.auth/user.json',
99
+ },
100
+ dependencies: ['setup'],
101
+ },
102
+ {
103
+ name: 'firefox',
104
+ use: {
105
+ ...devices['Desktop Firefox'],
106
+ storageState: 'playwright/.auth/user.json',
107
+ },
108
+ dependencies: ['setup'],
109
+ },
110
+ {
111
+ name: 'webkit',
112
+ use: {
113
+ ...devices['Desktop Safari'],
114
+ storageState: 'playwright/.auth/user.json',
115
+ },
116
+ dependencies: ['setup'],
117
+ },
118
+
119
+ // Mobile browsers
120
+ {
121
+ name: 'mobile-chrome',
122
+ use: {
123
+ ...devices['Pixel 5'],
124
+ storageState: 'playwright/.auth/user.json',
125
+ },
126
+ dependencies: ['setup'],
127
+ },
128
+ {
129
+ name: 'mobile-safari',
130
+ use: {
131
+ ...devices['iPhone 12'],
132
+ storageState: 'playwright/.auth/user.json',
133
+ },
134
+ dependencies: ['setup'],
135
+ },
136
+
137
+ // Branded browsers
138
+ {
139
+ name: 'edge',
140
+ use: {
141
+ ...devices['Desktop Edge'],
142
+ channel: 'msedge',
143
+ storageState: 'playwright/.auth/user.json',
144
+ },
145
+ dependencies: ['setup'],
146
+ },
147
+ {
148
+ name: 'chrome',
149
+ use: {
150
+ ...devices['Desktop Chrome'],
151
+ channel: 'chrome',
152
+ storageState: 'playwright/.auth/user.json',
153
+ },
154
+ dependencies: ['setup'],
155
+ },
156
+ ],
157
+
158
+ // Web server configuration
159
+ webServer: {
160
+ command: 'npm run dev',
161
+ url: 'http://localhost:3000',
162
+ reuseExistingServer: !process.env.CI,
163
+ timeout: 120000,
164
+ stdout: 'pipe',
165
+ stderr: 'pipe',
166
+ },
167
+ });
168
+ ```
169
+
170
+ ### 3. Page Object Model (POM)
171
+
172
+ **tests/e2e/pages/BasePage.ts**:
173
+ ```typescript
174
+ import { Page, Locator } from '@playwright/test';
175
+
176
+ export abstract class BasePage {
177
+ readonly page: Page;
178
+
179
+ constructor(page: Page) {
180
+ this.page = page;
181
+ }
182
+
183
+ // Common navigation
184
+ async goto(path: string) {
185
+ await this.page.goto(path);
186
+ }
187
+
188
+ // Wait helpers
189
+ async waitForNetworkIdle() {
190
+ await this.page.waitForLoadState('networkidle');
191
+ }
192
+
193
+ async waitForDomContentLoaded() {
194
+ await this.page.waitForLoadState('domcontentloaded');
195
+ }
196
+
197
+ // Screenshot helpers
198
+ async takeScreenshot(name: string) {
199
+ await this.page.screenshot({
200
+ path: `test-results/screenshots/${name}.png`,
201
+ fullPage: true,
202
+ });
203
+ }
204
+
205
+ // Cookie helpers
206
+ async getCookies() {
207
+ return await this.page.context().cookies();
208
+ }
209
+
210
+ async setCookies(cookies: any[]) {
211
+ await this.page.context().addCookies(cookies);
212
+ }
213
+
214
+ // Local storage helpers
215
+ async getLocalStorage(key: string): Promise<string | null> {
216
+ return await this.page.evaluate((key) => {
217
+ return localStorage.getItem(key);
218
+ }, key);
219
+ }
220
+
221
+ async setLocalStorage(key: string, value: string) {
222
+ await this.page.evaluate(({ key, value }) => {
223
+ localStorage.setItem(key, value);
224
+ }, { key, value });
225
+ }
226
+
227
+ // Common assertions
228
+ async assertUrl(expectedUrl: string) {
229
+ await this.page.waitForURL(expectedUrl);
230
+ }
231
+
232
+ async assertTitle(expectedTitle: string) {
233
+ await this.page.waitForFunction(
234
+ (title) => document.title === title,
235
+ expectedTitle
236
+ );
237
+ }
238
+ }
239
+ ```
240
+
241
+ **tests/e2e/pages/LoginPage.ts**:
242
+ ```typescript
243
+ import { Page, Locator, expect } from '@playwright/test';
244
+ import { BasePage } from './BasePage';
245
+
246
+ export class LoginPage extends BasePage {
247
+ // Locators
248
+ readonly emailInput: Locator;
249
+ readonly passwordInput: Locator;
250
+ readonly loginButton: Locator;
251
+ readonly errorMessage: Locator;
252
+ readonly rememberMeCheckbox: Locator;
253
+ readonly forgotPasswordLink: Locator;
254
+
255
+ constructor(page: Page) {
256
+ super(page);
257
+ this.emailInput = page.locator('input[name="email"]');
258
+ this.passwordInput = page.locator('input[name="password"]');
259
+ this.loginButton = page.locator('button[type="submit"]');
260
+ this.errorMessage = page.locator('[role="alert"]');
261
+ this.rememberMeCheckbox = page.locator('input[name="rememberMe"]');
262
+ this.forgotPasswordLink = page.locator('a[href="/forgot-password"]');
263
+ }
264
+
265
+ // Actions
266
+ async navigate() {
267
+ await this.goto('/login');
268
+ }
269
+
270
+ async login(email: string, password: string, rememberMe = false) {
271
+ await this.emailInput.fill(email);
272
+ await this.passwordInput.fill(password);
273
+
274
+ if (rememberMe) {
275
+ await this.rememberMeCheckbox.check();
276
+ }
277
+
278
+ await this.loginButton.click();
279
+ }
280
+
281
+ async loginAsAdmin() {
282
+ await this.login(
283
+ process.env.ADMIN_EMAIL || 'admin@example.com',
284
+ process.env.ADMIN_PASSWORD || 'admin123'
285
+ );
286
+ }
287
+
288
+ async loginAsUser() {
289
+ await this.login(
290
+ process.env.USER_EMAIL || 'user@example.com',
291
+ process.env.USER_PASSWORD || 'user123'
292
+ );
293
+ }
294
+
295
+ // Assertions
296
+ async assertErrorMessage(expectedMessage: string) {
297
+ await expect(this.errorMessage).toContainText(expectedMessage);
298
+ }
299
+
300
+ async assertLoginSuccessful() {
301
+ await this.page.waitForURL(/\/(dashboard|home)/);
302
+ }
303
+
304
+ async assertOnLoginPage() {
305
+ await expect(this.page).toHaveURL(/\/login/);
306
+ }
307
+ }
308
+ ```
309
+
310
+ **tests/e2e/pages/DashboardPage.ts**:
311
+ ```typescript
312
+ import { Page, Locator, expect } from '@playwright/test';
313
+ import { BasePage } from './BasePage';
314
+
315
+ export class DashboardPage extends BasePage {
316
+ readonly welcomeMessage: Locator;
317
+ readonly userMenu: Locator;
318
+ readonly logoutButton: Locator;
319
+ readonly notificationBadge: Locator;
320
+ readonly searchInput: Locator;
321
+
322
+ constructor(page: Page) {
323
+ super(page);
324
+ this.welcomeMessage = page.locator('h1');
325
+ this.userMenu = page.locator('[data-testid="user-menu"]');
326
+ this.logoutButton = page.locator('button:has-text("Logout")');
327
+ this.notificationBadge = page.locator('[data-testid="notification-badge"]');
328
+ this.searchInput = page.locator('input[placeholder="Search..."]');
329
+ }
330
+
331
+ async navigate() {
332
+ await this.goto('/dashboard');
333
+ }
334
+
335
+ async logout() {
336
+ await this.userMenu.click();
337
+ await this.logoutButton.click();
338
+ }
339
+
340
+ async search(query: string) {
341
+ await this.searchInput.fill(query);
342
+ await this.searchInput.press('Enter');
343
+ }
344
+
345
+ async getNotificationCount(): Promise<number> {
346
+ const text = await this.notificationBadge.textContent();
347
+ return parseInt(text || '0', 10);
348
+ }
349
+
350
+ async assertWelcomeMessage(username: string) {
351
+ await expect(this.welcomeMessage).toContainText(`Welcome, ${username}`);
352
+ }
353
+
354
+ async assertOnDashboard() {
355
+ await expect(this.page).toHaveURL(/\/dashboard/);
356
+ }
357
+ }
358
+ ```
359
+
360
+ ### 4. Custom Fixtures
361
+
362
+ **tests/e2e/fixtures/auth.fixture.ts**:
363
+ ```typescript
364
+ import { test as base } from '@playwright/test';
365
+ import { LoginPage } from '../pages/LoginPage';
366
+ import { DashboardPage } from '../pages/DashboardPage';
367
+
368
+ type AuthFixtures = {
369
+ authenticatedPage: Page;
370
+ loginPage: LoginPage;
371
+ dashboardPage: DashboardPage;
372
+ };
373
+
374
+ export const test = base.extend<AuthFixtures>({
375
+ authenticatedPage: async ({ page }, use) => {
376
+ const loginPage = new LoginPage(page);
377
+ await loginPage.navigate();
378
+ await loginPage.loginAsUser();
379
+ await use(page);
380
+ },
381
+
382
+ loginPage: async ({ page }, use) => {
383
+ const loginPage = new LoginPage(page);
384
+ await use(loginPage);
385
+ },
386
+
387
+ dashboardPage: async ({ page }, use) => {
388
+ const dashboardPage = new DashboardPage(page);
389
+ await use(dashboardPage);
390
+ },
391
+ });
392
+
393
+ export { expect } from '@playwright/test';
394
+ ```
395
+
396
+ **tests/e2e/fixtures/api.fixture.ts**:
397
+ ```typescript
398
+ import { test as base, APIRequestContext } from '@playwright/test';
399
+
400
+ type ApiFixtures = {
401
+ apiContext: APIRequestContext;
402
+ };
403
+
404
+ export const test = base.extend<ApiFixtures>({
405
+ apiContext: async ({ playwright }, use) => {
406
+ const context = await playwright.request.newContext({
407
+ baseURL: process.env.API_BASE_URL || 'http://localhost:3000/api',
408
+ extraHTTPHeaders: {
409
+ 'Accept': 'application/json',
410
+ 'Content-Type': 'application/json',
411
+ },
412
+ });
413
+ await use(context);
414
+ await context.dispose();
415
+ },
416
+ });
417
+
418
+ export { expect } from '@playwright/test';
419
+ ```
420
+
421
+ ### 5. Global Setup and Teardown
422
+
423
+ **tests/e2e/global-setup.ts**:
424
+ ```typescript
425
+ import { chromium, FullConfig } from '@playwright/test';
426
+ import path from 'path';
427
+ import fs from 'fs';
428
+
429
+ async function globalSetup(config: FullConfig) {
430
+ const { baseURL, storageState } = config.projects[0].use;
431
+
432
+ // Create auth directory
433
+ const authDir = path.dirname(storageState as string);
434
+ if (!fs.existsSync(authDir)) {
435
+ fs.mkdirSync(authDir, { recursive: true });
436
+ }
437
+
438
+ // Launch browser and authenticate
439
+ const browser = await chromium.launch();
440
+ const context = await browser.newContext();
441
+ const page = await context.newPage();
442
+
443
+ try {
444
+ // Navigate to login
445
+ await page.goto(`${baseURL}/login`);
446
+
447
+ // Perform login
448
+ await page.fill('input[name="email"]', process.env.USER_EMAIL || 'test@example.com');
449
+ await page.fill('input[name="password"]', process.env.USER_PASSWORD || 'password123');
450
+ await page.click('button[type="submit"]');
451
+
452
+ // Wait for successful login
453
+ await page.waitForURL(/\/(dashboard|home)/);
454
+
455
+ // Save authentication state
456
+ await context.storageState({ path: storageState as string });
457
+
458
+ console.log('✓ Global setup: Authentication completed');
459
+ } catch (error) {
460
+ console.error('✗ Global setup: Authentication failed', error);
461
+ throw error;
462
+ } finally {
463
+ await browser.close();
464
+ }
465
+ }
466
+
467
+ export default globalSetup;
468
+ ```
469
+
470
+ **tests/e2e/global-teardown.ts**:
471
+ ```typescript
472
+ import { FullConfig } from '@playwright/test';
473
+ import fs from 'fs';
474
+ import path from 'path';
475
+
476
+ async function globalTeardown(config: FullConfig) {
477
+ try {
478
+ // Clean up auth state
479
+ const authDir = 'playwright/.auth';
480
+ if (fs.existsSync(authDir)) {
481
+ fs.rmSync(authDir, { recursive: true });
482
+ console.log('✓ Global teardown: Cleaned auth state');
483
+ }
484
+
485
+ // Clean up test artifacts if not in CI
486
+ if (!process.env.CI) {
487
+ const artifactDirs = ['test-results', 'playwright-report'];
488
+ artifactDirs.forEach(dir => {
489
+ if (fs.existsSync(dir)) {
490
+ const files = fs.readdirSync(dir);
491
+ console.log(`✓ Global teardown: ${dir} contains ${files.length} files`);
492
+ }
493
+ });
494
+ }
495
+ } catch (error) {
496
+ console.error('✗ Global teardown: Cleanup failed', error);
497
+ }
498
+ }
499
+
500
+ export default globalTeardown;
501
+ ```
502
+
503
+ ### 6. Authentication Setup Tests
504
+
505
+ **tests/e2e/auth.setup.ts**:
506
+ ```typescript
507
+ import { test as setup, expect } from '@playwright/test';
508
+ import path from 'path';
509
+
510
+ const authFile = path.join(__dirname, '../../playwright/.auth/user.json');
511
+
512
+ setup('authenticate', async ({ page }) => {
513
+ // Navigate to login page
514
+ await page.goto('/login');
515
+
516
+ // Perform login
517
+ await page.fill('input[name="email"]', process.env.USER_EMAIL || 'test@example.com');
518
+ await page.fill('input[name="password"]', process.env.USER_PASSWORD || 'password123');
519
+ await page.click('button[type="submit"]');
520
+
521
+ // Wait for successful navigation
522
+ await page.waitForURL(/\/(dashboard|home)/);
523
+
524
+ // Verify authentication succeeded
525
+ await expect(page.locator('[data-testid="user-menu"]')).toBeVisible();
526
+
527
+ // Save signed-in state
528
+ await page.context().storageState({ path: authFile });
529
+ });
530
+
531
+ setup('admin authenticate', async ({ page }) => {
532
+ await page.goto('/login');
533
+
534
+ await page.fill('input[name="email"]', process.env.ADMIN_EMAIL || 'admin@example.com');
535
+ await page.fill('input[name="password"]', process.env.ADMIN_PASSWORD || 'admin123');
536
+ await page.click('button[type="submit"]');
537
+
538
+ await page.waitForURL(/\/(dashboard|admin)/);
539
+ await expect(page.locator('[data-testid="admin-menu"]')).toBeVisible();
540
+
541
+ await page.context().storageState({
542
+ path: path.join(__dirname, '../../playwright/.auth/admin.json')
543
+ });
544
+ });
545
+ ```
546
+
547
+ ### 7. Test Utilities
548
+
549
+ **tests/e2e/utils/test-helpers.ts**:
550
+ ```typescript
551
+ import { Page, expect } from '@playwright/test';
552
+
553
+ export class TestHelpers {
554
+ static async waitForApiResponse(page: Page, urlPattern: string | RegExp, timeout = 5000) {
555
+ return await page.waitForResponse(
556
+ response => {
557
+ const url = response.url();
558
+ const matches = typeof urlPattern === 'string'
559
+ ? url.includes(urlPattern)
560
+ : urlPattern.test(url);
561
+ return matches && response.status() === 200;
562
+ },
563
+ { timeout }
564
+ );
565
+ }
566
+
567
+ static async interceptApi(page: Page, urlPattern: string | RegExp, mockResponse: any) {
568
+ await page.route(urlPattern, route => {
569
+ route.fulfill({
570
+ status: 200,
571
+ contentType: 'application/json',
572
+ body: JSON.stringify(mockResponse),
573
+ });
574
+ });
575
+ }
576
+
577
+ static async blockRequests(page: Page, resourceTypes: string[]) {
578
+ await page.route('**/*', route => {
579
+ if (resourceTypes.includes(route.request().resourceType())) {
580
+ route.abort();
581
+ } else {
582
+ route.continue();
583
+ }
584
+ });
585
+ }
586
+
587
+ static async mockGeolocation(page: Page, latitude: number, longitude: number) {
588
+ await page.context().setGeolocation({ latitude, longitude });
589
+ await page.context().grantPermissions(['geolocation']);
590
+ }
591
+
592
+ static async fillForm(page: Page, formData: Record<string, string>) {
593
+ for (const [name, value] of Object.entries(formData)) {
594
+ await page.fill(`[name="${name}"]`, value);
595
+ }
596
+ }
597
+
598
+ static async selectDropdown(page: Page, selector: string, value: string) {
599
+ await page.selectOption(selector, value);
600
+ }
601
+
602
+ static async uploadFile(page: Page, selector: string, filePath: string) {
603
+ await page.setInputFiles(selector, filePath);
604
+ }
605
+
606
+ static async scrollToElement(page: Page, selector: string) {
607
+ await page.locator(selector).scrollIntoViewIfNeeded();
608
+ }
609
+
610
+ static async assertAccessibility(page: Page) {
611
+ // Basic accessibility checks
612
+ const violations = await page.evaluate(() => {
613
+ const issues: string[] = [];
614
+
615
+ // Check for alt text on images
616
+ document.querySelectorAll('img').forEach(img => {
617
+ if (!img.alt) {
618
+ issues.push(`Image missing alt text: ${img.src}`);
619
+ }
620
+ });
621
+
622
+ // Check for form labels
623
+ document.querySelectorAll('input, textarea, select').forEach(input => {
624
+ const id = input.getAttribute('id');
625
+ if (id && !document.querySelector(`label[for="${id}"]`)) {
626
+ issues.push(`Form element missing label: ${id}`);
627
+ }
628
+ });
629
+
630
+ return issues;
631
+ });
632
+
633
+ expect(violations).toHaveLength(0);
634
+ }
635
+ }
636
+ ```
637
+
638
+ **tests/e2e/utils/mock-data.ts**:
639
+ ```typescript
640
+ export const mockUsers = [
641
+ {
642
+ id: '1',
643
+ email: 'john@example.com',
644
+ name: 'John Doe',
645
+ role: 'user',
646
+ },
647
+ {
648
+ id: '2',
649
+ email: 'admin@example.com',
650
+ name: 'Admin User',
651
+ role: 'admin',
652
+ },
653
+ ];
654
+
655
+ export const mockProducts = [
656
+ {
657
+ id: '1',
658
+ name: 'Product A',
659
+ price: 29.99,
660
+ inStock: true,
661
+ },
662
+ {
663
+ id: '2',
664
+ name: 'Product B',
665
+ price: 49.99,
666
+ inStock: false,
667
+ },
668
+ ];
669
+
670
+ export function createMockApiResponse(data: any, delay = 0) {
671
+ return {
672
+ body: JSON.stringify({ data, success: true }),
673
+ status: 200,
674
+ headers: { 'Content-Type': 'application/json' },
675
+ delay,
676
+ };
677
+ }
678
+ ```
679
+
680
+ ### 8. Example E2E Tests
681
+
682
+ **tests/e2e/auth/login.spec.ts**:
683
+ ```typescript
684
+ import { test, expect } from '@playwright/test';
685
+ import { LoginPage } from '../pages/LoginPage';
686
+ import { DashboardPage } from '../pages/DashboardPage';
687
+
688
+ test.describe('Login Flow', () => {
689
+ let loginPage: LoginPage;
690
+ let dashboardPage: DashboardPage;
691
+
692
+ test.beforeEach(async ({ page }) => {
693
+ loginPage = new LoginPage(page);
694
+ dashboardPage = new DashboardPage(page);
695
+ await loginPage.navigate();
696
+ });
697
+
698
+ test('should login with valid credentials', async ({ page }) => {
699
+ await loginPage.login('test@example.com', 'password123');
700
+ await dashboardPage.assertOnDashboard();
701
+ await dashboardPage.assertWelcomeMessage('Test User');
702
+ });
703
+
704
+ test('should show error for invalid credentials', async () => {
705
+ await loginPage.login('wrong@example.com', 'wrongpassword');
706
+ await loginPage.assertErrorMessage('Invalid credentials');
707
+ await loginPage.assertOnLoginPage();
708
+ });
709
+
710
+ test('should validate empty fields', async () => {
711
+ await loginPage.loginButton.click();
712
+
713
+ await expect(loginPage.emailInput).toHaveAttribute('aria-invalid', 'true');
714
+ await expect(loginPage.passwordInput).toHaveAttribute('aria-invalid', 'true');
715
+ });
716
+
717
+ test('should remember user when checkbox checked', async ({ page }) => {
718
+ await loginPage.login('test@example.com', 'password123', true);
719
+ await dashboardPage.assertOnDashboard();
720
+
721
+ const cookies = await page.context().cookies();
722
+ const rememberMeCookie = cookies.find(c => c.name === 'rememberMe');
723
+ expect(rememberMeCookie).toBeDefined();
724
+ });
725
+
726
+ test('should navigate to forgot password', async ({ page }) => {
727
+ await loginPage.forgotPasswordLink.click();
728
+ await expect(page).toHaveURL('/forgot-password');
729
+ });
730
+ });
731
+ ```
732
+
733
+ **tests/e2e/api/users.spec.ts**:
734
+ ```typescript
735
+ import { test, expect } from '../fixtures/api.fixture';
736
+
737
+ test.describe('User API', () => {
738
+ test('should fetch users list', async ({ apiContext }) => {
739
+ const response = await apiContext.get('/users');
740
+ expect(response.ok()).toBeTruthy();
741
+
742
+ const data = await response.json();
743
+ expect(data).toHaveProperty('users');
744
+ expect(Array.isArray(data.users)).toBeTruthy();
745
+ });
746
+
747
+ test('should create new user', async ({ apiContext }) => {
748
+ const newUser = {
749
+ email: 'newuser@example.com',
750
+ name: 'New User',
751
+ role: 'user',
752
+ };
753
+
754
+ const response = await apiContext.post('/users', { data: newUser });
755
+ expect(response.ok()).toBeTruthy();
756
+
757
+ const data = await response.json();
758
+ expect(data.user).toMatchObject(newUser);
759
+ expect(data.user.id).toBeDefined();
760
+ });
761
+
762
+ test('should update user', async ({ apiContext }) => {
763
+ const updates = { name: 'Updated Name' };
764
+
765
+ const response = await apiContext.patch('/users/1', { data: updates });
766
+ expect(response.ok()).toBeTruthy();
767
+
768
+ const data = await response.json();
769
+ expect(data.user.name).toBe(updates.name);
770
+ });
771
+
772
+ test('should delete user', async ({ apiContext }) => {
773
+ const response = await apiContext.delete('/users/1');
774
+ expect(response.ok()).toBeTruthy();
775
+
776
+ // Verify deletion
777
+ const getResponse = await apiContext.get('/users/1');
778
+ expect(getResponse.status()).toBe(404);
779
+ });
780
+
781
+ test('should handle authentication errors', async ({ apiContext }) => {
782
+ const response = await apiContext.get('/users/protected', {
783
+ headers: { 'Authorization': 'Bearer invalid-token' },
784
+ });
785
+ expect(response.status()).toBe(401);
786
+ });
787
+ });
788
+ ```
789
+
790
+ ### 9. Visual Regression Testing
791
+
792
+ **tests/e2e/visual/homepage.spec.ts**:
793
+ ```typescript
794
+ import { test, expect } from '@playwright/test';
795
+
796
+ test.describe('Visual Regression', () => {
797
+ test('homepage should match snapshot', async ({ page }) => {
798
+ await page.goto('/');
799
+ await page.waitForLoadState('networkidle');
800
+
801
+ await expect(page).toHaveScreenshot('homepage.png', {
802
+ fullPage: true,
803
+ maxDiffPixels: 100,
804
+ });
805
+ });
806
+
807
+ test('login page should match snapshot', async ({ page }) => {
808
+ await page.goto('/login');
809
+
810
+ await expect(page).toHaveScreenshot('login.png', {
811
+ mask: [page.locator('.timestamp')], // Mask dynamic content
812
+ });
813
+ });
814
+
815
+ test('mobile viewport should match snapshot', async ({ page }) => {
816
+ await page.setViewportSize({ width: 375, height: 667 });
817
+ await page.goto('/');
818
+
819
+ await expect(page).toHaveScreenshot('homepage-mobile.png');
820
+ });
821
+ });
822
+ ```
823
+
824
+ ### 10. Performance Testing
825
+
826
+ **tests/e2e/performance/metrics.spec.ts**:
827
+ ```typescript
828
+ import { test, expect } from '@playwright/test';
829
+
830
+ test.describe('Performance Metrics', () => {
831
+ test('should meet Core Web Vitals thresholds', async ({ page }) => {
832
+ await page.goto('/');
833
+
834
+ const metrics = await page.evaluate(() => {
835
+ return new Promise((resolve) => {
836
+ new PerformanceObserver((list) => {
837
+ const entries = list.getEntries();
838
+ const values = entries.reduce((acc, entry) => {
839
+ acc[entry.name] = entry.value;
840
+ return acc;
841
+ }, {} as Record<string, number>);
842
+ resolve(values);
843
+ }).observe({ entryTypes: ['measure', 'navigation'] });
844
+ });
845
+ });
846
+
847
+ // Verify performance metrics
848
+ expect(metrics['first-contentful-paint']).toBeLessThan(1800); // FCP < 1.8s
849
+ expect(metrics['largest-contentful-paint']).toBeLessThan(2500); // LCP < 2.5s
850
+ });
851
+
852
+ test('should load page within 3 seconds', async ({ page }) => {
853
+ const startTime = Date.now();
854
+ await page.goto('/');
855
+ await page.waitForLoadState('load');
856
+ const loadTime = Date.now() - startTime;
857
+
858
+ expect(loadTime).toBeLessThan(3000);
859
+ });
860
+
861
+ test('should have minimal bundle size', async ({ page }) => {
862
+ const response = await page.goto('/');
863
+ const transferSize = await response?.request().sizes().then(s => s.responseBodySize);
864
+
865
+ expect(transferSize).toBeLessThan(500000); // < 500KB
866
+ });
867
+ });
868
+ ```
869
+
870
+ ### 11. CI/CD Integration
871
+
872
+ **GitHub Actions (.github/workflows/e2e.yml)**:
873
+ ```yaml
874
+ name: E2E Tests
875
+
876
+ on:
877
+ push:
878
+ branches: [main, develop]
879
+ pull_request:
880
+ branches: [main]
881
+
882
+ jobs:
883
+ test:
884
+ timeout-minutes: 60
885
+ runs-on: ubuntu-latest
886
+ strategy:
887
+ matrix:
888
+ browser: [chromium, firefox, webkit]
889
+
890
+ steps:
891
+ - uses: actions/checkout@v4
892
+
893
+ - uses: actions/setup-node@v4
894
+ with:
895
+ node-version: '20'
896
+ cache: 'npm'
897
+
898
+ - name: Install dependencies
899
+ run: npm ci
900
+
901
+ - name: Install Playwright Browsers
902
+ run: npx playwright install --with-deps ${{ matrix.browser }}
903
+
904
+ - name: Run Playwright tests
905
+ run: npx playwright test --project=${{ matrix.browser }}
906
+ env:
907
+ BASE_URL: http://localhost:3000
908
+ USER_EMAIL: ${{ secrets.TEST_USER_EMAIL }}
909
+ USER_PASSWORD: ${{ secrets.TEST_USER_PASSWORD }}
910
+
911
+ - name: Upload test results
912
+ if: always()
913
+ uses: actions/upload-artifact@v3
914
+ with:
915
+ name: playwright-report-${{ matrix.browser }}
916
+ path: playwright-report/
917
+ retention-days: 30
918
+
919
+ - name: Upload test videos
920
+ if: failure()
921
+ uses: actions/upload-artifact@v3
922
+ with:
923
+ name: test-videos-${{ matrix.browser }}
924
+ path: test-results/**/video.webm
925
+ retention-days: 7
926
+
927
+ test-sharded:
928
+ timeout-minutes: 60
929
+ runs-on: ubuntu-latest
930
+ strategy:
931
+ matrix:
932
+ shardIndex: [1, 2, 3, 4]
933
+ shardTotal: [4]
934
+
935
+ steps:
936
+ - uses: actions/checkout@v4
937
+ - uses: actions/setup-node@v4
938
+ with:
939
+ node-version: '20'
940
+
941
+ - name: Install dependencies
942
+ run: npm ci
943
+
944
+ - name: Install Playwright
945
+ run: npx playwright install --with-deps
946
+
947
+ - name: Run sharded tests
948
+ run: npx playwright test --shard=${{ matrix.shardIndex }}/${{ matrix.shardTotal }}
949
+
950
+ - name: Upload blob report
951
+ uses: actions/upload-artifact@v3
952
+ with:
953
+ name: blob-report-${{ matrix.shardIndex }}
954
+ path: blob-report
955
+ retention-days: 1
956
+
957
+ merge-reports:
958
+ if: always()
959
+ needs: [test-sharded]
960
+ runs-on: ubuntu-latest
961
+
962
+ steps:
963
+ - uses: actions/checkout@v4
964
+ - uses: actions/setup-node@v4
965
+
966
+ - name: Download all reports
967
+ uses: actions/download-artifact@v3
968
+ with:
969
+ path: all-blob-reports
970
+
971
+ - name: Merge reports
972
+ run: npx playwright merge-reports --reporter html ./all-blob-reports
973
+
974
+ - name: Upload merged report
975
+ uses: actions/upload-artifact@v3
976
+ with:
977
+ name: playwright-report
978
+ path: playwright-report/
979
+ ```
980
+
981
+ ### 12. Package Dependencies
982
+
983
+ ```json
984
+ {
985
+ "devDependencies": {
986
+ "@playwright/test": "^1.40.0",
987
+ "dotenv": "^16.3.1",
988
+ "playwright": "^1.40.0"
989
+ },
990
+ "scripts": {
991
+ "test:e2e": "playwright test",
992
+ "test:e2e:ui": "playwright test --ui",
993
+ "test:e2e:debug": "playwright test --debug",
994
+ "test:e2e:headed": "playwright test --headed",
995
+ "test:e2e:chromium": "playwright test --project=chromium",
996
+ "test:e2e:firefox": "playwright test --project=firefox",
997
+ "test:e2e:webkit": "playwright test --project=webkit",
998
+ "test:e2e:mobile": "playwright test --project=mobile-chrome --project=mobile-safari",
999
+ "test:e2e:report": "playwright show-report",
1000
+ "test:e2e:codegen": "playwright codegen"
1001
+ }
1002
+ }
1003
+ ```
1004
+
1005
+ ### 13. Environment Configuration
1006
+
1007
+ **.env.example**:
1008
+ ```bash
1009
+ # Base URLs
1010
+ BASE_URL=http://localhost:3000
1011
+ API_BASE_URL=http://localhost:3000/api
1012
+
1013
+ # Test credentials
1014
+ USER_EMAIL=test@example.com
1015
+ USER_PASSWORD=password123
1016
+ ADMIN_EMAIL=admin@example.com
1017
+ ADMIN_PASSWORD=admin123
1018
+
1019
+ # CI/CD
1020
+ CI=false
1021
+ PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=false
1022
+ ```
1023
+
1024
+ ### 14. Best Practices
1025
+
1026
+ **Test Organization**:
1027
+ - Group tests by feature/domain
1028
+ - Use descriptive test names
1029
+ - One assertion per test (when possible)
1030
+ - Use page objects for reusability
1031
+ - Keep tests independent
1032
+
1033
+ **Reliability**:
1034
+ - Use auto-waiting features
1035
+ - Avoid hard-coded waits (`page.waitForTimeout`)
1036
+ - Use data-testid attributes
1037
+ - Retry flaky tests automatically
1038
+ - Clean up test data
1039
+
1040
+ **Performance**:
1041
+ - Run tests in parallel
1042
+ - Use test sharding for large suites
1043
+ - Reuse authentication state
1044
+ - Block unnecessary resources
1045
+ - Use fast selectors (data-testid > text)
1046
+
1047
+ **Debugging**:
1048
+ - Use Playwright Inspector (`--debug`)
1049
+ - Record traces on failure
1050
+ - Take screenshots on failure
1051
+ - Use UI mode for interactive debugging
1052
+ - Check network logs
1053
+
1054
+ ## Workflow
1055
+
1056
+ 1. Ask about application type and E2E requirements
1057
+ 2. Install Playwright and dependencies
1058
+ 3. Create playwright.config.ts with all browsers
1059
+ 4. Set up page object model structure
1060
+ 5. Create custom fixtures for auth and API
1061
+ 6. Implement global setup/teardown
1062
+ 7. Create authentication setup tests
1063
+ 8. Generate test utilities and helpers
1064
+ 9. Write example E2E test suite
1065
+ 10. Configure CI/CD pipeline
1066
+ 11. Set up visual regression testing
1067
+ 12. Add performance testing
1068
+ 13. Provide debugging and maintenance guide
1069
+
1070
+ ## When to Use
1071
+
1072
+ - Setting up E2E testing from scratch
1073
+ - Migrating from Selenium/Cypress to Playwright
1074
+ - Adding cross-browser testing
1075
+ - Implementing visual regression testing
1076
+ - Setting up CI/CD for E2E tests
1077
+ - Adding performance testing
1078
+ - Creating page object model architecture
1079
+ - Implementing authentication testing
1080
+
1081
+ Set up production-ready Playwright E2E testing with comprehensive coverage!