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.
- package/.claude-plugin/marketplace.json +93 -49
- package/CLAUDE.md +137 -4
- package/dist/src/cli/helpers/ado-area-path-mapper.d.ts +89 -0
- package/dist/src/cli/helpers/ado-area-path-mapper.d.ts.map +1 -0
- package/dist/src/cli/helpers/ado-area-path-mapper.js +213 -0
- package/dist/src/cli/helpers/ado-area-path-mapper.js.map +1 -0
- package/dist/src/cli/helpers/issue-tracker/ado-auto-discover.d.ts +29 -0
- package/dist/src/cli/helpers/issue-tracker/ado-auto-discover.d.ts.map +1 -0
- package/dist/src/cli/helpers/issue-tracker/ado-auto-discover.js +109 -0
- package/dist/src/cli/helpers/issue-tracker/ado-auto-discover.js.map +1 -0
- package/dist/src/cli/helpers/issue-tracker/ado.d.ts +1 -0
- package/dist/src/cli/helpers/issue-tracker/ado.d.ts.map +1 -1
- package/dist/src/cli/helpers/issue-tracker/ado.js +2 -0
- package/dist/src/cli/helpers/issue-tracker/ado.js.map +1 -1
- package/dist/src/cli/helpers/smart-filter.d.ts +83 -0
- package/dist/src/cli/helpers/smart-filter.d.ts.map +1 -0
- package/dist/src/cli/helpers/smart-filter.js +265 -0
- package/dist/src/cli/helpers/smart-filter.js.map +1 -0
- package/dist/src/core/qa/quality-gate-decider.d.ts +1 -1
- package/dist/src/core/qa/quality-gate-decider.js +2 -2
- package/dist/src/core/qa/quality-gate-decider.js.map +1 -1
- package/dist/src/core/qa/risk-calculator.d.ts +2 -2
- package/dist/src/core/qa/risk-calculator.js +2 -2
- package/dist/src/core/validators/ac-presence-validator.d.ts +56 -0
- package/dist/src/core/validators/ac-presence-validator.d.ts.map +1 -0
- package/dist/src/core/validators/ac-presence-validator.js +149 -0
- package/dist/src/core/validators/ac-presence-validator.js.map +1 -0
- package/dist/src/integrations/ado/area-path-mapper.d.ts +137 -0
- package/dist/src/integrations/ado/area-path-mapper.d.ts.map +1 -0
- package/dist/src/integrations/ado/area-path-mapper.js +267 -0
- package/dist/src/integrations/ado/area-path-mapper.js.map +1 -0
- package/dist/src/integrations/jira/filter-processor.d.ts +126 -0
- package/dist/src/integrations/jira/filter-processor.d.ts.map +1 -0
- package/dist/src/integrations/jira/filter-processor.js +207 -0
- package/dist/src/integrations/jira/filter-processor.js.map +1 -0
- package/dist/src/integrations/jira/jira-client.d.ts +13 -0
- package/dist/src/integrations/jira/jira-client.d.ts.map +1 -1
- package/dist/src/integrations/jira/jira-client.js +33 -0
- package/dist/src/integrations/jira/jira-client.js.map +1 -1
- package/dist/src/utils/ac-embedder.d.ts +63 -0
- package/dist/src/utils/ac-embedder.d.ts.map +1 -0
- package/dist/src/utils/ac-embedder.js +217 -0
- package/dist/src/utils/ac-embedder.js.map +1 -0
- package/dist/src/utils/env-manager.d.ts +86 -0
- package/dist/src/utils/env-manager.d.ts.map +1 -0
- package/dist/src/utils/env-manager.js +188 -0
- package/dist/src/utils/env-manager.js.map +1 -0
- package/package.json +1 -1
- package/plugins/specweave/.claude-plugin/plugin.json +1 -1
- package/plugins/specweave/agents/AGENTS-INDEX.md +1 -1
- package/plugins/specweave/agents/increment-quality-judge-v2/AGENT.md +9 -9
- package/plugins/specweave/commands/specweave-do.md +37 -0
- package/plugins/specweave/commands/specweave-done.md +159 -0
- package/plugins/specweave/commands/specweave-embed-acs.md +446 -0
- package/plugins/specweave/commands/specweave-next.md +148 -3
- package/plugins/specweave/commands/specweave-qa.md +2 -2
- package/plugins/specweave/hooks/pre-increment-start.sh +168 -0
- package/plugins/specweave/skills/SKILLS-INDEX.md +1 -1
- package/plugins/specweave-ado/.claude-plugin/plugin.json +1 -1
- package/plugins/specweave-ado/commands/specweave-ado-import-projects.md +331 -0
- package/plugins/specweave-alternatives/.claude-plugin/plugin.json +10 -0
- package/plugins/specweave-alternatives/commands/alternatives-analyze.md +336 -0
- package/plugins/specweave-alternatives/skills/architecture-alternatives/SKILL.md +651 -0
- package/plugins/specweave-alternatives/skills/bmad-method/SKILL.md +420 -0
- package/plugins/specweave-alternatives/skills/spec-kit-expert/SKILL.md +487 -0
- package/plugins/specweave-backend/commands/api-scaffold.md +80 -0
- package/plugins/specweave-backend/commands/crud-generate.md +109 -0
- package/plugins/specweave-backend/commands/migration-generate.md +139 -0
- package/plugins/specweave-confluent/commands/connector-deploy.md +154 -0
- package/plugins/specweave-confluent/commands/ksqldb-query.md +179 -0
- package/plugins/specweave-confluent/commands/schema-register.md +123 -0
- package/plugins/specweave-core/.claude-plugin/plugin.json +21 -0
- package/plugins/specweave-core/commands/architecture-review.md +288 -0
- package/plugins/specweave-core/commands/code-review.md +213 -0
- package/plugins/specweave-core/commands/refactor-plan.md +249 -0
- package/plugins/specweave-core/skills/code-quality/SKILL.md +157 -0
- package/plugins/specweave-core/skills/design-patterns/SKILL.md +244 -0
- package/plugins/specweave-core/skills/software-architecture/SKILL.md +83 -0
- package/plugins/specweave-cost-optimizer/.claude-plugin/plugin.json +22 -0
- package/plugins/specweave-cost-optimizer/commands/cost-analyze.md +360 -0
- package/plugins/specweave-cost-optimizer/commands/cost-optimize.md +480 -0
- package/plugins/specweave-cost-optimizer/skills/aws-cost-expert/SKILL.md +416 -0
- package/plugins/specweave-cost-optimizer/skills/cloud-pricing/SKILL.md +325 -0
- package/plugins/specweave-cost-optimizer/skills/cost-optimization/SKILL.md +337 -0
- package/plugins/specweave-diagrams/.claude-plugin/plugin.json +1 -1
- package/plugins/specweave-diagrams/commands/diagrams-generate.md +168 -0
- package/plugins/specweave-docs/.claude-plugin/plugin.json +10 -0
- package/plugins/specweave-docs/commands/docs-generate.md +441 -0
- package/plugins/specweave-docs/commands/docs-init.md +334 -0
- package/plugins/specweave-docs/skills/docusaurus/SKILL.md +581 -0
- package/plugins/specweave-docs/skills/spec-driven-brainstorming/SKILL.md +689 -0
- package/plugins/specweave-docs/skills/technical-writing/SKILL.md +1039 -0
- package/plugins/specweave-docs-preview/.claude-plugin/plugin.json +1 -1
- package/plugins/specweave-figma/.claude-plugin/plugin.json +23 -0
- package/plugins/specweave-figma/commands/figma-import.md +690 -0
- package/plugins/specweave-figma/commands/figma-to-react.md +834 -0
- package/plugins/specweave-figma/commands/figma-tokens.md +815 -0
- package/plugins/specweave-frontend/.claude-plugin/plugin.json +21 -0
- package/plugins/specweave-frontend/agents/frontend-architect/AGENT.md +387 -0
- package/plugins/specweave-frontend/agents/frontend-architect/README.md +385 -0
- package/plugins/specweave-frontend/agents/frontend-architect/examples.md +590 -0
- package/plugins/specweave-frontend/agents/frontend-architect/templates/component-template.tsx +152 -0
- package/plugins/specweave-frontend/agents/frontend-architect/templates/hook-template.ts +311 -0
- package/plugins/specweave-frontend/agents/frontend-architect/templates/page-template.tsx +228 -0
- package/plugins/specweave-frontend/commands/component-generate.md +510 -0
- package/plugins/specweave-frontend/commands/design-system-init.md +494 -0
- package/plugins/specweave-frontend/commands/frontend-scaffold.md +207 -0
- package/plugins/specweave-frontend/commands/nextjs-setup.md +396 -0
- package/plugins/specweave-frontend/skills/design-system-architect/SKILL.md +278 -0
- package/plugins/specweave-frontend/skills/frontend/SKILL.md +420 -0
- package/plugins/specweave-frontend/skills/nextjs/SKILL.md +546 -0
- package/plugins/specweave-github/.claude-plugin/plugin.json +1 -1
- package/plugins/specweave-github/hooks/.specweave/logs/hooks-debug.log +194 -0
- package/plugins/specweave-infrastructure/.claude-plugin/plugin.json +1 -1
- package/plugins/specweave-jira/.claude-plugin/plugin.json +1 -1
- package/plugins/specweave-jira/commands/import-projects.js +183 -0
- package/plugins/specweave-jira/commands/import-projects.md +97 -0
- package/plugins/specweave-jira/commands/import-projects.ts +288 -0
- package/plugins/specweave-jira/commands/specweave-jira-import-projects.md +298 -0
- package/plugins/specweave-kafka/.claude-plugin/plugin.json +1 -1
- package/plugins/specweave-kafka-streams/.claude-plugin/plugin.json +1 -1
- package/plugins/specweave-kubernetes/commands/cluster-setup.md +262 -0
- package/plugins/specweave-kubernetes/commands/deployment-generate.md +242 -0
- package/plugins/specweave-kubernetes/commands/helm-scaffold.md +333 -0
- package/plugins/specweave-ml/.claude-plugin/plugin.json +1 -1
- package/plugins/specweave-mobile/commands/app-scaffold.md +233 -0
- package/plugins/specweave-mobile/commands/build-config.md +256 -0
- package/plugins/specweave-mobile/commands/screen-generate.md +289 -0
- package/plugins/specweave-n8n/.claude-plugin/plugin.json +1 -1
- package/plugins/specweave-plugin-dev/.claude-plugin/plugin.json +13 -12
- package/plugins/specweave-plugin-dev/commands/plugin-create.md +333 -0
- package/plugins/specweave-plugin-dev/commands/plugin-publish.md +339 -0
- package/plugins/specweave-plugin-dev/commands/plugin-test.md +293 -0
- package/plugins/specweave-plugin-dev/skills/claude-sdk/SKILL.md +162 -0
- package/plugins/specweave-plugin-dev/skills/marketplace-publishing/SKILL.md +263 -0
- package/plugins/specweave-plugin-dev/skills/plugin-development/SKILL.md +316 -0
- package/plugins/specweave-release/.claude-plugin/plugin.json +1 -1
- package/plugins/specweave-release/commands/specweave-release-npm.md +110 -0
- package/plugins/specweave-release/hooks/.specweave/logs/dora-tracking.log +168 -0
- package/plugins/specweave-testing/.claude-plugin/plugin.json +21 -0
- package/plugins/specweave-testing/agents/qa-engineer/AGENT.md +797 -0
- package/plugins/specweave-testing/agents/qa-engineer/README.md +443 -0
- package/plugins/specweave-testing/agents/qa-engineer/templates/playwright-e2e-test.ts +470 -0
- package/plugins/specweave-testing/agents/qa-engineer/templates/test-data-factory.ts +507 -0
- package/plugins/specweave-testing/agents/qa-engineer/templates/vitest-unit-test.ts +400 -0
- package/plugins/specweave-testing/agents/qa-engineer/test-strategies.md +726 -0
- package/plugins/specweave-testing/commands/e2e-setup.md +1081 -0
- package/plugins/specweave-testing/commands/test-coverage.md +979 -0
- package/plugins/specweave-testing/commands/test-generate.md +1156 -0
- package/plugins/specweave-testing/commands/test-init.md +409 -0
- package/plugins/specweave-testing/skills/e2e-playwright/SKILL.md +769 -0
- package/plugins/specweave-testing/skills/tdd-expert/SKILL.md +934 -0
- package/plugins/specweave-testing/skills/unit-testing-expert/SKILL.md +1011 -0
- package/plugins/specweave-tooling/.claude-plugin/plugin.json +22 -0
- package/plugins/specweave-tooling/commands/specweave-tooling-skill-create.md +691 -0
- package/plugins/specweave-tooling/commands/specweave-tooling-skill-package.md +751 -0
- package/plugins/specweave-tooling/commands/specweave-tooling-skill-validate.md +858 -0
- package/plugins/specweave-ui/.claude-plugin/plugin.json +10 -0
- package/plugins/specweave-ui/commands/ui-automate.md +199 -0
- package/plugins/specweave-ui/commands/ui-inspect.md +70 -0
- package/plugins/specweave-ui/skills/browser-automation/SKILL.md +314 -0
- package/plugins/specweave-ui/skills/ui-testing/SKILL.md +716 -0
- package/plugins/specweave-ui/skills/visual-regression/SKILL.md +728 -0
- package/plugins/specweave/commands/check-hooks.md +0 -257
- package/plugins/specweave/commands/specweave-archive-increments.md +0 -82
- package/plugins/specweave-plugin-dev/skills/plugin-expert/SKILL.md +0 -1231
- /package/plugins/specweave/{agents/code-reviewer.md → skills/code-reviewer/SKILL.md} +0 -0
|
@@ -0,0 +1,470 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Playwright E2E Test Template
|
|
3
|
+
*
|
|
4
|
+
* This template demonstrates best practices for writing end-to-end tests
|
|
5
|
+
* with Playwright for web applications.
|
|
6
|
+
*
|
|
7
|
+
* Features:
|
|
8
|
+
* - Page Object Model (POM)
|
|
9
|
+
* - Test fixtures
|
|
10
|
+
* - Cross-browser testing
|
|
11
|
+
* - Mobile emulation
|
|
12
|
+
* - API mocking
|
|
13
|
+
* - Visual regression
|
|
14
|
+
* - Accessibility testing
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import { test, expect, Page } from '@playwright/test';
|
|
18
|
+
import AxeBuilder from '@axe-core/playwright';
|
|
19
|
+
|
|
20
|
+
// ============================================================================
|
|
21
|
+
// PAGE OBJECTS
|
|
22
|
+
// ============================================================================
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Login Page Object
|
|
26
|
+
*
|
|
27
|
+
* Encapsulates all interactions with the login page
|
|
28
|
+
*/
|
|
29
|
+
class LoginPage {
|
|
30
|
+
constructor(private page: Page) {}
|
|
31
|
+
|
|
32
|
+
// Locators
|
|
33
|
+
get emailInput() {
|
|
34
|
+
return this.page.getByLabel('Email');
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
get passwordInput() {
|
|
38
|
+
return this.page.getByLabel('Password');
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
get loginButton() {
|
|
42
|
+
return this.page.getByRole('button', { name: 'Login' });
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
get errorMessage() {
|
|
46
|
+
return this.page.getByRole('alert');
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Actions
|
|
50
|
+
async goto() {
|
|
51
|
+
await this.page.goto('/login');
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
async login(email: string, password: string) {
|
|
55
|
+
await this.emailInput.fill(email);
|
|
56
|
+
await this.passwordInput.fill(password);
|
|
57
|
+
await this.loginButton.click();
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
async expectError(message: string) {
|
|
61
|
+
await expect(this.errorMessage).toContainText(message);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Dashboard Page Object
|
|
67
|
+
*/
|
|
68
|
+
class DashboardPage {
|
|
69
|
+
constructor(private page: Page) {}
|
|
70
|
+
|
|
71
|
+
get welcomeMessage() {
|
|
72
|
+
return this.page.getByText(/Welcome,/);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
get logoutButton() {
|
|
76
|
+
return this.page.getByRole('button', { name: 'Logout' });
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
async expectLoggedIn(username: string) {
|
|
80
|
+
await expect(this.welcomeMessage).toContainText(username);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
async logout() {
|
|
84
|
+
await this.logoutButton.click();
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// ============================================================================
|
|
89
|
+
// TEST FIXTURES
|
|
90
|
+
// ============================================================================
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Custom fixture that provides authenticated page
|
|
94
|
+
*/
|
|
95
|
+
const test = base.extend<{ authenticatedPage: Page }>({
|
|
96
|
+
authenticatedPage: async ({ page }, use) => {
|
|
97
|
+
// Setup: Login before test
|
|
98
|
+
const loginPage = new LoginPage(page);
|
|
99
|
+
await loginPage.goto();
|
|
100
|
+
await loginPage.login('user@example.com', 'password123');
|
|
101
|
+
|
|
102
|
+
// Wait for navigation to dashboard
|
|
103
|
+
await page.waitForURL('/dashboard');
|
|
104
|
+
|
|
105
|
+
// Provide authenticated page to test
|
|
106
|
+
await use(page);
|
|
107
|
+
|
|
108
|
+
// Teardown: Logout after test
|
|
109
|
+
const dashboardPage = new DashboardPage(page);
|
|
110
|
+
await dashboardPage.logout();
|
|
111
|
+
},
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
// ============================================================================
|
|
115
|
+
// BASIC E2E TESTS
|
|
116
|
+
// ============================================================================
|
|
117
|
+
|
|
118
|
+
test.describe('Authentication Flow', () => {
|
|
119
|
+
test('should login successfully with valid credentials', async ({ page }) => {
|
|
120
|
+
// ARRANGE
|
|
121
|
+
const loginPage = new LoginPage(page);
|
|
122
|
+
await loginPage.goto();
|
|
123
|
+
|
|
124
|
+
// ACT
|
|
125
|
+
await loginPage.login('user@example.com', 'password123');
|
|
126
|
+
|
|
127
|
+
// ASSERT
|
|
128
|
+
await expect(page).toHaveURL('/dashboard');
|
|
129
|
+
const dashboardPage = new DashboardPage(page);
|
|
130
|
+
await dashboardPage.expectLoggedIn('User');
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
test('should show error for invalid credentials', async ({ page }) => {
|
|
134
|
+
// ARRANGE
|
|
135
|
+
const loginPage = new LoginPage(page);
|
|
136
|
+
await loginPage.goto();
|
|
137
|
+
|
|
138
|
+
// ACT
|
|
139
|
+
await loginPage.login('invalid@example.com', 'wrongpassword');
|
|
140
|
+
|
|
141
|
+
// ASSERT
|
|
142
|
+
await loginPage.expectError('Invalid email or password');
|
|
143
|
+
await expect(page).toHaveURL('/login'); // Still on login page
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
test('should show validation errors for empty fields', async ({ page }) => {
|
|
147
|
+
// ARRANGE
|
|
148
|
+
const loginPage = new LoginPage(page);
|
|
149
|
+
await loginPage.goto();
|
|
150
|
+
|
|
151
|
+
// ACT
|
|
152
|
+
await loginPage.loginButton.click();
|
|
153
|
+
|
|
154
|
+
// ASSERT
|
|
155
|
+
await expect(page.getByText('Email is required')).toBeVisible();
|
|
156
|
+
await expect(page.getByText('Password is required')).toBeVisible();
|
|
157
|
+
});
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
// ============================================================================
|
|
161
|
+
// AUTHENTICATED TESTS (Using Fixture)
|
|
162
|
+
// ============================================================================
|
|
163
|
+
|
|
164
|
+
test.describe('Dashboard Features', () => {
|
|
165
|
+
test('should display user profile', async ({ authenticatedPage }) => {
|
|
166
|
+
// Navigate to profile
|
|
167
|
+
await authenticatedPage.goto('/profile');
|
|
168
|
+
|
|
169
|
+
// Verify profile data
|
|
170
|
+
await expect(
|
|
171
|
+
authenticatedPage.getByText('user@example.com')
|
|
172
|
+
).toBeVisible();
|
|
173
|
+
await expect(authenticatedPage.getByText('Member since')).toBeVisible();
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
test('should allow editing profile', async ({ authenticatedPage }) => {
|
|
177
|
+
// Navigate to profile
|
|
178
|
+
await authenticatedPage.goto('/profile');
|
|
179
|
+
|
|
180
|
+
// Edit name
|
|
181
|
+
const nameInput = authenticatedPage.getByLabel('Name');
|
|
182
|
+
await nameInput.clear();
|
|
183
|
+
await nameInput.fill('New Name');
|
|
184
|
+
|
|
185
|
+
// Save changes
|
|
186
|
+
await authenticatedPage
|
|
187
|
+
.getByRole('button', { name: 'Save Changes' })
|
|
188
|
+
.click();
|
|
189
|
+
|
|
190
|
+
// Verify success
|
|
191
|
+
await expect(
|
|
192
|
+
authenticatedPage.getByText('Profile updated successfully')
|
|
193
|
+
).toBeVisible();
|
|
194
|
+
});
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
// ============================================================================
|
|
198
|
+
// API MOCKING
|
|
199
|
+
// ============================================================================
|
|
200
|
+
|
|
201
|
+
test.describe('API Mocking', () => {
|
|
202
|
+
test('should handle API errors gracefully', async ({ page }) => {
|
|
203
|
+
// Mock API to return error
|
|
204
|
+
await page.route('**/api/users', (route) => {
|
|
205
|
+
route.fulfill({
|
|
206
|
+
status: 500,
|
|
207
|
+
body: JSON.stringify({ error: 'Internal Server Error' }),
|
|
208
|
+
});
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
// Navigate to users page
|
|
212
|
+
await page.goto('/users');
|
|
213
|
+
|
|
214
|
+
// Verify error message
|
|
215
|
+
await expect(
|
|
216
|
+
page.getByText('Failed to load users. Please try again.')
|
|
217
|
+
).toBeVisible();
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
test('should display mocked user data', async ({ page }) => {
|
|
221
|
+
// Mock API to return test data
|
|
222
|
+
await page.route('**/api/users', (route) => {
|
|
223
|
+
route.fulfill({
|
|
224
|
+
status: 200,
|
|
225
|
+
body: JSON.stringify([
|
|
226
|
+
{ id: 1, name: 'John Doe', email: 'john@example.com' },
|
|
227
|
+
{ id: 2, name: 'Jane Smith', email: 'jane@example.com' },
|
|
228
|
+
]),
|
|
229
|
+
});
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
// Navigate to users page
|
|
233
|
+
await page.goto('/users');
|
|
234
|
+
|
|
235
|
+
// Verify mocked data is displayed
|
|
236
|
+
await expect(page.getByText('John Doe')).toBeVisible();
|
|
237
|
+
await expect(page.getByText('Jane Smith')).toBeVisible();
|
|
238
|
+
});
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
// ============================================================================
|
|
242
|
+
// VISUAL REGRESSION TESTING
|
|
243
|
+
// ============================================================================
|
|
244
|
+
|
|
245
|
+
test.describe('Visual Regression', () => {
|
|
246
|
+
test('homepage matches baseline', async ({ page }) => {
|
|
247
|
+
await page.goto('/');
|
|
248
|
+
|
|
249
|
+
// Capture screenshot and compare to baseline
|
|
250
|
+
await expect(page).toHaveScreenshot('homepage.png', {
|
|
251
|
+
fullPage: true,
|
|
252
|
+
animations: 'disabled',
|
|
253
|
+
});
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
test('button states match baseline', async ({ page }) => {
|
|
257
|
+
await page.goto('/components');
|
|
258
|
+
|
|
259
|
+
const button = page.getByRole('button', { name: 'Submit' });
|
|
260
|
+
|
|
261
|
+
// Default state
|
|
262
|
+
await expect(button).toHaveScreenshot('button-default.png');
|
|
263
|
+
|
|
264
|
+
// Hover state
|
|
265
|
+
await button.hover();
|
|
266
|
+
await expect(button).toHaveScreenshot('button-hover.png');
|
|
267
|
+
|
|
268
|
+
// Focus state
|
|
269
|
+
await button.focus();
|
|
270
|
+
await expect(button).toHaveScreenshot('button-focus.png');
|
|
271
|
+
});
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
// ============================================================================
|
|
275
|
+
// ACCESSIBILITY TESTING
|
|
276
|
+
// ============================================================================
|
|
277
|
+
|
|
278
|
+
test.describe('Accessibility', () => {
|
|
279
|
+
test('should not have accessibility violations', async ({ page }) => {
|
|
280
|
+
await page.goto('/');
|
|
281
|
+
|
|
282
|
+
// Run axe accessibility scan
|
|
283
|
+
const accessibilityScanResults = await new AxeBuilder({ page })
|
|
284
|
+
.withTags(['wcag2a', 'wcag2aa', 'wcag21a', 'wcag21aa'])
|
|
285
|
+
.analyze();
|
|
286
|
+
|
|
287
|
+
// Assert no violations
|
|
288
|
+
expect(accessibilityScanResults.violations).toEqual([]);
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
test('should support keyboard navigation', async ({ page }) => {
|
|
292
|
+
await page.goto('/form');
|
|
293
|
+
|
|
294
|
+
// Tab through form fields
|
|
295
|
+
await page.keyboard.press('Tab');
|
|
296
|
+
await expect(page.getByLabel('Email')).toBeFocused();
|
|
297
|
+
|
|
298
|
+
await page.keyboard.press('Tab');
|
|
299
|
+
await expect(page.getByLabel('Password')).toBeFocused();
|
|
300
|
+
|
|
301
|
+
await page.keyboard.press('Tab');
|
|
302
|
+
await expect(
|
|
303
|
+
page.getByRole('button', { name: 'Submit' })
|
|
304
|
+
).toBeFocused();
|
|
305
|
+
|
|
306
|
+
// Submit with Enter
|
|
307
|
+
await page.keyboard.press('Enter');
|
|
308
|
+
});
|
|
309
|
+
|
|
310
|
+
test('should have proper ARIA labels', async ({ page }) => {
|
|
311
|
+
await page.goto('/');
|
|
312
|
+
|
|
313
|
+
// Check navigation has aria-label
|
|
314
|
+
await expect(
|
|
315
|
+
page.getByRole('navigation', { name: 'Main navigation' })
|
|
316
|
+
).toBeVisible();
|
|
317
|
+
|
|
318
|
+
// Check main content has aria-label
|
|
319
|
+
await expect(page.getByRole('main')).toHaveAttribute(
|
|
320
|
+
'aria-label',
|
|
321
|
+
'Main content'
|
|
322
|
+
);
|
|
323
|
+
|
|
324
|
+
// Check all images have alt text
|
|
325
|
+
const images = await page.getByRole('img').all();
|
|
326
|
+
for (const img of images) {
|
|
327
|
+
await expect(img).toHaveAttribute('alt');
|
|
328
|
+
}
|
|
329
|
+
});
|
|
330
|
+
});
|
|
331
|
+
|
|
332
|
+
// ============================================================================
|
|
333
|
+
// MOBILE TESTING
|
|
334
|
+
// ============================================================================
|
|
335
|
+
|
|
336
|
+
test.describe('Mobile Experience', () => {
|
|
337
|
+
test.use({ viewport: { width: 375, height: 667 } }); // iPhone SE
|
|
338
|
+
|
|
339
|
+
test('should render mobile navigation', async ({ page }) => {
|
|
340
|
+
await page.goto('/');
|
|
341
|
+
|
|
342
|
+
// Mobile menu button should be visible
|
|
343
|
+
await expect(
|
|
344
|
+
page.getByRole('button', { name: 'Menu' })
|
|
345
|
+
).toBeVisible();
|
|
346
|
+
|
|
347
|
+
// Desktop navigation should be hidden
|
|
348
|
+
const desktopNav = page.getByRole('navigation').first();
|
|
349
|
+
await expect(desktopNav).toBeHidden();
|
|
350
|
+
});
|
|
351
|
+
|
|
352
|
+
test('should handle touch gestures', async ({ page }) => {
|
|
353
|
+
await page.goto('/gallery');
|
|
354
|
+
|
|
355
|
+
// Get image element
|
|
356
|
+
const image = page.getByRole('img').first();
|
|
357
|
+
|
|
358
|
+
// Swipe left
|
|
359
|
+
await image.dispatchEvent('touchstart', {
|
|
360
|
+
touches: [{ clientX: 300, clientY: 200 }],
|
|
361
|
+
});
|
|
362
|
+
await image.dispatchEvent('touchmove', {
|
|
363
|
+
touches: [{ clientX: 100, clientY: 200 }],
|
|
364
|
+
});
|
|
365
|
+
await image.dispatchEvent('touchend');
|
|
366
|
+
|
|
367
|
+
// Verify navigation to next image
|
|
368
|
+
await expect(page.getByText('Image 2 of 10')).toBeVisible();
|
|
369
|
+
});
|
|
370
|
+
});
|
|
371
|
+
|
|
372
|
+
// ============================================================================
|
|
373
|
+
// PERFORMANCE TESTING
|
|
374
|
+
// ============================================================================
|
|
375
|
+
|
|
376
|
+
test.describe('Performance', () => {
|
|
377
|
+
test('page load performance', async ({ page }) => {
|
|
378
|
+
await page.goto('/');
|
|
379
|
+
|
|
380
|
+
// Get performance metrics
|
|
381
|
+
const performanceMetrics = await page.evaluate(() => {
|
|
382
|
+
const perfData = window.performance.timing;
|
|
383
|
+
return {
|
|
384
|
+
loadTime: perfData.loadEventEnd - perfData.navigationStart,
|
|
385
|
+
domContentLoaded:
|
|
386
|
+
perfData.domContentLoadedEventEnd - perfData.navigationStart,
|
|
387
|
+
};
|
|
388
|
+
});
|
|
389
|
+
|
|
390
|
+
// Assert performance targets
|
|
391
|
+
expect(performanceMetrics.loadTime).toBeLessThan(3000); // 3s max
|
|
392
|
+
expect(performanceMetrics.domContentLoaded).toBeLessThan(2000); // 2s max
|
|
393
|
+
});
|
|
394
|
+
});
|
|
395
|
+
|
|
396
|
+
// ============================================================================
|
|
397
|
+
// FILE UPLOAD/DOWNLOAD
|
|
398
|
+
// ============================================================================
|
|
399
|
+
|
|
400
|
+
test.describe('File Operations', () => {
|
|
401
|
+
test('should upload file', async ({ page }) => {
|
|
402
|
+
await page.goto('/upload');
|
|
403
|
+
|
|
404
|
+
// Set up file chooser
|
|
405
|
+
const fileChooserPromise = page.waitForEvent('filechooser');
|
|
406
|
+
await page.getByRole('button', { name: 'Upload File' }).click();
|
|
407
|
+
const fileChooser = await fileChooserPromise;
|
|
408
|
+
|
|
409
|
+
// Upload file
|
|
410
|
+
await fileChooser.setFiles('tests/fixtures/test-file.pdf');
|
|
411
|
+
|
|
412
|
+
// Verify upload success
|
|
413
|
+
await expect(page.getByText('File uploaded successfully')).toBeVisible();
|
|
414
|
+
});
|
|
415
|
+
|
|
416
|
+
test('should download file', async ({ page }) => {
|
|
417
|
+
await page.goto('/downloads');
|
|
418
|
+
|
|
419
|
+
// Set up download
|
|
420
|
+
const downloadPromise = page.waitForEvent('download');
|
|
421
|
+
await page.getByRole('link', { name: 'Download Report' }).click();
|
|
422
|
+
const download = await downloadPromise;
|
|
423
|
+
|
|
424
|
+
// Verify download
|
|
425
|
+
expect(download.suggestedFilename()).toBe('report.pdf');
|
|
426
|
+
await download.saveAs(`/tmp/${download.suggestedFilename()}`);
|
|
427
|
+
});
|
|
428
|
+
});
|
|
429
|
+
|
|
430
|
+
// ============================================================================
|
|
431
|
+
// MULTI-TAB/WINDOW TESTING
|
|
432
|
+
// ============================================================================
|
|
433
|
+
|
|
434
|
+
test.describe('Multi-Window', () => {
|
|
435
|
+
test('should handle popup windows', async ({ context, page }) => {
|
|
436
|
+
await page.goto('/');
|
|
437
|
+
|
|
438
|
+
// Wait for popup
|
|
439
|
+
const [popup] = await Promise.all([
|
|
440
|
+
context.waitForEvent('page'),
|
|
441
|
+
page.getByRole('button', { name: 'Open Help' }).click(),
|
|
442
|
+
]);
|
|
443
|
+
|
|
444
|
+
// Interact with popup
|
|
445
|
+
await expect(popup.getByText('Help Center')).toBeVisible();
|
|
446
|
+
await popup.close();
|
|
447
|
+
});
|
|
448
|
+
});
|
|
449
|
+
|
|
450
|
+
// ============================================================================
|
|
451
|
+
// BEST PRACTICES CHECKLIST
|
|
452
|
+
// ============================================================================
|
|
453
|
+
|
|
454
|
+
/*
|
|
455
|
+
✅ Page Object Model (POM)
|
|
456
|
+
✅ Test Fixtures for Setup/Teardown
|
|
457
|
+
✅ Descriptive Test Names
|
|
458
|
+
✅ Auto-Waiting (Playwright built-in)
|
|
459
|
+
✅ User-Centric Selectors (getByRole, getByLabel)
|
|
460
|
+
✅ API Mocking for Reliability
|
|
461
|
+
✅ Visual Regression Testing
|
|
462
|
+
✅ Accessibility Testing (axe-core)
|
|
463
|
+
✅ Mobile/Responsive Testing
|
|
464
|
+
✅ Performance Assertions
|
|
465
|
+
✅ File Upload/Download
|
|
466
|
+
✅ Multi-Tab/Window Handling
|
|
467
|
+
✅ Screenshot/Video on Failure (configured in playwright.config.ts)
|
|
468
|
+
✅ Parallel Execution (configured in playwright.config.ts)
|
|
469
|
+
✅ Cross-Browser Testing (configured in playwright.config.ts)
|
|
470
|
+
*/
|