specweave 0.23.18 → 0.24.1
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 +144 -45
- 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/repo-structure/repo-structure-manager.d.ts.map +1 -1
- package/dist/src/core/repo-structure/repo-structure-manager.js +76 -43
- package/dist/src/core/repo-structure/repo-structure-manager.js.map +1 -1
- 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 +408 -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 +212 -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-payments/commands/stripe-setup.md +931 -0
- package/plugins/specweave-payments/commands/subscription-flow.md +1193 -0
- package/plugins/specweave-payments/commands/subscription-manage.md +386 -0
- package/plugins/specweave-payments/commands/webhook-setup.md +295 -0
- 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 +818 -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,728 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: visual-regression
|
|
3
|
+
description: Visual regression testing expert using Playwright snapshots, Percy, Chromatic, BackstopJS, and pixel-diff analysis. Covers baseline management, responsive testing, cross-browser visual testing, component visual testing, and CI integration. Activates for visual regression, screenshot testing, visual diff, Percy, Chromatic, BackstopJS, pixel comparison, snapshot testing, visual testing, CSS regression.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Visual Regression Testing Skill
|
|
7
|
+
|
|
8
|
+
Expert in visual regression testing - automated detection of unintended visual changes in web applications using screenshot comparison, pixel diffing, and visual testing frameworks.
|
|
9
|
+
|
|
10
|
+
## Why Visual Regression Testing?
|
|
11
|
+
|
|
12
|
+
**Problems It Solves**:
|
|
13
|
+
- CSS changes breaking layout unexpectedly
|
|
14
|
+
- Responsive design regressions (mobile/tablet/desktop)
|
|
15
|
+
- Cross-browser rendering differences
|
|
16
|
+
- Component library changes affecting consumers
|
|
17
|
+
- UI regressions that functional tests miss
|
|
18
|
+
|
|
19
|
+
**Example Scenario**:
|
|
20
|
+
```
|
|
21
|
+
Developer changes global CSS: `.container { padding: 10px }`
|
|
22
|
+
↓
|
|
23
|
+
Accidentally breaks checkout page layout
|
|
24
|
+
↓
|
|
25
|
+
Functional E2E tests pass (buttons still clickable)
|
|
26
|
+
↓
|
|
27
|
+
Visual regression test catches layout shift
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Core Tools
|
|
31
|
+
|
|
32
|
+
### 1. Playwright Visual Snapshots (Built-in)
|
|
33
|
+
|
|
34
|
+
**Why Playwright?**
|
|
35
|
+
- No third-party service required (free)
|
|
36
|
+
- Fast (parallel execution)
|
|
37
|
+
- Built-in automatic masking (hide dynamic content)
|
|
38
|
+
- Cross-browser support (Chromium, Firefox, WebKit)
|
|
39
|
+
|
|
40
|
+
#### Basic Snapshot Test
|
|
41
|
+
|
|
42
|
+
```typescript
|
|
43
|
+
import { test, expect } from '@playwright/test';
|
|
44
|
+
|
|
45
|
+
test('homepage should match visual baseline', async ({ page }) => {
|
|
46
|
+
await page.goto('https://example.com');
|
|
47
|
+
|
|
48
|
+
// Take full-page screenshot and compare to baseline
|
|
49
|
+
await expect(page).toHaveScreenshot('homepage.png');
|
|
50
|
+
});
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
**First Run** (create baseline):
|
|
54
|
+
```bash
|
|
55
|
+
npx playwright test --update-snapshots
|
|
56
|
+
# Creates: tests/__screenshots__/homepage.spec.ts/homepage-chromium-darwin.png
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
**Subsequent Runs** (compare to baseline):
|
|
60
|
+
```bash
|
|
61
|
+
npx playwright test
|
|
62
|
+
# Compares current screenshot to baseline
|
|
63
|
+
# Fails if difference exceeds threshold
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
#### Element-Level Snapshots
|
|
67
|
+
|
|
68
|
+
```typescript
|
|
69
|
+
test('button should match visual baseline', async ({ page }) => {
|
|
70
|
+
await page.goto('/buttons');
|
|
71
|
+
|
|
72
|
+
const submitButton = page.locator('[data-testid="submit-button"]');
|
|
73
|
+
await expect(submitButton).toHaveScreenshot('submit-button.png');
|
|
74
|
+
});
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
#### Configurable Thresholds
|
|
78
|
+
|
|
79
|
+
```typescript
|
|
80
|
+
// playwright.config.ts
|
|
81
|
+
export default defineConfig({
|
|
82
|
+
expect: {
|
|
83
|
+
toHaveScreenshot: {
|
|
84
|
+
maxDiffPixels: 100, // Allow max 100 pixels to differ
|
|
85
|
+
// OR
|
|
86
|
+
maxDiffPixelRatio: 0.01, // Allow 1% of pixels to differ
|
|
87
|
+
},
|
|
88
|
+
},
|
|
89
|
+
});
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
#### Masking Dynamic Content
|
|
93
|
+
|
|
94
|
+
```typescript
|
|
95
|
+
test('dashboard with dynamic data', async ({ page }) => {
|
|
96
|
+
await page.goto('/dashboard');
|
|
97
|
+
|
|
98
|
+
// Mask elements that change frequently (timestamps, user IDs)
|
|
99
|
+
await expect(page).toHaveScreenshot({
|
|
100
|
+
mask: [
|
|
101
|
+
page.locator('.timestamp'),
|
|
102
|
+
page.locator('.user-avatar'),
|
|
103
|
+
page.locator('[data-testid="ad-banner"]'),
|
|
104
|
+
],
|
|
105
|
+
});
|
|
106
|
+
});
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
#### Responsive Testing (Multiple Viewports)
|
|
110
|
+
|
|
111
|
+
```typescript
|
|
112
|
+
const viewports = [
|
|
113
|
+
{ name: 'mobile', width: 375, height: 667 },
|
|
114
|
+
{ name: 'tablet', width: 768, height: 1024 },
|
|
115
|
+
{ name: 'desktop', width: 1920, height: 1080 },
|
|
116
|
+
];
|
|
117
|
+
|
|
118
|
+
for (const viewport of viewports) {
|
|
119
|
+
test(`homepage on ${viewport.name}`, async ({ page }) => {
|
|
120
|
+
await page.setViewportSize({ width: viewport.width, height: viewport.height });
|
|
121
|
+
await page.goto('https://example.com');
|
|
122
|
+
|
|
123
|
+
await expect(page).toHaveScreenshot(`homepage-${viewport.name}.png`);
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
### 2. Percy (Cloud-Based Visual Testing)
|
|
129
|
+
|
|
130
|
+
**Why Percy?**
|
|
131
|
+
- Smart diffing (ignores anti-aliasing differences)
|
|
132
|
+
- Review UI (approve/reject changes)
|
|
133
|
+
- Integrates with GitHub PRs
|
|
134
|
+
- Parallel testing across browsers
|
|
135
|
+
- Automatic baseline management
|
|
136
|
+
|
|
137
|
+
#### Setup
|
|
138
|
+
|
|
139
|
+
```bash
|
|
140
|
+
npm install --save-dev @percy/playwright
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
```typescript
|
|
144
|
+
// tests/visual.spec.ts
|
|
145
|
+
import { test } from '@playwright/test';
|
|
146
|
+
import percySnapshot from '@percy/playwright';
|
|
147
|
+
|
|
148
|
+
test('homepage visual test', async ({ page }) => {
|
|
149
|
+
await page.goto('https://example.com');
|
|
150
|
+
|
|
151
|
+
// Percy captures screenshot and compares to baseline
|
|
152
|
+
await percySnapshot(page, 'Homepage');
|
|
153
|
+
});
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
```bash
|
|
157
|
+
# Run tests with Percy
|
|
158
|
+
PERCY_TOKEN=your_token npx percy exec -- npx playwright test
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
#### Percy Configuration
|
|
162
|
+
|
|
163
|
+
```yaml
|
|
164
|
+
# .percy.yml
|
|
165
|
+
version: 2
|
|
166
|
+
snapshot:
|
|
167
|
+
widths:
|
|
168
|
+
- 375 # Mobile
|
|
169
|
+
- 768 # Tablet
|
|
170
|
+
- 1280 # Desktop
|
|
171
|
+
min-height: 1024
|
|
172
|
+
percy-css: |
|
|
173
|
+
/* Hide dynamic elements */
|
|
174
|
+
.timestamp { visibility: hidden; }
|
|
175
|
+
.ad-banner { display: none; }
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
#### Percy in CI (GitHub Actions)
|
|
179
|
+
|
|
180
|
+
```yaml
|
|
181
|
+
name: Visual Tests
|
|
182
|
+
|
|
183
|
+
on: [pull_request]
|
|
184
|
+
|
|
185
|
+
jobs:
|
|
186
|
+
percy:
|
|
187
|
+
runs-on: ubuntu-latest
|
|
188
|
+
steps:
|
|
189
|
+
- uses: actions/checkout@v3
|
|
190
|
+
- uses: actions/setup-node@v3
|
|
191
|
+
- run: npm ci
|
|
192
|
+
- run: npx playwright install --with-deps
|
|
193
|
+
|
|
194
|
+
- name: Run Percy tests
|
|
195
|
+
run: npx percy exec -- npx playwright test
|
|
196
|
+
env:
|
|
197
|
+
PERCY_TOKEN: ${{ secrets.PERCY_TOKEN }}
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
### 3. Chromatic (Storybook Visual Testing)
|
|
201
|
+
|
|
202
|
+
**Why Chromatic?**
|
|
203
|
+
- Designed for component libraries (Storybook integration)
|
|
204
|
+
- Captures all component states automatically
|
|
205
|
+
- UI review workflow (approve/reject)
|
|
206
|
+
- Detects accessibility issues
|
|
207
|
+
- Version control for design system
|
|
208
|
+
|
|
209
|
+
#### Setup (Storybook + Chromatic)
|
|
210
|
+
|
|
211
|
+
```bash
|
|
212
|
+
npm install --save-dev chromatic
|
|
213
|
+
npx chromatic --project-token=your_token
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
```javascript
|
|
217
|
+
// .storybook/main.js
|
|
218
|
+
module.exports = {
|
|
219
|
+
stories: ['../src/**/*.stories.@(js|jsx|ts|tsx)'],
|
|
220
|
+
addons: ['@storybook/addon-essentials'],
|
|
221
|
+
};
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
```typescript
|
|
225
|
+
// Button.stories.tsx
|
|
226
|
+
import { Button } from './Button';
|
|
227
|
+
|
|
228
|
+
export default {
|
|
229
|
+
title: 'Components/Button',
|
|
230
|
+
component: Button,
|
|
231
|
+
};
|
|
232
|
+
|
|
233
|
+
export const Primary = () => <Button variant="primary">Click me</Button>;
|
|
234
|
+
export const Disabled = () => <Button disabled>Disabled</Button>;
|
|
235
|
+
export const Loading = () => <Button loading>Loading...</Button>;
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
```bash
|
|
239
|
+
# Chromatic captures all stories automatically
|
|
240
|
+
npx chromatic --project-token=your_token
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
#### Chromatic in CI
|
|
244
|
+
|
|
245
|
+
```yaml
|
|
246
|
+
name: Chromatic
|
|
247
|
+
|
|
248
|
+
on: push
|
|
249
|
+
|
|
250
|
+
jobs:
|
|
251
|
+
chromatic:
|
|
252
|
+
runs-on: ubuntu-latest
|
|
253
|
+
steps:
|
|
254
|
+
- uses: actions/checkout@v3
|
|
255
|
+
with:
|
|
256
|
+
fetch-depth: 0 # Required for Chromatic
|
|
257
|
+
- uses: actions/setup-node@v3
|
|
258
|
+
- run: npm ci
|
|
259
|
+
- run: npx chromatic --project-token=${{ secrets.CHROMATIC_TOKEN }}
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
### 4. BackstopJS (Configuration-Based)
|
|
263
|
+
|
|
264
|
+
**Why BackstopJS?**
|
|
265
|
+
- No code required (JSON configuration)
|
|
266
|
+
- Local execution (no cloud service)
|
|
267
|
+
- Interactive reports
|
|
268
|
+
- CSS selector-based scenarios
|
|
269
|
+
|
|
270
|
+
#### Configuration
|
|
271
|
+
|
|
272
|
+
```json
|
|
273
|
+
{
|
|
274
|
+
"id": "myapp_visual_tests",
|
|
275
|
+
"viewports": [
|
|
276
|
+
{ "label": "phone", "width": 375, "height": 667 },
|
|
277
|
+
{ "label": "tablet", "width": 768, "height": 1024 },
|
|
278
|
+
{ "label": "desktop", "width": 1920, "height": 1080 }
|
|
279
|
+
],
|
|
280
|
+
"scenarios": [
|
|
281
|
+
{
|
|
282
|
+
"label": "Homepage",
|
|
283
|
+
"url": "https://example.com",
|
|
284
|
+
"selectors": ["document"],
|
|
285
|
+
"delay": 500
|
|
286
|
+
},
|
|
287
|
+
{
|
|
288
|
+
"label": "Login Form",
|
|
289
|
+
"url": "https://example.com/login",
|
|
290
|
+
"selectors": [".login-form"],
|
|
291
|
+
"hideSelectors": [".banner-ad"],
|
|
292
|
+
"delay": 1000
|
|
293
|
+
}
|
|
294
|
+
],
|
|
295
|
+
"paths": {
|
|
296
|
+
"bitmaps_reference": "backstop_data/bitmaps_reference",
|
|
297
|
+
"bitmaps_test": "backstop_data/bitmaps_test",
|
|
298
|
+
"html_report": "backstop_data/html_report"
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
```bash
|
|
304
|
+
# Create baseline
|
|
305
|
+
backstop reference
|
|
306
|
+
|
|
307
|
+
# Run test (compare to baseline)
|
|
308
|
+
backstop test
|
|
309
|
+
|
|
310
|
+
# Update baseline (approve changes)
|
|
311
|
+
backstop approve
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
## Testing Strategies
|
|
315
|
+
|
|
316
|
+
### 1. Component-Level Visual Testing
|
|
317
|
+
|
|
318
|
+
**Use Case**: Design system components (buttons, inputs, modals)
|
|
319
|
+
|
|
320
|
+
```typescript
|
|
321
|
+
// Component snapshots
|
|
322
|
+
test.describe('Button component', () => {
|
|
323
|
+
test('primary variant', async ({ page }) => {
|
|
324
|
+
await page.goto('/storybook?path=/story/button--primary');
|
|
325
|
+
await expect(page.locator('.button')).toHaveScreenshot('button-primary.png');
|
|
326
|
+
});
|
|
327
|
+
|
|
328
|
+
test('disabled state', async ({ page }) => {
|
|
329
|
+
await page.goto('/storybook?path=/story/button--disabled');
|
|
330
|
+
await expect(page.locator('.button')).toHaveScreenshot('button-disabled.png');
|
|
331
|
+
});
|
|
332
|
+
|
|
333
|
+
test('hover state', async ({ page }) => {
|
|
334
|
+
await page.goto('/storybook?path=/story/button--primary');
|
|
335
|
+
const button = page.locator('.button');
|
|
336
|
+
await button.hover();
|
|
337
|
+
await expect(button).toHaveScreenshot('button-hover.png');
|
|
338
|
+
});
|
|
339
|
+
});
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
### 2. Page-Level Visual Testing
|
|
343
|
+
|
|
344
|
+
**Use Case**: Full pages (homepage, checkout, profile)
|
|
345
|
+
|
|
346
|
+
```typescript
|
|
347
|
+
test('checkout page visual baseline', async ({ page }) => {
|
|
348
|
+
await page.goto('/checkout');
|
|
349
|
+
|
|
350
|
+
// Wait for page to fully load
|
|
351
|
+
await page.waitForLoadState('networkidle');
|
|
352
|
+
|
|
353
|
+
// Mask dynamic content
|
|
354
|
+
await expect(page).toHaveScreenshot('checkout.png', {
|
|
355
|
+
mask: [page.locator('.cart-timestamp'), page.locator('.promo-banner')],
|
|
356
|
+
fullPage: true, // Capture entire page (scrolling)
|
|
357
|
+
});
|
|
358
|
+
});
|
|
359
|
+
```
|
|
360
|
+
|
|
361
|
+
### 3. Interaction-Based Visual Testing
|
|
362
|
+
|
|
363
|
+
**Use Case**: Modals, dropdowns, tooltips (require interaction)
|
|
364
|
+
|
|
365
|
+
```typescript
|
|
366
|
+
test('modal visual test', async ({ page }) => {
|
|
367
|
+
await page.goto('/');
|
|
368
|
+
|
|
369
|
+
// Open modal
|
|
370
|
+
await page.click('[data-testid="open-modal"]');
|
|
371
|
+
await page.waitForSelector('.modal');
|
|
372
|
+
|
|
373
|
+
// Capture modal screenshot
|
|
374
|
+
await expect(page.locator('.modal')).toHaveScreenshot('modal-open.png');
|
|
375
|
+
|
|
376
|
+
// Test error state
|
|
377
|
+
await page.fill('input[name="email"]', 'invalid');
|
|
378
|
+
await page.click('button[type="submit"]');
|
|
379
|
+
await expect(page.locator('.modal')).toHaveScreenshot('modal-error.png');
|
|
380
|
+
});
|
|
381
|
+
```
|
|
382
|
+
|
|
383
|
+
### 4. Cross-Browser Visual Testing
|
|
384
|
+
|
|
385
|
+
```typescript
|
|
386
|
+
// playwright.config.ts
|
|
387
|
+
export default defineConfig({
|
|
388
|
+
projects: [
|
|
389
|
+
{ name: 'chromium', use: { ...devices['Desktop Chrome'] } },
|
|
390
|
+
{ name: 'firefox', use: { ...devices['Desktop Firefox'] } },
|
|
391
|
+
{ name: 'webkit', use: { ...devices['Desktop Safari'] } },
|
|
392
|
+
],
|
|
393
|
+
});
|
|
394
|
+
```
|
|
395
|
+
|
|
396
|
+
```bash
|
|
397
|
+
# Run tests across all browsers
|
|
398
|
+
npx playwright test
|
|
399
|
+
|
|
400
|
+
# Generates separate baselines per browser:
|
|
401
|
+
# - homepage-chromium-darwin.png
|
|
402
|
+
# - homepage-firefox-darwin.png
|
|
403
|
+
# - homepage-webkit-darwin.png
|
|
404
|
+
```
|
|
405
|
+
|
|
406
|
+
## Best Practices
|
|
407
|
+
|
|
408
|
+
### 1. Stabilize Before Capturing
|
|
409
|
+
|
|
410
|
+
**Problem**: Animations, lazy loading, fonts cause flaky tests.
|
|
411
|
+
|
|
412
|
+
```typescript
|
|
413
|
+
// ❌ BAD: Capture immediately
|
|
414
|
+
await page.goto('/');
|
|
415
|
+
await expect(page).toHaveScreenshot();
|
|
416
|
+
|
|
417
|
+
// ✅ GOOD: Wait for stability
|
|
418
|
+
await page.goto('/');
|
|
419
|
+
await page.waitForLoadState('networkidle'); // Wait for network idle
|
|
420
|
+
await page.waitForSelector('.main-content'); // Wait for key element
|
|
421
|
+
await page.evaluate(() => document.fonts.ready); // Wait for fonts
|
|
422
|
+
|
|
423
|
+
// Disable animations for consistent screenshots
|
|
424
|
+
await page.addStyleTag({
|
|
425
|
+
content: `
|
|
426
|
+
*, *::before, *::after {
|
|
427
|
+
animation-duration: 0s !important;
|
|
428
|
+
transition-duration: 0s !important;
|
|
429
|
+
}
|
|
430
|
+
`,
|
|
431
|
+
});
|
|
432
|
+
|
|
433
|
+
await expect(page).toHaveScreenshot();
|
|
434
|
+
```
|
|
435
|
+
|
|
436
|
+
### 2. Mask Dynamic Content
|
|
437
|
+
|
|
438
|
+
```typescript
|
|
439
|
+
await expect(page).toHaveScreenshot({
|
|
440
|
+
mask: [
|
|
441
|
+
page.locator('.timestamp'), // Changes every second
|
|
442
|
+
page.locator('.user-id'), // Different per user
|
|
443
|
+
page.locator('[data-dynamic="true"]'), // Marked as dynamic
|
|
444
|
+
page.locator('video'), // Video frames vary
|
|
445
|
+
],
|
|
446
|
+
});
|
|
447
|
+
```
|
|
448
|
+
|
|
449
|
+
### 3. Use Meaningful Names
|
|
450
|
+
|
|
451
|
+
```typescript
|
|
452
|
+
// ❌ BAD: Generic names
|
|
453
|
+
await expect(page).toHaveScreenshot('test1.png');
|
|
454
|
+
|
|
455
|
+
// ✅ GOOD: Descriptive names
|
|
456
|
+
await expect(page).toHaveScreenshot('homepage-logged-in-user.png');
|
|
457
|
+
await expect(page).toHaveScreenshot('checkout-empty-cart-error.png');
|
|
458
|
+
```
|
|
459
|
+
|
|
460
|
+
### 4. Test Critical Paths Only
|
|
461
|
+
|
|
462
|
+
**Visual regression tests are expensive (slow, storage)**. Prioritize:
|
|
463
|
+
|
|
464
|
+
```typescript
|
|
465
|
+
// ✅ High Priority (critical user flows)
|
|
466
|
+
- Homepage (first impression)
|
|
467
|
+
- Checkout flow (revenue-critical)
|
|
468
|
+
- Login/signup (user acquisition)
|
|
469
|
+
- Product details (conversion)
|
|
470
|
+
|
|
471
|
+
// ⚠️ Medium Priority (important but not critical)
|
|
472
|
+
- Profile settings
|
|
473
|
+
- Search results
|
|
474
|
+
- Category pages
|
|
475
|
+
|
|
476
|
+
// ❌ Low Priority (skip or sample)
|
|
477
|
+
- Admin dashboards (internal users)
|
|
478
|
+
- Footer (rarely changes)
|
|
479
|
+
- Legal pages
|
|
480
|
+
```
|
|
481
|
+
|
|
482
|
+
### 5. Baseline Management Strategy
|
|
483
|
+
|
|
484
|
+
**When to Update Baselines**:
|
|
485
|
+
- ✅ Intentional design changes (approved by design team)
|
|
486
|
+
- ✅ Component library upgrades (reviewed)
|
|
487
|
+
- ✅ Browser updates (expected differences)
|
|
488
|
+
- ❌ Unintentional changes (investigate first!)
|
|
489
|
+
|
|
490
|
+
```bash
|
|
491
|
+
# Review diff report BEFORE approving
|
|
492
|
+
npx playwright test --update-snapshots # Use carefully!
|
|
493
|
+
|
|
494
|
+
# Better: Update selectively
|
|
495
|
+
npx playwright test homepage.spec.ts --update-snapshots
|
|
496
|
+
```
|
|
497
|
+
|
|
498
|
+
## Debugging Visual Diffs
|
|
499
|
+
|
|
500
|
+
### 1. Review Diff Report
|
|
501
|
+
|
|
502
|
+
Playwright generates HTML report with side-by-side comparison:
|
|
503
|
+
|
|
504
|
+
```bash
|
|
505
|
+
npx playwright test
|
|
506
|
+
# On failure, opens: playwright-report/index.html
|
|
507
|
+
# Shows: Expected | Actual | Diff (highlighted pixels)
|
|
508
|
+
```
|
|
509
|
+
|
|
510
|
+
### 2. Adjust Thresholds
|
|
511
|
+
|
|
512
|
+
```typescript
|
|
513
|
+
// Tolerate minor differences (anti-aliasing, font rendering)
|
|
514
|
+
await expect(page).toHaveScreenshot({
|
|
515
|
+
maxDiffPixelRatio: 0.02, // 2% tolerance
|
|
516
|
+
});
|
|
517
|
+
```
|
|
518
|
+
|
|
519
|
+
### 3. Ignore Specific Regions
|
|
520
|
+
|
|
521
|
+
```typescript
|
|
522
|
+
// Ignore regions that legitimately differ
|
|
523
|
+
await expect(page).toHaveScreenshot({
|
|
524
|
+
mask: [page.locator('.animated-banner')],
|
|
525
|
+
clip: { x: 0, y: 0, width: 800, height: 600 }, // Capture specific area
|
|
526
|
+
});
|
|
527
|
+
```
|
|
528
|
+
|
|
529
|
+
## CI/CD Integration
|
|
530
|
+
|
|
531
|
+
### 1. GitHub Actions (Playwright Snapshots)
|
|
532
|
+
|
|
533
|
+
```yaml
|
|
534
|
+
name: Visual Regression Tests
|
|
535
|
+
|
|
536
|
+
on:
|
|
537
|
+
pull_request:
|
|
538
|
+
branches: [main]
|
|
539
|
+
|
|
540
|
+
jobs:
|
|
541
|
+
visual:
|
|
542
|
+
runs-on: ubuntu-latest
|
|
543
|
+
steps:
|
|
544
|
+
- uses: actions/checkout@v3
|
|
545
|
+
- uses: actions/setup-node@v3
|
|
546
|
+
- run: npm ci
|
|
547
|
+
- run: npx playwright install --with-deps
|
|
548
|
+
|
|
549
|
+
- name: Run visual tests
|
|
550
|
+
run: npx playwright test
|
|
551
|
+
|
|
552
|
+
- name: Upload diff report
|
|
553
|
+
if: failure()
|
|
554
|
+
uses: actions/upload-artifact@v3
|
|
555
|
+
with:
|
|
556
|
+
name: visual-diff-report
|
|
557
|
+
path: playwright-report/
|
|
558
|
+
```
|
|
559
|
+
|
|
560
|
+
### 2. Baseline Storage Strategies
|
|
561
|
+
|
|
562
|
+
**Option 1: Git LFS (Large File Storage)**
|
|
563
|
+
- Store baselines in Git (versioned with code)
|
|
564
|
+
- Use Git LFS to avoid bloating repository
|
|
565
|
+
- Automatic sync across developers
|
|
566
|
+
|
|
567
|
+
```bash
|
|
568
|
+
# .gitattributes
|
|
569
|
+
*.png filter=lfs diff=lfs merge=lfs -text
|
|
570
|
+
|
|
571
|
+
git lfs install
|
|
572
|
+
git add tests/__screenshots__/*.png
|
|
573
|
+
git commit -m "Add visual baselines"
|
|
574
|
+
```
|
|
575
|
+
|
|
576
|
+
**Option 2: Cloud Storage (S3, GCS)**
|
|
577
|
+
- Store baselines in cloud bucket
|
|
578
|
+
- Download in CI before test
|
|
579
|
+
- Faster CI (no Git LFS checkout)
|
|
580
|
+
|
|
581
|
+
```yaml
|
|
582
|
+
- name: Download baselines
|
|
583
|
+
run: aws s3 sync s3://my-bucket/baselines tests/__screenshots__/
|
|
584
|
+
```
|
|
585
|
+
|
|
586
|
+
**Option 3: Percy/Chromatic (Managed)**
|
|
587
|
+
- Baselines stored in service (no Git needed)
|
|
588
|
+
- Automatic baseline management
|
|
589
|
+
- UI for reviewing changes
|
|
590
|
+
|
|
591
|
+
### 3. Handling Baseline Drift
|
|
592
|
+
|
|
593
|
+
**Problem**: Developer A updates baselines, Developer B's tests fail.
|
|
594
|
+
|
|
595
|
+
**Solution 1: Require baseline review**
|
|
596
|
+
```yaml
|
|
597
|
+
# PR merge rules
|
|
598
|
+
- Require approval for changes in tests/__screenshots__/
|
|
599
|
+
```
|
|
600
|
+
|
|
601
|
+
**Solution 2: Auto-update in CI**
|
|
602
|
+
```yaml
|
|
603
|
+
- name: Update baselines if approved
|
|
604
|
+
if: contains(github.event.pull_request.labels.*.name, 'update-baselines')
|
|
605
|
+
run: |
|
|
606
|
+
npx playwright test --update-snapshots
|
|
607
|
+
git config user.name "GitHub Actions"
|
|
608
|
+
git add tests/__screenshots__/
|
|
609
|
+
git commit -m "Update visual baselines"
|
|
610
|
+
git push
|
|
611
|
+
```
|
|
612
|
+
|
|
613
|
+
## Common Pitfalls
|
|
614
|
+
|
|
615
|
+
### 1. Flaky Tests Due to Animations
|
|
616
|
+
|
|
617
|
+
❌ **Bad**:
|
|
618
|
+
```typescript
|
|
619
|
+
await page.goto('/'); // Page has CSS animations
|
|
620
|
+
await expect(page).toHaveScreenshot(); // Fails randomly (mid-animation)
|
|
621
|
+
```
|
|
622
|
+
|
|
623
|
+
✅ **Good**:
|
|
624
|
+
```typescript
|
|
625
|
+
await page.goto('/');
|
|
626
|
+
await page.addStyleTag({ content: '* { animation: none !important; }' });
|
|
627
|
+
await expect(page).toHaveScreenshot();
|
|
628
|
+
```
|
|
629
|
+
|
|
630
|
+
### 2. Font Loading Issues
|
|
631
|
+
|
|
632
|
+
❌ **Bad**:
|
|
633
|
+
```typescript
|
|
634
|
+
await page.goto('/'); // Fonts loading async
|
|
635
|
+
await expect(page).toHaveScreenshot(); // Sometimes uses fallback font
|
|
636
|
+
```
|
|
637
|
+
|
|
638
|
+
✅ **Good**:
|
|
639
|
+
```typescript
|
|
640
|
+
await page.goto('/');
|
|
641
|
+
await page.evaluate(() => document.fonts.ready); // Wait for fonts
|
|
642
|
+
await expect(page).toHaveScreenshot();
|
|
643
|
+
```
|
|
644
|
+
|
|
645
|
+
### 3. Testing Everything (Slow CI)
|
|
646
|
+
|
|
647
|
+
❌ **Bad**: 500 visual tests (30 min CI time)
|
|
648
|
+
✅ **Good**: 50 critical visual tests (5 min CI time)
|
|
649
|
+
|
|
650
|
+
**Optimize**:
|
|
651
|
+
```typescript
|
|
652
|
+
// Run visual tests only on visual changes
|
|
653
|
+
if (changedFiles.some(file => file.endsWith('.css'))) {
|
|
654
|
+
runVisualTests();
|
|
655
|
+
}
|
|
656
|
+
```
|
|
657
|
+
|
|
658
|
+
### 4. Platform Differences (macOS vs Linux)
|
|
659
|
+
|
|
660
|
+
**Problem**: Screenshots differ between macOS (local) and Linux (CI).
|
|
661
|
+
|
|
662
|
+
**Solution**: Use Docker for local development
|
|
663
|
+
```bash
|
|
664
|
+
# Local development with Docker
|
|
665
|
+
docker run -it --rm -v $(pwd):/work -w /work mcr.microsoft.com/playwright:v1.40.0-focal npx playwright test
|
|
666
|
+
```
|
|
667
|
+
|
|
668
|
+
## Advanced Techniques
|
|
669
|
+
|
|
670
|
+
### 1. Visual Regression for Emails
|
|
671
|
+
|
|
672
|
+
```typescript
|
|
673
|
+
test('email template visual test', async ({ page }) => {
|
|
674
|
+
const emailHtml = await generateEmailTemplate({ userName: 'John', orderTotal: '$99.99' });
|
|
675
|
+
|
|
676
|
+
await page.setContent(emailHtml);
|
|
677
|
+
await expect(page).toHaveScreenshot('order-confirmation-email.png');
|
|
678
|
+
});
|
|
679
|
+
```
|
|
680
|
+
|
|
681
|
+
### 2. PDF Visual Testing
|
|
682
|
+
|
|
683
|
+
```typescript
|
|
684
|
+
test('invoice PDF visual test', async ({ page }) => {
|
|
685
|
+
await page.goto('/invoice/123');
|
|
686
|
+
const pdfBuffer = await page.pdf({ format: 'A4' });
|
|
687
|
+
|
|
688
|
+
// Convert PDF to image and compare
|
|
689
|
+
const pdfImage = await pdfToImage(pdfBuffer);
|
|
690
|
+
expect(pdfImage).toMatchSnapshot('invoice.png');
|
|
691
|
+
});
|
|
692
|
+
```
|
|
693
|
+
|
|
694
|
+
### 3. A/B Test Visual Variants
|
|
695
|
+
|
|
696
|
+
```typescript
|
|
697
|
+
test('A/B test variant visual comparison', async ({ page }) => {
|
|
698
|
+
// Test control variant
|
|
699
|
+
await page.goto('/?variant=control');
|
|
700
|
+
await expect(page).toHaveScreenshot('homepage-control.png');
|
|
701
|
+
|
|
702
|
+
// Test experiment variant
|
|
703
|
+
await page.goto('/?variant=experiment');
|
|
704
|
+
await expect(page).toHaveScreenshot('homepage-experiment.png');
|
|
705
|
+
|
|
706
|
+
// Manual review to ensure both look good
|
|
707
|
+
});
|
|
708
|
+
```
|
|
709
|
+
|
|
710
|
+
## Resources
|
|
711
|
+
|
|
712
|
+
- [Playwright Visual Comparisons](https://playwright.dev/docs/test-snapshots)
|
|
713
|
+
- [Percy Documentation](https://docs.percy.io/)
|
|
714
|
+
- [Chromatic Documentation](https://www.chromatic.com/docs/)
|
|
715
|
+
- [BackstopJS](https://github.com/garris/BackstopJS)
|
|
716
|
+
|
|
717
|
+
## Activation Keywords
|
|
718
|
+
|
|
719
|
+
Ask me about:
|
|
720
|
+
- "How to set up visual regression testing"
|
|
721
|
+
- "Playwright screenshot testing"
|
|
722
|
+
- "Percy vs Chromatic comparison"
|
|
723
|
+
- "Visual testing for components"
|
|
724
|
+
- "How to fix flaky visual tests"
|
|
725
|
+
- "Managing visual baselines in CI"
|
|
726
|
+
- "Cross-browser visual testing"
|
|
727
|
+
- "Screenshot comparison best practices"
|
|
728
|
+
- "Visual regression CI integration"
|