Initial commit

This commit is contained in:
Zhongwei Li
2025-11-29 18:16:40 +08:00
commit f125e90b9f
370 changed files with 67769 additions and 0 deletions

View File

@@ -0,0 +1,22 @@
# Changelog
## 0.3.0
- Refactored to Anthropic progressive disclosure pattern
- Updated description with "Use PROACTIVELY when..." format
- Removed version/author/category/tags from frontmatter
## 0.2.0
- Added framework version detection (Tailwind v3/v4, React 17-19, Next.js 13-14)
- Added pre-flight health check (Phase 2.5)
- Added error pattern recovery database
- Fixed Tailwind CSS v4 compatibility
## 0.1.0
- Initial release with zero-setup Playwright automation
- Multi-framework support: React/Vite, Next.js, Node.js, static
- LLM-powered visual analysis for UI bug detection
- Visual regression testing with baseline comparison
- Fix recommendations with file:line references

View File

@@ -0,0 +1,387 @@
# Playwright E2E Automation
> Automated Playwright e2e testing framework with LLM-powered visual debugging, screenshot analysis, and regression testing
## Quick Start
### Trigger Phrases
Simply ask Claude Code:
```
"set up playwright testing for my app"
"help me debug UI issues with screenshots"
"create e2e tests with visual regression"
"analyze my app's UI with screenshots"
```
### What Happens
This skill will automatically:
1. **Detect your application** - Identifies React/Vite, Node.js, static sites, or full-stack apps
2. **Detect framework versions** (NEW) - Determines Tailwind v3 vs v4, React version, etc.
3. **Pre-flight validation** (NEW) - Checks app loads before running tests, catches config errors early
4. **Install Playwright** - Runs `npm init playwright@latest` with optimal configuration
5. **Generate test suite** - Creates screenshot-enabled tests with version-appropriate templates
6. **Capture screenshots** - Takes full-page screenshots at key interaction points
7. **Analyze visually** - Uses LLM vision to identify UI bugs, layout issues, accessibility problems
8. **Detect regressions** - Compares against baselines to find unexpected visual changes
9. **Generate fixes** - Produces actionable code recommendations with file:line references
10. **Export test suite** - Provides production-ready tests you can run independently
**Total time**: ~5-8 minutes (one-time setup)
**New in v0.2.0**: Version detection and pre-flight validation prevent configuration errors
## Features
### Zero-Setup Automation
No configuration required. The skill:
- Detects your framework automatically (React, Vite, Next.js, Express, etc.)
- Installs Playwright and browsers without prompts
- Generates optimal configuration based on your app type
- Creates tests following best practices
- Runs everything end-to-end
### Multi-Framework Support
Works with:
- **React/Vite** - Modern React apps with Vite dev server
- **Next.js** - Server-side rendered React applications
- **Node.js/Express** - Backend services with HTML responses
- **Static HTML/CSS/JS** - Traditional web applications
- **Full-stack** - Combined frontend + backend applications
### Version-Aware Configuration (NEW in v0.2.0)
The skill now detects installed framework versions and adapts automatically:
**Tailwind CSS**:
- **v3.x**: Uses `@tailwind base; @tailwind components; @tailwind utilities;` syntax
- **v4.x**: Uses `@import "tailwindcss";` syntax and `@tailwindcss/postcss` plugin
**React**:
- **v17**: Classic JSX transform (requires React import)
- **v18+**: Automatic JSX transform (no import needed)
**Detection Process**:
1. Reads `package.json` dependencies
2. Matches versions against compatibility database
3. Selects appropriate templates (CSS, PostCSS config, etc.)
4. Warns about breaking changes or unknown versions
**Pre-flight Validation**:
- Loads app in browser before running tests
- Monitors console for critical errors
- Matches errors against known patterns (Tailwind v4 syntax, PostCSS plugin, etc.)
- Provides specific fix steps with file:line references
- **Prevents cascade failures**: One config error won't fail all 10 tests
**Example Error Detection**:
```
❌ Pre-flight check failed: Tailwind CSS v4 syntax mismatch
Root cause: CSS file uses @tailwind directives but v4 requires @import
Fix:
1. Update src/index.css:
Change from: @tailwind base; @tailwind components; @tailwind utilities;
Change to: @import "tailwindcss";
2. Update postcss.config.js:
Change from: plugins: { tailwindcss: {} }
Change to: plugins: { '@tailwindcss/postcss': {} }
3. Restart dev server: npm run dev
```
### LLM-Powered Visual Analysis
Automatically identifies:
- **UI Bugs** - Broken layouts, overlapping elements, cut-off text
- **Accessibility Issues** - Color contrast, missing labels, improper heading hierarchy
- **Responsive Problems** - Elements not scaling, overflow on mobile
- **Visual Regressions** - Unexpected changes from baseline screenshots
- **Missing Elements** - Expected UI components not rendered
### Actionable Fix Recommendations
Generates specific fixes with:
- File paths and line numbers (`src/components/Button.tsx:45`)
- Current code snippets showing the issue
- Recommended code changes
- Explanation of why the fix works
- Priority level (critical, high, medium, low)
### Production-Ready Test Suite
Exports include:
- Organized test files following best practices
- Page object models for maintainability
- Screenshot helpers and utilities
- npm scripts for test execution
- README with usage instructions
- CI/CD integration examples
## Usage Examples
### Example 1: Initial Setup for React App
```
User: "Set up Playwright testing for my Vite React app"
Claude (via skill):
✅ Detected React + Vite application
✅ Installing Playwright...
✅ Generated playwright.config.ts with base URL http://localhost:5173
✅ Created 5 test specs covering critical journeys
✅ Captured 23 screenshots across desktop, tablet, mobile viewports
✅ Visual analysis complete: Found 3 issues (1 high, 2 medium)
✅ Generated fix recommendations in fix-recommendations.md
✅ Test suite exported to tests/e2e/
Next steps:
- Review fix-recommendations.md
- Run tests: npm run test:e2e
- Update baselines after fixes: npm run test:e2e:update-snapshots
```
### Example 2: Visual Regression Detection
```
User: "Check for visual regressions after my recent changes"
Claude (via skill):
✅ Running existing Playwright test suite...
✅ Captured 23 screenshots
✅ Comparing against baselines...
⚠️ Found 4 visual changes:
- Home page hero section: Layout shift detected (12% difference)
- Navigation menu: New dropdown added (expected change)
- Contact form: Button text color changed (2% difference)
- Footer: Logo size increased (5% difference)
📊 Visual regression report: screenshots/diffs/report.html
Would you like to:
1. Accept these changes as new baseline
2. Review specific diffs
3. Get fix recommendations for unintended changes
```
### Example 3: Bug Investigation
```
User: "Users report the checkout button is cut off on mobile"
Claude (via skill):
✅ Generating mobile viewport test...
✅ Captured screenshot: screenshots/current/checkout-mobile.png
✅ Visual analysis complete
🐛 Issue confirmed: Button text truncated on 375px viewport
Location: src/components/CheckoutButton.tsx:28
Current code:
```tsx
<button className="px-6 py-3 text-xl font-bold">
Complete Purchase
</button>
```
Recommended fix:
```tsx
<button className="px-4 py-2 text-sm sm:text-base md:text-xl font-bold max-w-full">
Complete Purchase
</button>
```
Reasoning: Fixed padding and font size cause overflow on narrow screens.
Added responsive sizing and max-width constraint.
Test added: tests/e2e/checkout-responsive.spec.ts
```
## What Gets Generated
After running this skill, you'll have:
```
your-project/
├── playwright.config.ts # Playwright configuration
├── tests/
│ └── e2e/
│ ├── setup/
│ │ └── global-setup.ts # Dev server startup
│ ├── pages/
│ │ └── *.page.ts # Page object models
│ ├── specs/
│ │ └── *.spec.ts # Test specifications
│ └── utils/
│ └── screenshot-helper.ts
├── screenshots/
│ ├── baselines/ # Reference images
│ ├── current/ # Latest run
│ └── diffs/ # Visual comparisons
├── fix-recommendations.md # Generated fix suggestions
├── visual-analysis-report.md # LLM analysis results
└── package.json # Updated with test scripts
```
## Performance
**Typical execution time** (React app with 5 critical journeys):
- Application detection: ~5 seconds
- Playwright installation: ~2-3 minutes (one-time)
- Test generation: ~30 seconds
- Test execution: ~30-60 seconds
- Visual analysis: ~1-2 minutes
- Regression comparison: ~10 seconds
- Fix generation: ~30 seconds
**Total**: ~5-8 minutes (excluding one-time Playwright install)
## CI/CD Integration
### GitHub Actions
The skill generates GitHub Actions workflow examples. Basic setup:
```yaml
name: Playwright E2E Tests
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
- run: npm ci
- run: npx playwright install --with-deps
- run: npm run test:e2e
- uses: actions/upload-artifact@v3
if: always()
with:
name: playwright-screenshots
path: screenshots/
```
### Baseline Management
**In CI**:
- Store baselines in repository: `git add screenshots/baselines/`
- Tests fail if visual diffs exceed threshold
- Review artifacts before merging
**Locally**:
- Update baselines: `npm run test:e2e:update-snapshots`
- Commit updated baselines after review
## Advanced Usage
### Custom Test Generation
After initial setup, you can:
1. **Add more tests** - Follow the pattern in generated specs
2. **Customize viewports** - Edit playwright.config.ts
3. **Add custom assertions** - Extend screenshot helpers
4. **Configure browsers** - Enable Firefox/WebKit in config
5. **Adjust thresholds** - Modify visual diff sensitivity
### Visual Analysis Customization
The skill's analysis focuses on:
- WCAG 2.1 AA accessibility compliance (see `data/accessibility-checks.md`)
- Common UI bug patterns (see `data/common-ui-bugs.md`)
- Framework-specific best practices
### Integration with Existing Tests
This skill complements your existing test suite:
- **Unit tests** (Vitest/Jest) - Test logic and calculations
- **Integration tests** - Test component interaction
- **E2E tests** (Playwright) - Test full user workflows + visual regression
All three work together without conflicts.
## Troubleshooting
### "Application not detected"
**Solution**: Specify manually
```
"Set up Playwright for my [framework] app running on port [port]"
```
### "Dev server not running"
**Solution**: The skill will attempt to start it automatically. If that fails:
```bash
npm run dev # Start your dev server first
```
Then re-run the skill.
### "Screenshot capture timeout"
**Solution**: Increase timeout in playwright.config.ts:
```typescript
timeout: 60000, // 60 seconds instead of default 30
```
### "Visual analysis found too many false positives"
**Solution**: Adjust the visual diff threshold:
```typescript
expect(await page.screenshot()).toMatchSnapshot({
maxDiffPixelRatio: 0.05, // Allow 5% difference
});
```
## Requirements
- **Node.js**: >=16.0.0
- **npm**: >=7.0.0
- **Disk space**: ~500MB for Playwright browsers (one-time)
- **Memory**: ~500MB during test execution
## Best Practices
1. **Baseline management** - Commit baselines to git, update deliberately
2. **Screenshot organization** - Use .gitignore for current/diffs, keep baselines
3. **Test critical paths** - Focus on user journeys that matter (80/20 rule)
4. **Run in CI** - Catch regressions before production
5. **Review diffs carefully** - Not all changes are bugs
6. **Use semantic selectors** - Prefer getByRole over CSS selectors
7. **Capture context** - Take screenshots before AND after interactions
## Learn More
- [Playwright Documentation](https://playwright.dev/)
- [Visual Regression Testing Guide](https://playwright.dev/docs/test-snapshots)
- [Accessibility Testing](https://playwright.dev/docs/accessibility-testing)
- [CI/CD Integration](https://playwright.dev/docs/ci)
## Support
Issues with this skill? Please report at:
- [Claude Code Issues](https://github.com/anthropics/claude-code/issues)
---
**Created with**: skill-creator v0.1.0
**Skill Version**: 0.1.0
**Last Updated**: 2025-11-01

View File

@@ -0,0 +1,141 @@
---
name: playwright-e2e-automation
description: Use PROACTIVELY when setting up e2e testing, debugging UI issues, or creating regression test suites. Automated Playwright framework with LLM-powered visual analysis, screenshot capture, and fix recommendations with file:line references. Zero-setup for React/Vite, Node.js, static sites, and full-stack applications. Not for unit testing, API-only testing, or mobile native apps.
---
# Playwright E2E Automation
## Overview
This skill automates the complete Playwright e2e testing lifecycle with LLM-powered visual debugging. It detects your app type, installs Playwright, generates tests, captures screenshots, analyzes for UI bugs, and produces fix recommendations with file paths and line numbers.
**Key Capabilities**:
- Zero-setup automation with multi-framework support
- Visual debugging with screenshot capture and LLM analysis
- Regression testing with baseline comparison
- Actionable fix recommendations with file:line references
- CI/CD ready test suite export
## When to Use This Skill
**Trigger Phrases**:
- "set up playwright testing for my app"
- "help me debug UI issues with screenshots"
- "create e2e tests with visual regression"
- "analyze my app's UI with screenshots"
- "generate playwright tests for [my app]"
**Use Cases**:
- Setting up Playwright testing from scratch
- Debugging visual/UI bugs hard to describe in text
- Creating screenshot-based regression testing
- Generating e2e test suites for new applications
- Identifying accessibility issues through visual inspection
**NOT for**:
- Unit testing or component testing (use Vitest/Jest)
- API-only testing without UI
- Performance/load testing
- Mobile native app testing (use Detox/Appium)
## Response Style
- **Automated**: Execute entire workflow with minimal user intervention
- **Informative**: Clear progress updates at each phase
- **Visual**: Always capture and analyze screenshots
- **Actionable**: Generate specific fixes with file paths and line numbers
## Quick Decision Matrix
| User Request | Action | Reference |
|--------------|--------|-----------|
| "set up playwright" | Full setup workflow | `workflow/phase-1-discovery.md``phase-2-setup.md` |
| "debug UI issues" | Capture + Analyze | `workflow/phase-4-capture.md``phase-5-analysis.md` |
| "check for regressions" | Compare baselines | `workflow/phase-6-regression.md` |
| "generate fix recommendations" | Analyze + Generate | `workflow/phase-7-fixes.md` |
| "export test suite" | Package for CI/CD | `workflow/phase-8-export.md` |
## Workflow Overview
### Phase 1: Application Discovery
Detect app type, framework versions, and optimal configuration.
**Details**: `workflow/phase-1-discovery.md`
### Phase 2: Playwright Setup
Install Playwright and generate configuration.
**Details**: `workflow/phase-2-setup.md`
### Phase 2.5: Pre-flight Health Check
Validate app loads correctly before full test suite.
**Details**: `workflow/phase-2.5-preflight.md`
### Phase 3: Test Generation
Create screenshot-enabled tests for critical workflows.
**Details**: `workflow/phase-3-generation.md`
### Phase 4: Screenshot Capture
Run tests and capture visual data.
**Details**: `workflow/phase-4-capture.md`
### Phase 5: Visual Analysis
LLM-powered analysis to identify UI bugs.
**Details**: `workflow/phase-5-analysis.md`
### Phase 6: Regression Detection
Compare screenshots against baselines.
**Details**: `workflow/phase-6-regression.md`
### Phase 7: Fix Generation
Map issues to source code with actionable fixes.
**Details**: `workflow/phase-7-fixes.md`
### Phase 8: Test Suite Export
Package production-ready test suite.
**Details**: `workflow/phase-8-export.md`
## Important Reminders
1. **Capture before AND after interactions** - Provides context for visual debugging
2. **Use semantic selectors** - Prefer getByRole, getByLabel over CSS selectors
3. **Baseline management is critical** - Keep in sync with intentional UI changes
4. **LLM analysis is supplementary** - Use alongside automated assertions
5. **Test critical paths first** - Focus on user journeys that matter most (80/20 rule)
6. **Screenshots are large** - Consider .gitignore for screenshots/, use CI artifacts
7. **Run tests in CI** - Catch visual regressions before production
8. **Update baselines deliberately** - Review diffs carefully before accepting
## Limitations
- Requires Node.js >= 16
- Browser download needs ~500MB disk space
- Screenshot comparison requires consistent rendering (may vary across OS)
- LLM analysis adds ~5-10 seconds per screenshot
- Not suitable for testing behind VPNs without additional configuration
## Reference Materials
| Resource | Purpose |
|----------|---------|
| `workflow/*.md` | Detailed phase instructions |
| `reference/troubleshooting.md` | Common issues and fixes |
| `reference/ci-cd-integration.md` | GitHub Actions, GitLab CI examples |
| `data/framework-versions.yaml` | Version compatibility database |
| `data/error-patterns.yaml` | Known error patterns with recovery |
| `templates/` | Config and test templates |
| `examples/` | Sample setups for different frameworks |
## Success Criteria
- [ ] Playwright installed with browsers
- [ ] Configuration generated for app type
- [ ] Test suite created (3-5 critical journey tests)
- [ ] Screenshots captured and organized
- [ ] Visual analysis completed with issue categorization
- [ ] Regression comparison performed
- [ ] Fix recommendations generated
- [ ] Test suite exported with documentation
- [ ] All tests executable via `npm run test:e2e`
---
**Total time**: ~5-8 minutes (excluding one-time Playwright install)

View File

@@ -0,0 +1,414 @@
# Accessibility Checks for Visual Analysis
WCAG 2.1 AA compliance criteria for LLM-powered screenshot analysis.
## Overview
When analyzing screenshots, check for these accessibility violations. This guide follows WCAG 2.1 Level AA standards.
## 1. Color Contrast
### Minimum Contrast Ratios
**Text:**
- Normal text (< 18pt or < 14pt bold): **4.5:1**
- Large text (≥ 18pt or ≥ 14pt bold): **3:1**
**UI Components:**
- Form inputs, buttons, icons: **3:1** against background
### Common Violations in Screenshots
```
❌ Light gray text on white background (2:1 ratio)
✅ Dark gray #595959 on white #FFFFFF (7:1 ratio)
❌ Blue link #4A90E2 on light blue #E8F4FF (1.8:1 ratio)
✅ Blue link #0066CC on white #FFFFFF (8.2:1 ratio)
❌ Gray placeholder text #CCCCCC on white (1.6:1 ratio)
✅ Gray placeholder text #757575 on white (4.6:1 ratio)
```
### Visual Indicators
When analyzing screenshots, look for:
- Pale or faded text that's hard to read
- Low-contrast buttons that don't stand out
- Links that blend into surrounding text
- Disabled states that are barely distinguishable
## 2. Text Size and Readability
### Minimum Font Sizes
- Body text: **16px** minimum (1rem)
- Small text acceptable: **14px** for secondary content
- Avoid: Text smaller than **12px** (fails WCAG)
### Common Violations
```
❌ Body text at 12px - too small for many users
✅ Body text at 16px or larger
❌ Mobile text at 10px - illegible on small screens
✅ Mobile text at 14px minimum
❌ Long paragraphs with no line height spacing
✅ Line height 1.5x for body text (e.g., 16px text with 24px line height)
```
### Visual Indicators
- Text that appears squished or compressed
- Long lines of text with minimal spacing
- Tiny labels on buttons or form fields
## 3. Focus Indicators
### Requirements
All interactive elements must have **visible focus indicators**:
- Minimum **2px** outline or border
- Contrast ratio of **3:1** against background
- Clearly visible when tabbing through interface
### Common Violations
```
❌ No visible outline when button is focused
✅ Blue 2px outline appears on focus
❌ Focus outline same color as background (invisible)
✅ High-contrast focus outline (e.g., black on white)
❌ Focus state only indicated by subtle background color change
✅ Focus state with outline + background color change
```
### Visual Indicators in Screenshots
Look for:
- Focused element (if screenshot captures tab state)
- Absence of visible outline or border
- Focus indicator that's too subtle or low-contrast
## 4. Form Labels and Instructions
### Requirements
- Every form input must have a visible **<label>** or aria-label
- Labels must be **adjacent** to their inputs
- Required fields must be clearly indicated
- Error messages must be **visible and associated** with inputs
### Common Violations
```
❌ Input with only placeholder text (disappears when typing)
✅ Input with persistent label above or beside it
❌ Label far away from input (hard to associate)
✅ Label immediately adjacent to input
❌ Required field marked only with color (red border)
✅ Required field marked with * and "Required" text
❌ Error message in different part of page
✅ Error message directly below input field
```
### Visual Indicators
- Inputs without visible labels
- Placeholder text used as labels (disappears on focus)
- Required fields indicated only by color
- Error states without clear error text
## 5. Heading Hierarchy
### Requirements
- Headings must follow logical order: **H1 → H2 → H3** (no skipping)
- Page should have exactly **one H1** (page title)
- Headings should be **visually distinct** from body text
### Common Violations
```
❌ Page with H1 → H4 (skips H2, H3)
✅ Page with H1 → H2 → H3
❌ Multiple H1 headings on same page
✅ Single H1 for page title, H2s for sections
❌ Heading text same size as body text
✅ Headings progressively larger: H3 < H2 < H1
```
### Visual Indicators in Screenshots
- Headings that don't look like headings (same size as body)
- Missing visual hierarchy (all headings same size)
- Text that looks like headings but isn't (bold body text)
## 6. Alternative Text for Images
### Requirements
- Decorative images: Empty alt="" or aria-hidden="true"
- Informative images: Descriptive alt text
- Complex images (charts, graphs): Detailed description
### Common Violations
**Note:** Can't always detect from screenshots alone, but can identify likely issues:
```
❌ Icon buttons with no visible text label (likely missing aria-label)
✅ Icon buttons with visible text label or tooltip
❌ Charts/graphs with no accompanying data table or description
✅ Charts with descriptive caption or linked data table
❌ Images that convey important info but might lack alt text
✅ Important info also available in visible text
```
### Visual Indicators
- Icon-only buttons without text labels
- Charts/infographics without textual explanations
- Images that appear to contain important information
## 7. Keyboard Navigation
### Requirements
- All interactive elements accessible via keyboard
- Logical tab order (top to bottom, left to right)
- No keyboard traps
- Skip links for navigation
### Visual Analysis Cues
**Can identify potential issues from screenshots:**
```
❌ Custom dropdown without visible keyboard focus states
✅ Standard HTML select or custom with clear focus indicators
❌ Modal dialog with no visible close button (might trap keyboard)
✅ Modal with visible, accessible close button
❌ Navigation menu requiring hover (might be keyboard inaccessible)
✅ Navigation menu that works on click/enter
```
## 8. Touch Target Size
### Minimum Sizes (Mobile)
- Interactive elements: **44x44 CSS pixels** minimum
- Adequate spacing between targets: **8px** minimum
### Common Violations
```
❌ Mobile buttons at 32x32px (too small)
✅ Mobile buttons at 48x48px
❌ Links in mobile menu spaced 4px apart (accidental taps)
✅ Links spaced 12px apart
❌ Checkbox at 16x16px on mobile (hard to tap)
✅ Checkbox with expanded tap area 44x44px
```
### Visual Indicators in Mobile Screenshots
- Tiny buttons that would be hard to tap accurately
- Densely packed clickable elements
- Links or buttons too close together
## 9. Responsive Design
### Requirements
- Content readable without horizontal scrolling
- No text truncation
- Proper scaling on different viewports
- No overlapping content
### Common Violations
```
❌ Desktop layout on mobile with horizontal scroll
✅ Mobile-optimized layout with no horizontal scroll
❌ Text cut off at viewport edge
✅ Text wraps properly within viewport
❌ Fixed-width elements overflow on small screens
✅ Flexible/responsive elements scale to screen size
❌ Overlapping elements on mobile (buttons on top of text)
✅ Elements stack vertically with proper spacing
```
### Visual Indicators Across Viewports
When comparing desktop/tablet/mobile screenshots:
- Text that gets cut off on smaller screens
- Overlapping or compressed elements
- Horizontal scrollbars on mobile
- Unreadable small text on mobile
## 10. Color Not Sole Indicator
### Requirements
- Information must not rely on **color alone**
- Use patterns, icons, or text in addition to color
### Common Violations
```
❌ Required fields indicated only by red border
✅ Required fields with red border + "*" icon + "Required" text
❌ Success/error only shown by green/red color
✅ Success/error shown by color + icon + text message
❌ Chart legend with only colored boxes
✅ Chart legend with colored boxes + patterns + labels
❌ Form validation using only red/green highlighting
✅ Form validation with color + icons + error text
```
### Visual Indicators
- Status indicators using only color
- Charts relying solely on color to differentiate data
- Form states indicated only by color changes
- Links distinguished only by color (not underline)
## Visual Analysis Workflow
When analyzing a screenshot for accessibility:
### Step 1: Text and Contrast
1. Check all text for sufficient contrast (4.5:1 for body, 3:1 for large)
2. Verify text is large enough (16px minimum)
3. Check line height and spacing for readability
### Step 2: Interactive Elements
1. Identify all buttons, links, form inputs
2. Verify they have sufficient size (44x44px on mobile)
3. Check for visible focus indicators (if focus state captured)
4. Ensure adequate spacing between targets
### Step 3: Form Elements
1. Check each input has visible label
2. Verify required fields clearly marked (not just color)
3. Look for error messages (should be near inputs)
### Step 4: Structure
1. Check heading hierarchy (visual size progression)
2. Verify logical content flow
3. Look for proper spacing and organization
### Step 5: Responsive Issues
1. Check for text truncation or cutoff
2. Look for overlapping elements
3. Verify no horizontal scroll
4. Ensure touch targets appropriate for viewport
### Step 6: Color Usage
1. Identify any color-only indicators
2. Verify status messages use icons or text too
3. Check charts/graphs have non-color differentiation
## Severity Levels
When reporting accessibility issues from screenshots:
### Critical (P0)
- Contrast ratio < 3:1 for any text
- Missing form labels
- Keyboard trap (if detectable)
- Content not accessible without horizontal scroll
### High (P1)
- Contrast ratio 3:1-4.4:1 for normal text
- Touch targets < 44x44px on mobile
- Heading hierarchy violations
- Color as sole indicator for critical info
### Medium (P2)
- Text size < 14px for body content
- Insufficient spacing between touch targets (< 8px)
- Inconsistent focus indicators
- Minor responsive issues
### Low (P3)
- Line height < 1.4 for long text blocks
- Decorative images possibly missing alt (can't confirm from screenshot)
- Minor visual hierarchy inconsistencies
## Example Analysis Output
```markdown
## Accessibility Issues Found
### Critical (1)
1. **Insufficient color contrast on form labels**
- Location: Contact form, all input labels
- Issue: Light gray #AAAAAA on white #FFFFFF (2.6:1 ratio)
- Requirement: 4.5:1 for normal text
- Fix: Use darker gray #595959 (7:1 ratio)
### High (2)
1. **Missing visible labels on inputs**
- Location: Email and password fields
- Issue: Only placeholder text, no persistent label
- Fix: Add visible <label> elements above inputs
2. **Touch targets too small on mobile**
- Location: Social media icons in footer
- Issue: Icons are 24x24px (below 44x44px minimum)
- Fix: Increase tap area to 44x44px with padding
### Medium (1)
1. **Body text too small**
- Location: Article content
- Issue: 14px font size (recommended 16px minimum)
- Fix: Increase base font size to 16px
```
## Tools for Automated Checking
While visual analysis is manual, recommend these tools for comprehensive checks:
```typescript
// Integrate axe-core in Playwright tests
import { test, expect } from '@playwright/test';
import AxeBuilder from '@axe-core/playwright';
test('should not have accessibility violations', async ({ page }) => {
await page.goto('/');
const accessibilityScanResults = await new AxeBuilder({ page }).analyze();
expect(accessibilityScanResults.violations).toEqual([]);
});
```
---
**References:**
- [WCAG 2.1 Guidelines](https://www.w3.org/WAI/WCAG21/quickref/)
- [WebAIM Contrast Checker](https://webaim.org/resources/contrastchecker/)
- [Axe DevTools](https://www.deque.com/axe/devtools/)

View File

@@ -0,0 +1,557 @@
# Common UI Bugs - Visual Analysis Guide
Patterns and indicators for identifying UI bugs from screenshots during LLM-powered visual analysis.
## Layout Issues
### 1. Overlapping Elements
**Visual Indicators:**
- Text overlapping other text
- Buttons overlapping images or other buttons
- Content extending beyond container boundaries
- Z-index issues causing incorrect stacking
**Examples:**
```
❌ Modal dialog overlapped by dropdown menu
❌ Footer content overlapping main content
❌ Notification banner covering navigation
❌ Search results hidden behind fixed header
```
**Screenshot Analysis:**
- Look for any elements that appear on top of others unexpectedly
- Check if all content is fully visible and not obscured
- Verify layering makes sense (modals on top, backgrounds behind)
### 2. Text Truncation / Overflow
**Visual Indicators:**
- Text cut off mid-word or mid-letter
- Ellipsis (...) in unexpected places
- Content extending outside visible area
- Horizontal scrollbars on text containers
**Examples:**
```
❌ Button text: "Continue to Chec..." (truncated on mobile)
❌ Table header: "Customer N..." instead of "Customer Name"
❌ Card title cut off at viewport edge
❌ Long email addresses broken into random positions
```
**Screenshot Analysis:**
- Check if all text is fully visible
- Look for truncation indicators (...)
- Verify important text isn't cut off
- Check if text wraps properly on smaller viewports
### 3. Broken Grid/Flexbox Layouts
**Visual Indicators:**
- Cards or items with inconsistent sizes
- Uneven spacing between elements
- Elements not aligned in columns/rows
- One element significantly larger/smaller than siblings
**Examples:**
```
❌ Product grid: 3 cards same height, 1 card twice as tall
❌ Navigation items: uneven spacing (10px, 20px, 15px)
❌ Form inputs: labels misaligned with inputs
❌ Cards: some with images, some without, causing height mismatch
```
**Screenshot Analysis:**
- Check if grid items are evenly sized
- Verify consistent spacing between elements
- Look for alignment issues in rows/columns
- Identify items breaking out of grid structure
### 4. Responsive Breakpoint Issues
**Visual Indicators (comparing viewport sizes):**
- Desktop layout on mobile (very small text, horizontal scroll)
- Mobile layout on desktop (everything too large, wasted space)
- Sudden jumps in layout between similar viewport sizes
- Media queries not triggering at expected breakpoints
**Examples:**
```
❌ Desktop: 3-column layout → Mobile: Still 3 columns (too cramped)
✅ Desktop: 3-column layout → Tablet: 2 columns → Mobile: 1 column
❌ Desktop: 16px text → Mobile: 16px text (too small on small screen)
✅ Desktop: 16px text → Mobile: 14px text with increased line height
❌ Fixed sidebar pushes main content off screen on tablet
✅ Sidebar collapses to hamburger menu on tablet
```
**Screenshot Analysis:**
- Compare same page across desktop (1280px+), tablet (768px), mobile (375px)
- Check if layout adapts appropriately at each size
- Verify no horizontal scrolling on mobile
- Ensure touch targets are 44x44px minimum on mobile
## Component-Specific Issues
### 5. Form Validation Problems
**Visual Indicators:**
- Error messages in wrong location (far from input)
- No visible error state (input looks normal despite error)
- Success state not clearly indicated
- Disabled buttons without indication why
**Examples:**
```
❌ Error message at top of page, input in middle (hard to associate)
✅ Error message directly below relevant input
❌ Invalid input: red border only (color-blind users miss it)
✅ Invalid input: red border + error icon + error text
❌ Submit button disabled, no explanation why
✅ Submit button disabled with tooltip "Complete required fields"
❌ Multiple validation errors shown as one generic message
✅ Each field shows its specific error message
```
**Screenshot Analysis:**
- Check if error states are clearly visible
- Verify error messages are near their inputs
- Look for validation indicators beyond just color
- Confirm required fields are clearly marked
### 6. Button States
**Visual Indicators:**
- Active/hover/focus states indistinguishable
- Disabled buttons look clickable
- Primary vs secondary buttons unclear
- Loading/submitting state not indicated
**Examples:**
```
❌ Disabled button: gray text on light gray (looks clickable)
✅ Disabled button: clear visual indication (opacity, cursor, label)
❌ Primary and secondary buttons identical appearance
✅ Primary: bold color, secondary: outline only
❌ Button clicked but no loading indicator (looks broken)
✅ Button shows spinner or "Loading..." text when clicked
❌ Hover state same as default state (no feedback)
✅ Hover state: darker background or subtle animation
```
**Screenshot Analysis:**
- Verify different button states are visually distinct
- Check if disabled buttons clearly look non-interactive
- Look for visual feedback on interactive states
- Ensure primary actions are visually prominent
### 7. Image Loading Issues
**Visual Indicators:**
- Broken image icons (usually a small icon or alt text)
- Missing images (blank space where image should be)
- Images with wrong aspect ratio (stretched/squashed)
- Low-resolution images appearing pixelated
**Examples:**
```
❌ Product image slot shows broken image icon
❌ Profile picture area: empty circle (image failed to load)
❌ Banner image: 16:9 image stretched to 1:1 (distorted)
❌ Thumbnail: tiny image scaled up 3x (pixelated)
```
**Screenshot Analysis:**
- Look for broken image indicators
- Check if all images loaded successfully
- Verify images maintain proper aspect ratios
- Identify pixelated or low-quality images
### 8. Table/List Issues
**Visual Indicators:**
- Headers not aligned with columns
- Inconsistent row heights
- Text overflow in cells
- Missing borders or inconsistent borders
- Poor mobile table handling (horizontal scroll)
**Examples:**
```
❌ Table headers offset from column data
❌ One row 2x height of others (content wrapping differently)
❌ Cell content: "john.doe@verylongemailaddr..." (truncated)
❌ Mobile table: requires horizontal scroll to see all columns
```
**Screenshot Analysis:**
- Verify column headers align with data
- Check for consistent row heights
- Look for text overflow in cells
- Ensure tables are readable on mobile (responsive design)
## Content Issues
### 9. Missing Content
**Visual Indicators:**
- Empty sections (just headers, no content)
- Placeholder text in production ("Lorem ipsum", "TBD", "Coming soon")
- Missing images or icons where expected
- Incomplete sentences or paragraphs
**Examples:**
```
❌ Section header "Latest Articles" with no articles below
❌ Product description: "Lorem ipsum dolor sit amet..."
❌ Icon placeholder: gray square instead of actual icon
❌ Bio section: ends mid-sentence
```
**Screenshot Analysis:**
- Identify any placeholder content
- Look for empty sections or containers
- Check if all expected content is present
- Verify no incomplete text
### 10. Inconsistent Spacing/Padding
**Visual Indicators:**
- Uneven margins between sections
- Inconsistent padding inside containers
- Elements touching edges (no breathing room)
- Random spacing that doesn't follow a system
**Examples:**
```
❌ Section 1: 40px margin → Section 2: 25px margin → Section 3: 35px
✅ All sections: consistent 40px margin
❌ Card padding: 16px top, 20px right, 14px bottom, 18px left
✅ Card padding: 16px all sides
❌ Button text touching button edge (no padding)
✅ Button: 12px vertical, 20px horizontal padding
```
**Screenshot Analysis:**
- Check for consistent spacing throughout page
- Verify elements have appropriate padding
- Look for crowded areas with insufficient spacing
- Identify spacing that breaks visual rhythm
## Typography Issues
### 11. Font Rendering Problems
**Visual Indicators:**
- Jagged or pixelated text
- Text weight too thin (hard to read)
- Inconsistent font families
- Line height too tight or too loose
**Examples:**
```
❌ Body text: font-weight 100 (barely visible)
✅ Body text: font-weight 400 (readable)
❌ Headings: Arial → Body: Times New Roman (inconsistent)
✅ All text: consistent font family
❌ Long paragraph: line-height 1.0 (text touching)
✅ Long paragraph: line-height 1.5 (readable spacing)
```
**Screenshot Analysis:**
- Check if text is crisp and readable
- Verify consistent font families
- Look for appropriate line height (1.4-1.6 for body text)
- Ensure font weights are accessible
### 12. Text Alignment Issues
**Visual Indicators:**
- Center-aligned paragraphs (hard to read)
- Inconsistent alignment within a section
- Right-aligned text in LTR layout without reason
- Justified text with large gaps
**Examples:**
```
❌ Long paragraph: center-aligned (hard to follow)
✅ Long paragraph: left-aligned
❌ Form: labels left-aligned, some center-aligned randomly
✅ Form: all labels consistently left-aligned
❌ Justified text: large gaps between words ("rivers")
✅ Left-aligned text with ragged right edge
```
**Screenshot Analysis:**
- Check if alignment aids readability
- Verify consistent alignment within sections
- Look for awkward gaps in justified text
- Ensure alignment makes sense for content type
## Interactive Element Issues
### 13. Hover/Focus States Missing
**Note:** Only detectable if screenshot captures focused/hovered state
**Visual Indicators:**
- Link looks identical to surrounding text (no underline, same color)
- Focused input indistinguishable from unfocused
- Hovered button shows no change
- Dropdown menu items don't highlight on hover
**Examples:**
```
❌ Link: blue text, no underline, same as normal text
✅ Link: blue text with underline, or different color
❌ Input focused: looks identical to unfocused state
✅ Input focused: blue border or outline appears
❌ Menu item hovered: no visual change
✅ Menu item hovered: background color change
```
**Screenshot Analysis (if interactive state captured):**
- Verify interactive elements show visual feedback
- Check if focused element has clear indicator
- Look for hover states that provide feedback
- Ensure keyboard focus is visible
### 14. Icon Issues
**Visual Indicators:**
- Icons misaligned with text
- Icons wrong size (too large or too small)
- Icons wrong color (low contrast, invisible)
- Icon-only buttons without labels or tooltips
**Examples:**
```
❌ Icon button: 16x16px icon in 48x48px button (looks lost)
✅ Icon button: 24x24px icon in 48x48px button (balanced)
❌ Icon: white on light gray background (barely visible)
✅ Icon: dark gray on light gray (clear contrast)
❌ Icon: baseline-aligned with text (appears raised)
✅ Icon: center-aligned with text
❌ Icon-only button with no label (unclear purpose)
✅ Icon button with aria-label or visible text label
```
**Screenshot Analysis:**
- Check if icons are appropriately sized
- Verify icons have sufficient contrast
- Look for proper alignment with adjacent text
- Ensure icon buttons have clear purpose
## Color and Theme Issues
### 15. Dark Mode Issues
**Visual Indicators (when comparing light/dark screenshots):**
- White text on light background (inverted incorrectly)
- Hard-coded colors not switching with theme
- Images/logos with wrong theme variant
- Insufficient contrast in dark mode
**Examples:**
```
❌ Dark mode: #333 text on #000 background (low contrast)
✅ Dark mode: #E0E0E0 text on #1A1A1A background
❌ Light mode logo on dark background (invisible)
✅ Dark mode variant logo displayed
❌ Input background: white in both modes (wrong in dark)
✅ Input background: white in light, #2A2A2A in dark
```
**Screenshot Analysis (compare light/dark if available):**
- Verify all colors invert appropriately
- Check contrast ratios in both modes
- Look for hard-coded colors that don't adapt
- Ensure images/logos have correct variants
### 16. Brand Color Misuse
**Visual Indicators:**
- Too many competing colors
- Brand colors used incorrectly (primary for everything)
- Status colors confusing (green for error, red for success)
- Inaccessible color combinations
**Examples:**
```
❌ All buttons primary color (no hierarchy)
✅ Primary button: brand color, secondary: gray/outline
❌ Success message in red, error in green (confusing)
✅ Success in green, error in red, warning in amber
❌ 8 different colors used on one page (chaotic)
✅ Consistent color palette: 2-3 main colors + neutrals
```
**Screenshot Analysis:**
- Check if color usage is consistent and meaningful
- Verify status colors match conventions (green=success, red=error)
- Look for excessive color variety
- Ensure brand colors used appropriately
## Animation and Transition Issues
**Note:** Difficult to detect from static screenshots, but can infer
### 17. Loading States
**Visual Indicators:**
- Content area completely empty (no skeleton/spinner)
- "Loading..." text with no visual indicator
- Sudden content appearance (jarring)
- Infinite loading (screenshot shows spinner forever)
**Examples:**
```
❌ Empty white space while loading (looks broken)
✅ Skeleton UI placeholders during load
❌ Just text "Loading..." (static, looks stuck)
✅ Animated spinner + "Loading..." text
❌ Screenshot from 30 seconds ago: still loading (timeout issue)
✅ Content loads within reasonable time (< 3 seconds)
```
**Screenshot Analysis:**
- Look for loading indicators
- Check if empty states have placeholders
- Identify potential timeout issues (loading too long)
## Mobile-Specific Issues
### 18. Fixed Positioning Problems
**Visual Indicators:**
- Fixed header covering content (not enough top padding)
- Fixed footer hiding interactive elements
- Input fields hidden behind keyboard (inferred)
- Fixed elements overlapping each other
**Examples:**
```
❌ Fixed header: covers first line of content
✅ Content has top padding equal to header height
❌ Fixed "Chat with us" button: covers form submit button
✅ Fixed button repositions when other content appears
❌ Input field: likely behind keyboard when focused
✅ Page scrolls input into view above keyboard
```
**Screenshot Analysis (mobile viewports):**
- Check if fixed headers leave room for content
- Verify fixed elements don't overlap important content
- Look for sufficient padding to account for fixed elements
### 19. Orientation Issues
**Visual Indicators (portrait vs landscape):**
- Content cut off in landscape mode
- Poor use of available space in landscape
- Fixed height elements that don't adapt
- Horizontal layout forced into vertical space
**Examples:**
```
❌ Portrait: shows full content → Landscape: content cut off
✅ Both orientations show full content
❌ Landscape: wide empty margins, cramped center content
✅ Landscape: content uses available width appropriately
```
**Screenshot Analysis (if both orientations available):**
- Compare same page in portrait and landscape
- Verify content adapts to available space
- Check if all content remains accessible
## Analysis Priority
When analyzing screenshots, prioritize issues by impact:
### Critical (Stop immediately)
1. Content completely missing or invisible
2. Major layout breaks (overlapping, off-screen)
3. Severe contrast violations (< 3:1)
4. Broken images or core UI elements
### High (Fix soon)
1. Text truncation losing important info
2. Form validation not visible
3. Responsive breakpoint failures
4. Touch targets too small (< 44px)
### Medium (Fix in next iteration)
1. Inconsistent spacing
2. Minor alignment issues
3. Missing hover/focus states
4. Moderate contrast issues (3:1-4.4:1)
### Low (Polish)
1. Minor typography inconsistencies
2. Slight spacing irregularities
3. Non-critical icon sizing
4. Subtle animation issues
## Generating Bug Reports
For each issue found, provide:
```markdown
### [Issue Title]
**Severity**: Critical | High | Medium | Low
**Location**: [Specific page/component where visible]
**Screenshot**: `path/to/screenshot.png` (timestamp: YYYY-MM-DD HH:MM:SS)
**Viewport**: Desktop 1280x720 | Tablet 768x1024 | Mobile 375x667
**Description**: [Clear description of what's wrong]
**Expected Behavior**: [What should appear instead]
**Likely Cause**: [Technical reason, e.g., "Missing max-width constraint", "Improper flexbox configuration"]
**Recommended Fix**:
- **File**: `src/components/Button.tsx`
- **Line**: 45
- **Current**: \`className="px-4 text-xl"\`
- **Fixed**: \`className="px-4 text-sm sm:text-base md:text-xl max-w-full"\`
- **Reasoning**: Text size needs responsive scaling and max-width to prevent overflow on mobile
```
---
**Remember**: Focus on bugs that impact **usability** and **accessibility**. Not every minor imperfection is critical. Prioritize issues that prevent users from completing tasks or accessing content.

View File

@@ -0,0 +1,415 @@
# Error Pattern Recovery Database
# Maps common error patterns to diagnosis and recovery steps
# This file is used for:
# 1. Pre-flight health checks - detect errors before running full test suite
# 2. Test failure analysis - provide actionable fixes when tests fail
# 3. User guidance - self-service troubleshooting
# ========== CSS & STYLING ERRORS ==========
css_errors:
tailwind_v4_syntax_mismatch:
pattern: "Cannot apply unknown utility class"
alternative_patterns:
- "Utilities must be known at build time"
- "Unknown utility class"
diagnosis: "Tailwind CSS v4 detected but v3 syntax used in CSS file"
severity: "critical"
category: "configuration"
root_cause: |
Tailwind CSS v4 changed from @tailwind directives to @import syntax.
Your CSS file likely still uses the old @tailwind directives.
detection_method: "console_error"
recovery_steps:
- step: "Identify your main CSS file"
details: "Usually src/index.css, src/App.css, or src/globals.css"
- step: "Update CSS directives"
from: |
@tailwind base;
@tailwind components;
@tailwind utilities;
to: |
@import "tailwindcss";
files_to_check:
- "src/index.css"
- "src/App.css"
- "src/globals.css"
- "src/styles/globals.css"
- step: "Update PostCSS configuration"
from: |
plugins: {
tailwindcss: {},
autoprefixer: {},
}
to: |
plugins: {
'@tailwindcss/postcss': {},
autoprefixer: {},
}
files_to_check:
- "postcss.config.js"
- "postcss.config.cjs"
- "postcss.config.mjs"
- step: "Restart dev server"
command: "npm run dev"
reason: "CSS changes require server restart"
- step: "Clear browser cache and reload"
details: "Hard refresh: Ctrl+Shift+R (Windows) or Cmd+Shift+R (Mac)"
prevention: |
The skill now detects Tailwind version and uses appropriate templates.
This error should not occur in new setups.
documentation: "https://tailwindcss.com/docs/upgrade-guide"
related_errors:
- "postcss_plugin_not_found"
postcss_plugin_not_found:
pattern: "Plugin tailwindcss not found"
alternative_patterns:
- "Cannot find module 'tailwindcss'"
- "postcss plugin tailwindcss not found"
diagnosis: "PostCSS configuration uses old Tailwind v3 plugin name with Tailwind v4"
severity: "critical"
category: "configuration"
root_cause: |
Tailwind CSS v4 renamed its PostCSS plugin from 'tailwindcss' to '@tailwindcss/postcss'.
Your postcss.config.js still references the old plugin name.
recovery_steps:
- step: "Update postcss.config.js"
from: |
export default {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
};
to: |
export default {
plugins: {
'@tailwindcss/postcss': {},
autoprefixer: {},
},
};
file: "postcss.config.js"
- step: "Verify @tailwindcss/postcss is installed"
command: "npm list @tailwindcss/postcss"
if_not_installed: "npm install -D @tailwindcss/postcss"
- step: "Restart dev server"
command: "npm run dev"
documentation: "https://tailwindcss.com/docs/upgrade-guide#migrating-from-v3"
# ========== ACCESSIBILITY ERRORS ==========
accessibility_errors:
heading_hierarchy_violation:
pattern: "heading-order - Heading levels should only increase by one"
alternative_patterns:
- "Heading levels should increase by one"
- "heading-order violation"
diagnosis: "WCAG heading hierarchy violation - skipped heading levels"
severity: "moderate"
category: "accessibility"
root_cause: |
HTML heading elements (h1-h6) must follow logical order without skipping levels.
For example: h1 → h2 → h3 is correct, but h1 → h3 (skipping h2) is incorrect.
recovery_steps:
- step: "Locate the problematic heading in test output"
details: "Playwright accessibility tests will show file and line number"
- step: "Check heading hierarchy in that component"
example: |
❌ Bad:
<h1>Page Title</h1>
<h3>Section</h3> <!-- Skips h2 -->
✅ Good:
<h1>Page Title</h1>
<h2>Section</h2>
- step: "Fix heading levels to follow order"
details: "Ensure each heading is only one level deeper than its parent"
- step: "Re-run accessibility tests"
command: "npm run test:e2e -- accessibility.spec.ts"
prevention: |
Always outline content structure before implementing:
- Page title: h1 (only one per page)
- Main sections: h2
- Subsections: h3
- Sub-subsections: h4
documentation: "https://www.w3.org/WAI/WCAG21/Understanding/info-and-relationships.html"
missing_form_labels:
pattern: "Form elements must have labels"
alternative_patterns:
- "label - Form elements must have labels"
- "Inputs must have associated labels"
diagnosis: "Form input missing associated label element"
severity: "high"
category: "accessibility"
root_cause: |
Every form input must have an associated <label> element for screen reader users.
Placeholder text alone is NOT sufficient.
recovery_steps:
- step: "Add label element to input"
from: |
<input type="email" placeholder="Email" />
to: |
<label htmlFor="email">Email Address</label>
<input id="email" type="email" placeholder="Email" />
- step: "Alternative: Use aria-label if visual label not desired"
example: |
<input
type="search"
aria-label="Search"
placeholder="Search..."
/>
- step: "Ensure label and input are properly associated"
details: "Use 'htmlFor' attribute matching input 'id'"
- step: "Re-run accessibility tests"
command: "npm run test:e2e -- accessibility.spec.ts"
documentation: "https://www.w3.org/WAI/tutorials/forms/labels/"
insufficient_color_contrast:
pattern: "color-contrast - Elements must meet minimum color contrast ratio"
alternative_patterns:
- "color contrast ratio"
- "contrast ratio"
diagnosis: "Text or UI element has insufficient color contrast (WCAG 2.1 AA violation)"
severity: "high"
category: "accessibility"
root_cause: |
WCAG 2.1 AA requires:
- Normal text: 4.5:1 contrast ratio
- Large text (18pt+ or 14pt+ bold): 3:1 contrast ratio
- UI components: 3:1 contrast ratio
recovery_steps:
- step: "Identify low-contrast element from test output"
details: "Test results show which element and current ratio"
- step: "Use WebAIM contrast checker"
url: "https://webaim.org/resources/contrastchecker/"
details: "Test current colors and find compliant alternatives"
- step: "Update color values"
example: |
❌ Bad: #AAAAAA on #FFFFFF (2.6:1 ratio)
✅ Good: #595959 on #FFFFFF (7:1 ratio)
For Tailwind:
❌ text-gray-400 → ✅ text-gray-700
- step: "Re-run accessibility tests"
command: "npm run test:e2e -- accessibility.spec.ts"
prevention: |
Use established color palettes with pre-tested contrast ratios.
Tailwind's default palette (500+ for text) generally meets WCAG AA.
documentation: "https://www.w3.org/WAI/WCAG21/Understanding/contrast-minimum.html"
# ========== BUILD & CONFIGURATION ERRORS ==========
build_errors:
vite_port_in_use:
pattern: "Port 5173 is in use"
alternative_patterns:
- "EADDRINUSE"
- "address already in use"
diagnosis: "Vite dev server port already in use by another process"
severity: "moderate"
category: "environment"
recovery_steps:
- step: "Find process using port 5173"
command: "lsof -i :5173"
platforms: ["mac", "linux"]
- step: "Kill the process"
command: "kill -9 <PID>"
details: "Replace <PID> with process ID from previous command"
- step: "Alternative: Use different port"
details: "Add to vite.config.ts:"
config: |
server: {
port: 3000
}
- step: "Restart dev server"
command: "npm run dev"
playwright_browsers_not_installed:
pattern: "Executable doesn't exist"
alternative_patterns:
- "browserType.launch: Executable doesn't exist"
- "Browser not found"
diagnosis: "Playwright browser binaries not installed"
severity: "critical"
category: "installation"
recovery_steps:
- step: "Install Playwright browsers"
command: "npx playwright install"
details: "Downloads Chromium, Firefox, and WebKit"
- step: "Install system dependencies (Linux only)"
command: "npx playwright install-deps"
platforms: ["linux"]
- step: "Verify installation"
command: "npx playwright test --list"
expected: "Should list available tests without errors"
typescript_strict_errors:
pattern: "TypeScript strict mode errors"
alternative_patterns:
- "TS2345"
- "TS2322"
- "Type 'any' is not assignable"
diagnosis: "TypeScript strict mode violations in generated code"
severity: "moderate"
category: "type_safety"
recovery_steps:
- step: "Review TypeScript errors in output"
details: "Each error shows file, line, and type issue"
- step: "Add explicit type annotations"
example: |
❌ const data = await fetch(url);
✅ const data: Response = await fetch(url);
- step: "Use type assertions cautiously"
example: |
const element = page.locator('#id') as Locator;
- step: "Fix null/undefined handling"
example: |
❌ const text = element.textContent();
✅ const text = await element.textContent() ?? '';
# ========== RESPONSIVE & LAYOUT ERRORS ==========
layout_errors:
horizontal_scroll_mobile:
pattern: "Horizontal scroll detected on mobile viewport"
diagnosis: "Content wider than viewport on mobile, causing horizontal scroll"
severity: "moderate"
category: "responsive"
root_cause: |
Fixed-width elements or overflow content exceeds mobile viewport width.
Common causes: large images, wide tables, fixed px widths.
recovery_steps:
- step: "Identify overflowing element"
details: "Use browser DevTools → Elements → Computed → scroll width"
- step: "Make element responsive"
example: |
❌ width: 800px;
✅ max-width: 100%;
❌ className="w-[800px]"
✅ className="w-full max-w-screen-lg"
- step: "Add overflow handling"
example: |
className="overflow-x-auto" // For tables
className="overflow-hidden" // For containers
- step: "Test on mobile viewport"
command: "npm run test:e2e -- --project=mobile-chrome"
# ========== RECOVERY STRATEGIES ==========
recovery_strategies:
incremental_fix:
description: "Fix errors one at a time, test after each fix"
when_to_use: "Multiple related errors"
steps:
- "Fix highest severity error first"
- "Run tests to verify fix"
- "Move to next error"
- "Repeat until all errors resolved"
clean_slate:
description: "Remove generated files and regenerate"
when_to_use: "Configuration completely broken"
steps:
- "Delete generated playwright.config.ts"
- "Delete tests/ directory"
- "Clear node_modules/.cache/"
- "Re-run skill"
version_rollback:
description: "Downgrade to last working version"
when_to_use: "Breaking change in newly installed package"
steps:
- "Check package.json for recently updated packages"
- "Install previous version: npm install package@previous-version"
- "Lock version in package.json"
- "Test to confirm working"
# ========== ERROR SEVERITY LEVELS ==========
severity_definitions:
critical:
impact: "App won't build or run"
response_time: "Fix immediately"
examples: ["Syntax errors", "Missing dependencies", "Config errors"]
high:
impact: "Major functionality broken"
response_time: "Fix within hours"
examples: ["Accessibility violations", "Failed test cases", "Type errors"]
moderate:
impact: "Reduced usability or performance"
response_time: "Fix within days"
examples: ["Layout issues", "Minor accessibility", "Warnings"]
low:
impact: "Cosmetic or minor issues"
response_time: "Fix when convenient"
examples: ["Code style", "Documentation", "Minor visual bugs"]

View File

@@ -0,0 +1,430 @@
# Framework Detection Patterns
# Used to automatically identify application type and determine optimal Playwright configuration
# Detection happens in phases:
# 1. Read package.json dependencies
# 2. Check for config files
# 3. Identify dev server command
# 4. Determine base URL and port
frameworks:
# ========== REACT + VITE ==========
react_vite:
name: "React + Vite"
priority: 1 # Check first (most common modern setup)
detection:
package_json_dependencies:
required:
- "react"
- "vite"
optional:
- "react-dom"
- "@vitejs/plugin-react"
config_files:
- "vite.config.ts"
- "vite.config.js"
- "vite.config.mjs"
indicators:
- "src/main.tsx exists"
- "src/main.jsx exists"
- "index.html with Vite script tag"
configuration:
base_url: "http://localhost:5173"
dev_server_command: "npm run dev"
build_command: "npm run build"
preview_command: "npm run preview"
playwright_config:
timeout: 30000
retries: 2
use:
viewport: { width: 1280, height: 720 }
screenshot: "only-on-failure"
video: "retain-on-failure"
projects:
- name: "chromium"
use:
browserName: "chromium"
- name: "mobile"
use:
...devices["iPhone 13"]
webServer:
command: "npm run dev"
url: "http://localhost:5173"
reuseExistingServer: true
timeout: 120000
test_generation:
entry_point: "src/App.tsx"
routing: "react-router-dom" # If detected in dependencies
state_management: ["redux", "zustand", "jotai"] # Check for these
common_pages:
- "/"
- "/about"
- "/contact"
- "/login"
- "/dashboard"
# ========== NEXT.JS ==========
nextjs:
name: "Next.js"
priority: 2
detection:
package_json_dependencies:
required:
- "next"
- "react"
optional:
- "react-dom"
config_files:
- "next.config.js"
- "next.config.mjs"
- "next.config.ts"
indicators:
- "app/ directory exists" # App Router
- "pages/ directory exists" # Pages Router
- ".next/ directory exists"
configuration:
base_url: "http://localhost:3000"
dev_server_command: "npm run dev"
build_command: "npm run build"
playwright_config:
timeout: 45000 # Next.js can be slower on first load
retries: 2
webServer:
command: "npm run dev"
url: "http://localhost:3000"
reuseExistingServer: true
timeout: 120000
test_generation:
router_type: "app" # or "pages" - detect from directory structure
entry_point: "app/page.tsx" # App Router
api_routes: "app/api/" # Check if API routes exist
common_pages:
- "/"
- "/about"
- "/blog"
- "/contact"
# ========== CREATE REACT APP ==========
create_react_app:
name: "Create React App"
priority: 3
detection:
package_json_dependencies:
required:
- "react"
- "react-scripts"
optional:
- "react-dom"
config_files:
- "public/index.html"
indicators:
- "src/index.js exists"
- "src/index.tsx exists"
- "package.json scripts.start includes react-scripts"
configuration:
base_url: "http://localhost:3000"
dev_server_command: "npm start"
build_command: "npm run build"
playwright_config:
timeout: 30000
retries: 2
webServer:
command: "npm start"
url: "http://localhost:3000"
reuseExistingServer: true
timeout: 120000
test_generation:
entry_point: "src/App.js"
routing: "react-router-dom"
# ========== NODE.JS + EXPRESS ==========
express:
name: "Node.js + Express"
priority: 4
detection:
package_json_dependencies:
required:
- "express"
optional:
- "ejs"
- "pug"
- "handlebars"
indicators:
- "app.js exists"
- "server.js exists"
- "index.js exists"
- "views/ directory exists"
configuration:
base_url: "http://localhost:3000" # Default, check process.env.PORT
dev_server_command: "npm run dev"
alternative_commands:
- "npm start"
- "node server.js"
- "nodemon server.js"
playwright_config:
timeout: 30000
retries: 2
webServer:
command: "npm run dev"
url: "http://localhost:3000"
reuseExistingServer: true
test_generation:
entry_point: "server.js"
template_engine: "ejs" # Detect from dependencies
api_endpoints: true # Generate API tests
common_routes:
- "/"
- "/api/health"
- "/api/users"
# ========== STATIC HTML/CSS/JS ==========
static:
name: "Static HTML/CSS/JS"
priority: 10 # Check last (fallback)
detection:
indicators:
- "index.html exists in root"
- "No package.json"
- "No build tools detected"
configuration:
base_url: "http://localhost:8080"
dev_server_command: "npx serve ."
install_dev_server: "npm install -g serve" # Install if needed
playwright_config:
timeout: 15000 # Faster, no build step
retries: 1
webServer:
command: "npx serve . -l 8080"
url: "http://localhost:8080"
reuseExistingServer: true
test_generation:
entry_point: "index.html"
detect_pages_from:
- "HTML files in root"
- "Links in index.html"
# ========== ASTRO ==========
astro:
name: "Astro"
priority: 5
detection:
package_json_dependencies:
required:
- "astro"
config_files:
- "astro.config.mjs"
indicators:
- "src/pages/ directory exists"
configuration:
base_url: "http://localhost:4321"
dev_server_command: "npm run dev"
playwright_config:
webServer:
command: "npm run dev"
url: "http://localhost:4321"
reuseExistingServer: true
# ========== SVELTE + VITE ==========
svelte_vite:
name: "Svelte + Vite"
priority: 6
detection:
package_json_dependencies:
required:
- "svelte"
- "vite"
optional:
- "@sveltejs/vite-plugin-svelte"
config_files:
- "svelte.config.js"
- "vite.config.js"
configuration:
base_url: "http://localhost:5173"
dev_server_command: "npm run dev"
# ========== VUE + VITE ==========
vue_vite:
name: "Vue + Vite"
priority: 7
detection:
package_json_dependencies:
required:
- "vue"
- "vite"
optional:
- "@vitejs/plugin-vue"
config_files:
- "vite.config.ts"
- "vite.config.js"
configuration:
base_url: "http://localhost:5173"
dev_server_command: "npm run dev"
# ========== NUXT ==========
nuxt:
name: "Nuxt"
priority: 8
detection:
package_json_dependencies:
required:
- "nuxt"
config_files:
- "nuxt.config.ts"
- "nuxt.config.js"
configuration:
base_url: "http://localhost:3000"
dev_server_command: "npm run dev"
# ========== DETECTION WORKFLOW ==========
detection_workflow:
steps:
- name: "Check for package.json"
action: "Read package.json if exists"
output: "dependencies and devDependencies lists"
- name: "Match framework patterns"
action: "Compare dependencies against framework patterns"
priority: "Use priority field to determine order"
output: "List of matching frameworks (highest priority first)"
- name: "Verify with config files"
action: "Check if expected config files exist"
output: "Confirm framework match"
- name: "Check additional indicators"
action: "Verify directory structure and entry points"
output: "Final framework identification"
- name: "Determine dev server"
action: "Read package.json scripts section"
fallback: "Use framework's default dev_server_command"
output: "Command to start dev server"
- name: "Detect port"
action: "Check if server is already running"
fallback: "Use framework's default base_url port"
output: "Base URL for tests"
# ========== PORT DETECTION ==========
port_detection:
methods:
- name: "Check running processes"
command: "lsof -i :3000 -i :5173 -i :8080 -i :4321"
description: "See if common ports are in use"
- name: "Check package.json scripts"
pattern: "--port (\\d+)"
description: "Extract port from dev script"
- name: "Check config files"
files:
- "vite.config.ts": "server.port"
- "next.config.js": "devServer.port"
- ".env": "PORT"
- name: "Attempt connection"
ports: [3000, 5173, 8080, 4321, 8000, 4200]
description: "Try common ports in order"
# ========== MULTI-APP DETECTION ==========
fullstack_detection:
patterns:
monorepo:
indicators:
- "package.json with workspaces field"
- "packages/ or apps/ directory"
- "Lerna or Nx configuration"
strategy:
- "Detect each package separately"
- "Generate tests for each app"
- "Configure multiple web servers if needed"
separate_frontend_backend:
indicators:
- "client/ and server/ directories"
- "frontend/ and backend/ directories"
- "Multiple package.json files"
strategy:
- "Detect frontend framework"
- "Detect backend framework"
- "Start both servers for e2e tests"
- "Configure API proxy if needed"
# ========== FALLBACK STRATEGY ==========
fallback:
when_no_match:
- "Prompt user to specify framework manually"
- "List detected files and ask for clarification"
- "Suggest generic static server approach"
generic_config:
base_url: "http://localhost:8080"
dev_server: "npx serve . -l 8080"
timeout: 30000
# ========== VALIDATION ==========
validation:
checks:
- name: "Server starts successfully"
test: "Run dev server command, wait for port to open"
timeout: 120000
failure: "Ask user to start server manually"
- name: "Base URL accessible"
test: "HTTP GET to base_url returns 200"
timeout: 30000
failure: "Check if port is different, ask user"
- name: "Page renders content"
test: "Page has visible content (not blank)"
timeout: 10000
failure: "Possible SPA routing issue, check entry point"

View File

@@ -0,0 +1,303 @@
# Framework Version Compatibility Database
# Used for version-aware template selection and configuration generation
# This file maps framework versions to their syntax, configuration requirements,
# and breaking changes. When the skill detects installed package versions, it
# consults this database to select appropriate templates and warn about issues.
# ========== TAILWIND CSS ==========
tailwindcss:
v3:
version_range: ">=3.0.0 <4.0.0"
detection_priority: 2
syntax:
css_directives:
- "@tailwind base;"
- "@tailwind components;"
- "@tailwind utilities;"
postcss_plugin: "tailwindcss"
config_file: "tailwind.config.js"
templates:
css: "templates/css/tailwind-v3.css"
postcss_config: "templates/configs/postcss-tailwind-v3.js"
tailwind_config: "templates/configs/tailwind-v3.config.js"
notes: "Stable version with @tailwind directive syntax"
v4:
version_range: ">=4.0.0"
detection_priority: 1 # Check first (latest)
syntax:
css_directives:
- "@import \"tailwindcss\";"
postcss_plugin: "@tailwindcss/postcss"
config_file: "tailwind.config.ts" # Now TypeScript by default
templates:
css: "templates/css/tailwind-v4.css"
postcss_config: "templates/configs/postcss-tailwind-v4.js"
tailwind_config: "templates/configs/tailwind-v4.config.ts"
breaking_changes:
- "CSS syntax changed from @tailwind directives to @import"
- "PostCSS plugin renamed from 'tailwindcss' to '@tailwindcss/postcss'"
- "Configuration file now TypeScript by default"
- "Some utility classes restructured (check migration guide)"
migration_guide: "https://tailwindcss.com/docs/upgrade-guide"
notes: "Major rewrite with new @import syntax and improved PostCSS integration"
# ========== REACT ==========
react:
v17:
version_range: ">=17.0.0 <18.0.0"
detection_priority: 2
features:
jsx_transform: "classic" # Requires React import
concurrent_features: false
automatic_batching: false
templates:
component: "templates/react/component-v17.tsx"
notes: "Classic JSX transform, requires 'import React from react'"
v18:
version_range: ">=18.0.0 <19.0.0"
detection_priority: 1
features:
jsx_transform: "automatic" # No React import needed
concurrent_features: true
automatic_batching: true
use_client_directive: false # Not yet (that's React 19)
templates:
component: "templates/react/component-v18.tsx"
breaking_changes:
- "Automatic batching may affect state update timing"
- "Concurrent features require opt-in (via createRoot)"
- "IE11 no longer supported"
migration_guide: "https://react.dev/blog/2022/03/08/react-18-upgrade-guide"
notes: "New JSX transform, concurrent features, automatic batching"
v19:
version_range: ">=19.0.0"
detection_priority: 1
features:
jsx_transform: "automatic"
concurrent_features: true
automatic_batching: true
use_client_directive: true # Server Components
actions: true # Server Actions
templates:
component: "templates/react/component-v19.tsx"
breaking_changes:
- "'use client' directive required for client components in RSC apps"
- "ref is now a regular prop (no forwardRef needed)"
- "Context.Provider shorthand removed (use <Context> directly)"
migration_guide: "https://react.dev/blog/2024/04/25/react-19-upgrade-guide"
notes: "Server Components, Actions, ref as prop, improved performance"
# ========== NEXT.JS ==========
nextjs:
v13:
version_range: ">=13.0.0 <14.0.0"
detection_priority: 2
features:
app_router: true # Optional
pages_router: true
turbopack: "beta"
server_actions: "alpha"
router_detection:
app_router: "app/ directory exists"
pages_router: "pages/ directory exists"
templates:
config: "templates/nextjs/next-v13.config.js"
page_app: "templates/nextjs/page-v13-app.tsx"
page_pages: "templates/nextjs/page-v13-pages.tsx"
notes: "App Router introduced alongside Pages Router"
v14:
version_range: ">=14.0.0 <15.0.0"
detection_priority: 1
features:
app_router: true
pages_router: true
turbopack: "stable"
server_actions: "stable"
partial_prerendering: "experimental"
router_detection:
app_router: "app/ directory exists"
pages_router: "pages/ directory exists"
templates:
config: "templates/nextjs/next-v14.config.js"
page_app: "templates/nextjs/page-v14-app.tsx"
page_pages: "templates/nextjs/page-v14-pages.tsx"
breaking_changes:
- "Server Actions stable (syntax changes from v13)"
- "Turbopack stable for dev (replaces webpack in dev mode)"
- "Minimum Node.js version: 18.17"
migration_guide: "https://nextjs.org/docs/app/building-your-application/upgrading/version-14"
notes: "Stable Server Actions and Turbopack"
# ========== VITE ==========
vite:
v4:
version_range: ">=4.0.0 <5.0.0"
detection_priority: 2
features:
default_port: 5173
rollup_version: 3
css_code_split: true
templates:
config: "templates/vite/vite-v4.config.ts"
notes: "Stable Vite 4 with Rollup 3"
v5:
version_range: ">=5.0.0 <6.0.0"
detection_priority: 1
features:
default_port: 5173
rollup_version: 4
css_code_split: true
improved_hmr: true
templates:
config: "templates/vite/vite-v5.config.ts"
breaking_changes:
- "Rollup 4 (plugin compatibility check needed)"
- "Minimum Node.js version: 18.0"
- "Some deprecated options removed"
migration_guide: "https://vitejs.dev/guide/migration"
notes: "Vite 5 with Rollup 4 and improved HMR"
# ========== PLAYWRIGHT ==========
playwright:
v1_40_plus:
version_range: ">=1.40.0"
detection_priority: 1
features:
component_testing: true
trace_viewer: true
codegen: true
auto_waiting: true
templates:
config: "templates/playwright.config.template.ts"
test_spec: "templates/test-spec.template.ts"
page_object: "templates/page-object.template.ts"
notes: "Modern Playwright with full feature set"
# ========== POSTCSS ==========
postcss:
v8:
version_range: ">=8.0.0"
detection_priority: 1
notes: "Standard PostCSS v8 - most common version"
# ========== VERSION DETECTION STRATEGIES ==========
detection_strategies:
priority_order:
description: "Check versions in priority order (higher priority first)"
example: "For Tailwind: check v4 rules before v3 rules"
semver_matching:
description: "Use semver.satisfies() for version range matching"
library: "semver npm package"
fallback:
description: "If no version matches, warn and use sensible defaults"
default_strategy: "Use latest stable templates with warning"
multi_framework:
description: "Detect multiple frameworks simultaneously"
example: "React 18 + Vite 5 + Tailwind 4 + Playwright 1.40"
# ========== COMMON BREAKING CHANGE PATTERNS ==========
breaking_change_categories:
syntax_changes:
examples:
- "Tailwind v3→v4: @tailwind → @import"
- "React v17→v18: Optional JSX import"
impact: "Critical - app won't build/run"
detection: "Parse error, syntax error in build output"
configuration_changes:
examples:
- "Tailwind v4: PostCSS plugin rename"
- "Next.js v14: next.config.js options"
impact: "High - build fails or misconfigured"
detection: "Build error, plugin not found"
api_changes:
examples:
- "React v19: ref as prop"
- "Next.js v13: getServerSideProps in App Router"
impact: "Medium - runtime errors or warnings"
detection: "TypeScript errors, runtime warnings"
deprecation_warnings:
examples:
- "React v18: ReactDOM.render → createRoot"
- "Vite: Legacy options removed"
impact: "Low - works but will break in future"
detection: "Console warnings, deprecation notices"
# ========== USAGE NOTES ==========
usage:
detection_flow:
- "Read package.json dependencies and devDependencies"
- "For each framework, iterate through versions in priority order"
- "Use semver.satisfies(installedVersion, versionRange)"
- "First match wins (highest priority)"
- "If no match, use fallback and warn"
template_selection:
- "Based on detected version, select appropriate template files"
- "Templates use version-specific syntax and best practices"
- "Combine multiple framework templates (e.g., React + Tailwind + Vite)"
error_prevention:
- "Check for known breaking changes before generating config"
- "Warn user if mixing incompatible versions"
- "Provide migration guides for major version differences"
maintenance:
- "Add new versions as they're released"
- "Update breaking_changes based on real-world issues"
- "Keep migration_guide links current"
- "Test template compatibility regularly"

View File

@@ -0,0 +1,456 @@
# Playwright Best Practices
Official best practices for Playwright test automation, optimized for LLM-assisted development.
## Test Structure
### Use Page Object Models (POM)
**Why**: Separates page structure from test logic, improves maintainability
```typescript
// Good: Page Object Model
// pages/login.page.ts
export class LoginPage {
constructor(private page: Page) {}
async goto() {
await this.page.goto('/login');
}
async login(username: string, password: string) {
await this.page.getByLabel('Username').fill(username);
await this.page.getByLabel('Password').fill(password);
await this.page.getByRole('button', { name: 'Sign in' }).click();
}
}
// specs/login.spec.ts
test('user can login', async ({ page }) => {
const loginPage = new LoginPage(page);
await loginPage.goto();
await loginPage.login('user@example.com', 'password123');
await expect(page).toHaveURL('/dashboard');
});
```
### Use Semantic Selectors
**Priority order** (most stable → least stable):
1. **getByRole** - Accessible role (button, heading, textbox, etc.)
2. **getByLabel** - Form inputs with associated labels
3. **getByPlaceholder** - Input placeholder text
4. **getByText** - User-visible text content
5. **getByTestId** - data-testid attributes (last resort)
```typescript
// Best: Role-based (accessible and stable)
await page.getByRole('button', { name: 'Submit' }).click();
// Good: Label-based (for forms)
await page.getByLabel('Email address').fill('user@example.com');
// Acceptable: Text-based
await page.getByText('Continue to checkout').click();
// Avoid: CSS selectors (brittle)
await page.click('.btn-primary'); // ❌ Breaks if class changes
// Last resort: Test IDs (when semantic selectors don't work)
await page.getByTestId('checkout-button').click();
```
## Screenshot Best Practices
### When to Capture Screenshots
1. **Initial page load** - Baseline visual state
2. **Before interaction** - Pre-state for comparison
3. **After interaction** - Result of user action
4. **Error states** - When validation fails or errors occur
5. **Success states** - Confirmation screens, success messages
6. **Test failures** - Automatic capture for debugging
### Screenshot Naming Convention
```typescript
// Pattern: {test-name}-{viewport}-{state}-{timestamp}.png
await page.screenshot({
path: `screenshots/current/login-desktop-initial-${Date.now()}.png`,
fullPage: true
});
await page.screenshot({
path: `screenshots/current/checkout-mobile-error-${Date.now()}.png`,
fullPage: true
});
```
### Full-Page vs Element Screenshots
```typescript
// Full-page: For layout and overall UI analysis
await page.screenshot({
path: 'homepage-full.png',
fullPage: true // Captures entire scrollable page
});
// Element-specific: For component testing
const button = page.getByRole('button', { name: 'Submit' });
await button.screenshot({
path: 'submit-button.png'
});
```
## Waiting and Timing
### Auto-Waiting
Playwright automatically waits for:
- Element to be attached to DOM
- Element to be visible
- Element to be stable (not animating)
- Element to receive events (not obscured)
- Element to be enabled
```typescript
// This automatically waits for button to be clickable
await page.getByRole('button', { name: 'Submit' }).click();
```
### Explicit Waits (when needed)
```typescript
// Wait for navigation
await page.waitForURL('/dashboard');
// Wait for network idle (good before screenshots)
await page.waitForLoadState('networkidle');
// Wait for specific element
await page.waitForSelector('img[alt="Profile picture"]');
// Wait for custom condition
await page.waitForFunction(() => window.scrollY === 0);
```
### Avoid Fixed Timeouts
```typescript
// Bad: Arbitrary delays
await page.waitForTimeout(3000); // ❌ Flaky, slow
// Good: Wait for specific condition
await expect(page.getByText('Success')).toBeVisible(); // ✅ Fast and reliable
```
## Test Isolation
### Independent Tests
Each test should be completely independent:
```typescript
// Good: Test is self-contained
test('user can add item to cart', async ({ page }) => {
// Set up: Create user, log in
await page.goto('/');
await login(page, 'user@example.com', 'password');
// Action: Add to cart
await page.getByRole('button', { name: 'Add to cart' }).click();
// Assert: Item in cart
await expect(page.getByTestId('cart-count')).toHaveText('1');
// Cleanup happens automatically with new page context
});
// Bad: Depends on previous test state
test('user can checkout', async ({ page }) => {
// ❌ Assumes cart already has items from previous test
await page.goto('/checkout');
// ...
});
```
### Use test.beforeEach for Common Setup
```typescript
test.describe('Shopping cart', () => {
test.beforeEach(async ({ page }) => {
await page.goto('/');
await login(page, 'user@example.com', 'password');
});
test('can add item to cart', async ({ page }) => {
// Setup already done
await page.getByRole('button', { name: 'Add to cart' }).click();
await expect(page.getByTestId('cart-count')).toHaveText('1');
});
test('can remove item from cart', async ({ page }) => {
// Setup already done, fresh state
await page.getByRole('button', { name: 'Add to cart' }).click();
await page.getByRole('button', { name: 'Remove' }).click();
await expect(page.getByTestId('cart-count')).toHaveText('0');
});
});
```
## Visual Regression Testing
### Snapshot Testing
```typescript
// Basic snapshot
await expect(page).toHaveScreenshot('homepage.png');
// With threshold (allow minor differences)
await expect(page).toHaveScreenshot('homepage.png', {
maxDiffPixelRatio: 0.05 // Allow 5% difference
});
// Element snapshot
const card = page.getByRole('article').first();
await expect(card).toHaveScreenshot('product-card.png');
```
### Updating Baselines
```bash
# Update all snapshots
npx playwright test --update-snapshots
# Update specific test
npx playwright test login.spec.ts --update-snapshots
```
### Baseline Management
- **Store baselines in git** - Commit to repository for consistency
- **Review diffs carefully** - Not all changes are bugs
- **Update deliberately** - Only update when changes are intentional
- **Use CI checks** - Fail pipeline on unexpected visual changes
## Configuration Best Practices
### playwright.config.ts Essentials
```typescript
import { defineConfig, devices } from '@playwright/test';
export default defineConfig({
testDir: './tests',
// Timeout for each test
timeout: 30 * 1000,
// Global setup/teardown
globalSetup: require.resolve('./tests/setup/global-setup.ts'),
// Fail fast on CI, retry locally
forbidOnly: !!process.env.CI,
retries: process.env.CI ? 2 : 0,
// Parallel execution
workers: process.env.CI ? 1 : undefined,
// Reporter
reporter: process.env.CI ? 'github' : 'html',
use: {
// Base URL
baseURL: 'http://localhost:5173',
// Screenshot on failure
screenshot: 'only-on-failure',
// Trace on first retry
trace: 'on-first-retry',
// Video on failure
video: 'retain-on-failure',
},
// Projects for multi-browser testing
projects: [
{
name: 'chromium',
use: { ...devices['Desktop Chrome'] },
},
{
name: 'firefox',
use: { ...devices['Desktop Firefox'] },
},
{
name: 'webkit',
use: { ...devices['Desktop Safari'] },
},
{
name: 'mobile-chrome',
use: { ...devices['Pixel 5'] },
},
{
name: 'mobile-safari',
use: { ...devices['iPhone 13'] },
},
],
// Web server for dev
webServer: {
command: 'npm run dev',
url: 'http://localhost:5173',
reuseExistingServer: !process.env.CI,
},
});
```
## Debugging
### Playwright Inspector
```bash
# Debug specific test
npx playwright test --debug login.spec.ts
# Debug from specific line
npx playwright test --debug --grep "user can login"
```
### VS Code Debugger
```json
// .vscode/launch.json
{
"configurations": [
{
"name": "Debug Playwright Tests",
"type": "node",
"request": "launch",
"program": "${workspaceFolder}/node_modules/@playwright/test/cli.js",
"args": ["test", "--headed", "${file}"],
"console": "integratedTerminal"
}
]
}
```
### Trace Viewer
```bash
# Run with trace
npx playwright test --trace on
# View trace
npx playwright show-trace trace.zip
```
## Performance Optimization
### Parallel Execution
```typescript
// Run tests in parallel (default)
test.describe.configure({ mode: 'parallel' });
// Run tests serially (when needed)
test.describe.configure({ mode: 'serial' });
```
### Reuse Authentication State
```typescript
// global-setup.ts
import { chromium } from '@playwright/test';
export default async function globalSetup() {
const browser = await chromium.launch();
const page = await browser.newPage();
await page.goto('http://localhost:5173/login');
await page.getByLabel('Username').fill('admin');
await page.getByLabel('Password').fill('password');
await page.getByRole('button', { name: 'Sign in' }).click();
// Save authentication state
await page.context().storageState({ path: 'auth.json' });
await browser.close();
}
// Use in tests
test.use({ storageState: 'auth.json' });
```
## Common Pitfalls to Avoid
### 1. Not Waiting for Network Idle Before Screenshots
```typescript
// Bad: Screenshot may capture loading state
await page.goto('/dashboard');
await page.screenshot({ path: 'dashboard.png' });
// Good: Wait for content to load
await page.goto('/dashboard');
await page.waitForLoadState('networkidle');
await page.screenshot({ path: 'dashboard.png' });
```
### 2. Using Non-Stable Selectors
```typescript
// Bad: Position-based (breaks if order changes)
await page.locator('button').nth(2).click();
// Good: Content-based
await page.getByRole('button', { name: 'Submit' }).click();
```
### 3. Not Handling Dynamic Content
```typescript
// Bad: Assumes content is already loaded
const text = await page.getByTestId('user-name').textContent();
// Good: Wait for element first
await expect(page.getByTestId('user-name')).toBeVisible();
const text = await page.getByTestId('user-name').textContent();
```
### 4. Overly Broad Assertions
```typescript
// Bad: Fails on any minor change
await expect(page).toHaveScreenshot({ maxDiffPixelRatio: 0 });
// Good: Allow reasonable tolerance
await expect(page).toHaveScreenshot({ maxDiffPixelRatio: 0.02 });
```
## Summary Checklist
- [ ] Use Page Object Models for test organization
- [ ] Prefer semantic selectors (getByRole, getByLabel)
- [ ] Capture screenshots at key interaction points
- [ ] Wait for network idle before screenshots
- [ ] Use auto-waiting instead of fixed timeouts
- [ ] Make tests independent and isolated
- [ ] Configure proper retry logic (2-3 retries in CI)
- [ ] Store authentication state for reuse
- [ ] Use trace viewer for debugging
- [ ] Review visual diffs before updating baselines
- [ ] Run tests in parallel for performance
- [ ] Enable screenshot/video on failure
- [ ] Store baselines in version control
- [ ] Use meaningful screenshot names with timestamps
- [ ] Configure appropriate visual diff thresholds
---
**References:**
- [Playwright Official Docs](https://playwright.dev/)
- [Best Practices Guide](https://playwright.dev/docs/best-practices)
- [Locators Guide](https://playwright.dev/docs/locators)

View File

@@ -0,0 +1,91 @@
import { Page, Locator } from '@playwright/test';
/**
* HomePage Page Object Model
*
* Example POM for a React + Vite application homepage
* Demonstrates best practices for locator selection
*/
export class HomePage {
readonly page: Page;
// Locators - Using semantic selectors (priority: getByRole > getByLabel > getByText > getByTestId)
readonly welcomeMessage: Locator;
readonly aboutLink: Locator;
readonly contactLink: Locator;
readonly navbar: Locator;
readonly heroSection: Locator;
readonly ctaButton: Locator;
readonly featureCards: Locator;
constructor(page: Page) {
this.page = page;
// Initialize locators with semantic selectors
this.navbar = page.getByRole('navigation');
this.welcomeMessage = page.getByRole('heading', { name: /welcome/i });
this.aboutLink = page.getByRole('link', { name: /about/i });
this.contactLink = page.getByRole('link', { name: /contact/i });
this.heroSection = page.getByRole('banner');
this.ctaButton = page.getByRole('button', { name: /get started/i });
this.featureCards = page.getByRole('article');
}
/**
* Navigate to homepage
*/
async goto() {
await this.page.goto('/');
await this.page.waitForLoadState('networkidle');
}
/**
* Wait for page to be fully loaded and ready
*/
async waitForReady() {
await this.welcomeMessage.waitFor({ state: 'visible' });
await this.navbar.waitFor({ state: 'visible' });
}
/**
* Navigate to About page
*/
async goToAbout() {
await this.aboutLink.click();
await this.page.waitForURL('**/about');
}
/**
* Navigate to Contact page
*/
async goToContact() {
await this.contactLink.click();
await this.page.waitForURL('**/contact');
}
/**
* Click the main CTA button
*/
async clickCTA() {
await this.ctaButton.click();
}
/**
* Get count of feature cards
*/
async getFeatureCardCount(): Promise<number> {
return await this.featureCards.count();
}
/**
* Take screenshot of homepage
*/
async screenshot(name: string = 'homepage') {
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
await this.page.screenshot({
path: `screenshots/current/${name}-${timestamp}.png`,
fullPage: true,
});
}
}

View File

@@ -0,0 +1,149 @@
import { test, expect } from '@playwright/test';
import { HomePage } from '../pages/home.page';
import { captureWithContext } from '../utils/screenshot-helper';
/**
* Example Playwright Test for React + Vite Application
*
* This demonstrates best practices for e2e testing with screenshot capture
*/
test.describe('Homepage', () => {
let homePage: HomePage;
test.beforeEach(async ({ page }) => {
homePage = new HomePage(page);
await homePage.goto();
// Capture initial page load
await captureWithContext(page, 'homepage-initial-load', 'Homepage loaded successfully');
});
test('should display welcome message', async ({ page }) => {
// Arrange: Page is already loaded in beforeEach
// Act: No action needed, just checking initial state
await captureWithContext(page, 'homepage-welcome-check', 'Checking for welcome message');
// Assert: Welcome message is visible
await expect(homePage.welcomeMessage).toBeVisible();
await expect(homePage.welcomeMessage).toContainText('Welcome');
});
test('should navigate to about page when clicking About link', async ({ page }) => {
// Arrange: Page loaded
await captureWithContext(page, 'homepage-before-nav', 'Before clicking About link');
// Act: Click About link
await homePage.aboutLink.click();
// Capture after navigation
await page.waitForURL('**/about');
await captureWithContext(page, 'about-page-loaded', 'About page after navigation');
// Assert: URL changed and about page content visible
expect(page.url()).toContain('/about');
await expect(page.getByRole('heading', { name: 'About' })).toBeVisible();
});
test('should submit contact form successfully', async ({ page }) => {
// Arrange: Navigate to contact page
await page.goto('/contact');
await captureWithContext(page, 'contact-form-initial', 'Contact form initial state');
// Act: Fill out form
await page.getByLabel('Name').fill('John Doe');
await page.getByLabel('Email').fill('john@example.com');
await page.getByLabel('Message').fill('This is a test message');
await captureWithContext(page, 'contact-form-filled', 'Form filled before submission');
await page.getByRole('button', { name: 'Send Message' }).click();
// Wait for success message
await page.waitForSelector('[data-testid="success-message"]', { state: 'visible' });
await captureWithContext(page, 'contact-form-success', 'Success message displayed');
// Assert: Success message appears
await expect(page.getByTestId('success-message')).toBeVisible();
await expect(page.getByTestId('success-message')).toContainText('Message sent successfully');
});
test('should validate required fields', async ({ page }) => {
// Arrange: Navigate to contact page
await page.goto('/contact');
await captureWithContext(page, 'contact-form-validation-init', 'Before validation check');
// Act: Try to submit empty form
await page.getByRole('button', { name: 'Send Message' }).click();
await captureWithContext(page, 'contact-form-validation-errors', 'Validation errors displayed');
// Assert: Error messages appear
await expect(page.getByText('Name is required')).toBeVisible();
await expect(page.getByText('Email is required')).toBeVisible();
await expect(page.getByText('Message is required')).toBeVisible();
});
test('should not have accessibility violations', async ({ page }) => {
const AxeBuilder = (await import('@axe-core/playwright')).default;
const accessibilityScanResults = await new AxeBuilder({ page })
.withTags(['wcag2a', 'wcag2aa', 'wcag21a', 'wcag21aa'])
.analyze();
await captureWithContext(
page,
'homepage-accessibility-check',
`Found ${accessibilityScanResults.violations.length} accessibility violations`
);
// Log violations for review
if (accessibilityScanResults.violations.length > 0) {
console.log('\n⚠ Accessibility Violations:');
accessibilityScanResults.violations.forEach((violation) => {
console.log(`\n- ${violation.id}: ${violation.description}`);
console.log(` Impact: ${violation.impact}`);
console.log(` Nodes: ${violation.nodes.length}`);
});
}
// Fail on critical violations only (for this example)
const criticalViolations = accessibilityScanResults.violations.filter(
(v) => v.impact === 'critical' || v.impact === 'serious'
);
expect(criticalViolations).toEqual([]);
});
test('should display correctly across viewports', async ({ page }) => {
const viewports = [
{ name: 'desktop', width: 1280, height: 720 },
{ name: 'tablet', width: 768, height: 1024 },
{ name: 'mobile', width: 375, height: 667 },
];
for (const viewport of viewports) {
await page.setViewportSize(viewport);
await page.waitForTimeout(500); // Let responsive changes settle
await captureWithContext(
page,
`homepage-responsive-${viewport.name}`,
`${viewport.width}x${viewport.height} viewport`
);
// Verify no horizontal scroll on mobile/tablet
if (viewport.name !== 'desktop') {
const scrollWidth = await page.evaluate(() => document.body.scrollWidth);
const clientWidth = await page.evaluate(() => document.body.clientWidth);
expect(scrollWidth).toBeLessThanOrEqual(clientWidth + 1); // Allow 1px tolerance
}
// Verify main navigation is accessible
const nav = page.getByRole('navigation');
await expect(nav).toBeVisible();
}
});
});

View File

@@ -0,0 +1,592 @@
# Fix Recommendations
**Generated**: 2025-11-01 16:35:22
**Based On**: visual-analysis-report.md
**Issues Addressed**: 8
**Estimated Effort**: 4-6 hours
---
## How to Use This Report
Each fix includes:
- **File location** with line numbers (when identifiable)
- **Current code** showing the problematic implementation
- **Recommended fix** with specific code changes
- **Reasoning** explaining why this fix works
- **Testing steps** to validate the fix
Apply fixes in priority order: Critical → High → Medium → Low
---
## Critical Fixes (Implement Immediately)
### Fix #1: Increase Form Label Contrast
**Issue**: Insufficient color contrast on form labels (2.6:1, requires 4.5:1)
**Location**: `src/components/ContactForm.tsx:45-52`
**Current Code**:
```tsx
<label htmlFor="name" className="block text-gray-400 text-sm mb-1">
Name
</label>
<input
id="name"
type="text"
className="w-full px-4 py-2 border border-gray-300 rounded"
placeholder="Enter your name"
/>
```
**Recommended Fix**:
```tsx
<label htmlFor="name" className="block text-gray-700 text-sm font-medium mb-1">
Name
</label>
<input
id="name"
type="text"
className="w-full px-4 py-2 border border-gray-300 rounded"
placeholder="Enter your name"
aria-required="true"
/>
```
**Changes Made**:
- `text-gray-400``text-gray-700` (changes color from #AAAAAA to #374151)
- Added `font-medium` for improved readability
- Added `aria-required="true"` for accessibility
**Reasoning**:
- `text-gray-700` (#374151) on white (#FFFFFF) = 9.7:1 contrast ratio ✅
- Exceeds WCAG 2.1 AA requirement (4.5:1)
- `font-medium` improves readability without affecting contrast
- `aria-required` helps screen reader users identify required fields
**Testing**:
1. Visual check: Labels should be clearly readable
2. Contrast tool: Verify 9.7:1 ratio at https://webaim.org/resources/contrastchecker/
3. Accessibility audit: Run axe-core, verify no contrast violations
4. Screen reader: Test with NVDA/VoiceOver, verify required field announcement
**Impact**: Fixes critical WCAG 2.1 violation, improves usability for low-vision users
---
### Fix #2: Responsive Button Text Sizing
**Issue**: Button text truncated on mobile (shows "Send Mes...")
**Location**: `src/components/ContactForm.tsx:78`
**Current Code**:
```tsx
<button
type="submit"
className="w-full px-6 py-3 text-xl font-bold bg-blue-600 text-white rounded"
>
Send Message
</button>
```
**Recommended Fix**:
```tsx
<button
type="submit"
className="w-full px-4 py-2 text-sm sm:text-base md:text-lg font-bold bg-blue-600 text-white rounded whitespace-nowrap overflow-visible"
>
Send Message
</button>
```
**Changes Made**:
- `px-6``px-4` (reduced padding to allow more text space)
- `py-3``py-2` (slightly reduced vertical padding)
- `text-xl``text-sm sm:text-base md:text-lg` (responsive text sizing)
- Added `whitespace-nowrap` (prevent text wrapping)
- Added `overflow-visible` (ensure text isn't hidden)
**Reasoning**:
- Mobile (375px): 14px font (text-sm) fits comfortably
- Tablet (768px): 16px font (text-base) for better readability
- Desktop (1280px): 18px font (text-lg) for prominence
- Reduced padding provides more space for text
- `whitespace-nowrap` prevents awkward line breaks
**Testing**:
1. Mobile (375px viewport): Verify full text "Send Message" visible
2. Tablet (768px): Check font size scales appropriately
3. Desktop (1280px): Ensure button looks proportional
4. Accessibility: Verify button is tappable (min 44x44px)
**Impact**: Fixes broken user experience on mobile, ensures button purpose is clear
---
## High Priority Fixes
### Fix #3: Prevent Navigation Overlap on Tablet
**Issue**: Nav items overlap on tablet breakpoint (768px)
**Location**: `src/components/Header.tsx:32-45`
**Current Code**:
```tsx
<nav className="flex space-x-6">
<a href="/" className="text-gray-700 hover:text-blue-600">
Home
</a>
<a href="/about" className="text-gray-700 hover:text-blue-600">
About
</a>
<a href="/contact" className="text-gray-700 hover:text-blue-600">
Contact
</a>
<a href="/blog" className="text-gray-700 hover:text-blue-600">
Blog
</a>
</nav>
```
**Recommended Fix**:
```tsx
<nav className="flex flex-col md:flex-row md:space-x-6 space-y-2 md:space-y-0">
<a href="/" className="text-gray-700 hover:text-blue-600 py-2 md:py-0">
Home
</a>
<a href="/about" className="text-gray-700 hover:text-blue-600 py-2 md:py-0">
About
</a>
<a href="/contact" className="text-gray-700 hover:text-blue-600 py-2 md:py-0">
Contact
</a>
<a href="/blog" className="text-gray-700 hover:text-blue-600 py-2 md:py-0">
Blog
</a>
</nav>
```
**Alternative Fix** (if horizontal menu required):
```tsx
<nav className="flex space-x-3 md:space-x-6 text-sm md:text-base">
<a href="/" className="text-gray-700 hover:text-blue-600 whitespace-nowrap">
Home
</a>
<a href="/about" className="text-gray-700 hover:text-blue-600 whitespace-nowrap">
About
</a>
<a href="/contact" className="text-gray-700 hover:text-blue-600 whitespace-nowrap">
Contact
</a>
<a href="/blog" className="text-gray-700 hover:text-blue-600 whitespace-nowrap">
Blog
</a>
</nav>
```
**Reasoning**:
- **Option 1**: Stack links vertically on tablet/mobile, horizontal on desktop
- More reliable, works with any link text length
- Better for mobile usability
- **Option 2**: Reduce spacing and font size on smaller screens
- Maintains horizontal layout
- Risk: May still overflow with longer link text
**Recommendation**: Use Option 1 for reliability
**Testing**:
1. Tablet (768px): Verify links stack vertically or have adequate spacing
2. Desktop (1024px+): Verify links display horizontally
3. Check all breakpoints: 640px, 768px, 1024px, 1280px
4. Test with longer link text (e.g., "Our Services" instead of "Blog")
**Impact**: Fixes navigation usability on tablet devices
---
### Fix #4: Prevent Hero Image Distortion
**Issue**: Hero background image stretched on mobile viewport
**Location**: `src/components/Hero.tsx:15-25`
**Current Code**:
```tsx
<div
className="hero-section h-96 bg-cover bg-center"
style={{
backgroundImage: "url('/images/hero-bg.jpg')",
}}
>
<div className="container mx-auto h-full flex items-center">
<h1 className="text-4xl font-bold text-white">Welcome to Our Site</h1>
</div>
</div>
```
**Recommended Fix**:
```tsx
<div className="hero-section h-96 relative overflow-hidden">
<img
src="/images/hero-bg.jpg"
alt=""
className="absolute inset-0 w-full h-full object-cover object-center"
aria-hidden="true"
/>
<div className="container mx-auto h-full flex items-center relative z-10">
<h1 className="text-4xl font-bold text-white drop-shadow-lg">
Welcome to Our Site
</h1>
</div>
</div>
```
**Changes Made**:
- Replaced CSS background image with `<img>` tag
- Added `object-cover` to maintain aspect ratio while filling container
- Added `object-center` for centered focal point
- Made container `relative` with image `absolute` for layering
- Added `drop-shadow-lg` to h1 for better text visibility
- Added `aria-hidden="true"` since image is decorative
**Reasoning**:
- `object-cover` scales image proportionally to fill container
- Crops excess rather than stretching to fit
- Maintains image quality and recognizability
- Works consistently across all viewport sizes
**Testing**:
1. Mobile (375px): Verify image not stretched, focal point visible
2. Tablet (768px): Check image scales appropriately
3. Desktop (1280px): Ensure full image coverage
4. Test with different aspect ratio images (16:9, 4:3, 1:1)
**Impact**: Professional appearance maintained across all devices
---
### Fix #5: Add Visible Error Messages
**Issue**: Form validation errors indicated only by red border (no text)
**Location**: `src/components/ContactForm.tsx:55-95`
**Current Code**:
```tsx
<input
id="email"
type="email"
className={`w-full px-4 py-2 border rounded ${
errors.email ? 'border-red-500' : 'border-gray-300'
}`}
/>
```
**Recommended Fix**:
```tsx
<div className="mb-4">
<label htmlFor="email" className="block text-gray-700 text-sm font-medium mb-1">
Email Address <span className="text-red-600" aria-label="required">*</span>
</label>
<input
id="email"
type="email"
className={`w-full px-4 py-2 border rounded ${
errors.email
? 'border-red-500 focus:ring-red-500'
: 'border-gray-300 focus:ring-blue-500'
}`}
aria-invalid={errors.email ? 'true' : 'false'}
aria-describedby={errors.email ? 'email-error' : undefined}
/>
{errors.email && (
<div
id="email-error"
className="mt-1 text-sm text-red-600 flex items-center"
role="alert"
>
<svg
className="w-4 h-4 mr-1 flex-shrink-0"
fill="currentColor"
viewBox="0 0 20 20"
aria-hidden="true"
>
<path
fillRule="evenodd"
d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z"
clipRule="evenodd"
/>
</svg>
{errors.email}
</div>
)}
</div>
```
**Changes Made**:
- Added required indicator (*) with `aria-label`
- Added `aria-invalid` attribute for screen readers
- Added `aria-describedby` linking to error message
- Added visible error message below input
- Added error icon for visual reinforcement
- Added `role="alert"` to announce errors to screen readers
**Reasoning**:
- Error text provides specific guidance (not just "there's an error")
- Icon + color + text = multiple indicators (not color alone)
- ARIA attributes ensure screen reader compatibility
- Error message ID allows programmatic association with input
**Testing**:
1. Visual: Submit empty form, verify error text appears below inputs
2. Screen reader: Verify error messages are announced
3. Keyboard: Tab to input, verify error is read aloud
4. Contrast: Verify error text meets 4.5:1 ratio (red-600 on white)
**Impact**: Makes form errors accessible to all users, improves error recovery
---
## Medium Priority Fixes
### Fix #6: Standardize Feature Card Heights
**Issue**: Feature cards have inconsistent heights
**Location**: `src/components/FeatureSection.tsx:28-42`
**Current Code**:
```tsx
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
{features.map((feature) => (
<div key={feature.id} className="bg-white p-6 rounded shadow">
<h3 className="text-xl font-bold mb-2">{feature.title}</h3>
<p className="text-gray-600">{feature.description}</p>
</div>
))}
</div>
```
**Recommended Fix**:
```tsx
<div className="grid grid-cols-1 md:grid-cols-3 gap-6 items-start">
{features.map((feature) => (
<div key={feature.id} className="bg-white p-6 rounded shadow flex flex-col h-full">
<h3 className="text-xl font-bold mb-2">{feature.title}</h3>
<p className="text-gray-600 flex-grow">{feature.description}</p>
{feature.link && (
<a
href={feature.link}
className="mt-4 text-blue-600 hover:text-blue-700 font-medium"
>
Learn more
</a>
)}
</div>
))}
</div>
```
**Changes Made**:
- Added `items-start` to grid (align cards to top)
- Added `flex flex-col h-full` to card (flexbox layout)
- Added `flex-grow` to description (fills available space)
- Positioned link at bottom with `mt-4` (consistent spacing)
**Reasoning**:
- `h-full` makes all cards same height (tallest card determines height)
- `flex-grow` on description pushes "Learn more" link to bottom
- Creates visual consistency across grid
- Maintains readability while looking polished
**Testing**:
1. Desktop (3 columns): Verify all cards same height
2. Test with varying description lengths
3. Ensure "Learn more" links align at bottom
4. Mobile (1 column): Verify cards still look good stacked
**Impact**: More professional, polished appearance
---
### Fix #7: Increase Footer Link Spacing on Mobile
**Issue**: Footer links only 4-6px apart on mobile, difficult to tap
**Location**: `src/components/Footer.tsx:45-58`
**Current Code**:
```tsx
<div className="flex flex-col space-y-1">
<a href="/about" className="text-gray-600 hover:text-gray-900">
About
</a>
<a href="/contact" className="text-gray-600 hover:text-gray-900">
Contact
</a>
<a href="/privacy" className="text-gray-600 hover:text-gray-900">
Privacy Policy
</a>
</div>
```
**Recommended Fix**:
```tsx
<div className="flex flex-col space-y-3">
<a
href="/about"
className="text-gray-600 hover:text-gray-900 py-2 -my-2 inline-block"
>
About
</a>
<a
href="/contact"
className="text-gray-600 hover:text-gray-900 py-2 -my-2 inline-block"
>
Contact
</a>
<a
href="/privacy"
className="text-gray-600 hover:text-gray-900 py-2 -my-2 inline-block"
>
Privacy Policy
</a>
</div>
```
**Changes Made**:
- `space-y-1``space-y-3` (increased spacing from ~4px to ~12px)
- Added `py-2` (8px vertical padding, expanding tap area)
- Added `-my-2` (negative margin to maintain visual spacing)
- Added `inline-block` (allow vertical padding on inline element)
**Reasoning**:
- `space-y-3` provides minimum 8px spacing (WCAG recommendation)
- `py-2` creates 44px minimum tap target height (8px padding + ~28px text)
- Negative margin prevents excessive visual spacing
- Easier to tap accurately on mobile devices
**Testing**:
1. Mobile (375px): Verify 44x44px minimum tap target
2. Test tapping each link with finger (not stylus)
3. Ensure no accidental mis-taps to adjacent links
4. Check visual spacing looks appropriate
**Impact**: Improved mobile usability, reduces user frustration
---
## Low Priority Fixes
### Fix #8: Improve Heading Size Hierarchy
**Issue**: H2 and H3 appear same size, reducing visual hierarchy
**Location**: `src/styles/globals.css:15-25` OR Tailwind config
**Current Code** (CSS):
```css
h2, h3 {
font-size: 1.25rem; /* 20px */
font-weight: 700;
}
```
**Recommended Fix** (CSS):
```css
h2 {
font-size: 1.5rem; /* 24px */
font-weight: 700;
margin-bottom: 0.75rem;
}
h3 {
font-size: 1.25rem; /* 20px */
font-weight: 600;
margin-bottom: 0.5rem;
}
```
**OR** (Tailwind utility classes):
Replace `text-xl` on H2s with `text-2xl`, keep `text-xl` on H3s:
```tsx
<h2 className="text-2xl font-bold mb-3">Section Heading</h2>
<h3 className="text-xl font-semibold mb-2">Subsection Heading</h3>
```
**Reasoning**:
- H2 (24px) → H3 (20px) creates clear hierarchy
- Progressively lighter font weights reinforce hierarchy
- Proper heading sizes aid content scanning
- Improves semantic structure perception
**Testing**:
1. Visual check: H1 > H2 > H3 size progression
2. Compare before/after screenshots
3. Test with screen reader: Verify heading navigation still works
4. Check across different pages for consistency
**Impact**: Improved content scanability and professional appearance
---
## Implementation Checklist
### Critical (Do First)
- [ ] Fix #1: Form label contrast
- [ ] Fix #2: Button text sizing
### High (This Sprint)
- [ ] Fix #3: Navigation overlap
- [ ] Fix #4: Hero image aspect ratio
- [ ] Fix #5: Visible error messages
### Medium (Next Iteration)
- [ ] Fix #6: Card height consistency
- [ ] Fix #7: Footer link spacing
### Low (Backlog)
- [ ] Fix #8: Heading hierarchy
---
## Testing After Implementation
1. **Re-run Playwright tests**:
```bash
npm run test:e2e
```
2. **Capture new screenshots**:
```bash
npm run test:e2e -- --update-snapshots
```
3. **Run accessibility audit**:
```bash
npm run test:e2e -- accessibility.spec.ts
```
4. **Manual testing**:
- Test on real devices (iPhone, Android, iPad)
- Test with screen reader (VoiceOver on iOS, TalkBack on Android)
- Verify color contrast with browser DevTools
5. **Compare screenshots**:
- Before: `screenshots/baselines/`
- After: `screenshots/current/`
- Ensure visual improvements visible
---
**Generated by**: playwright-e2e-automation skill
**Estimated Total Time**: 4-6 hours
**Confidence Level**: High (fixes based on standard patterns)
All fixes follow React + Tailwind CSS best practices and maintain existing code structure.

View File

@@ -0,0 +1,266 @@
# Visual Analysis Report
**Generated**: 2025-11-01 16:30:45
**Test Run**: Homepage e2e tests
**Screenshots Analyzed**: 23
**Issues Found**: 8 (2 Critical, 3 High, 2 Medium, 1 Low)
---
## Executive Summary
Analyzed 23 screenshots across 3 viewports (desktop 1280x720, tablet 768x1024, mobile 375x667). Found 8 UI/UX issues requiring attention, including 2 critical accessibility violations and 3 high-priority layout bugs.
### Issue Breakdown by Category
- **Layout Issues**: 4
- **Accessibility Violations**: 2
- **Typography Problems**: 1
- **Responsive Design Issues**: 1
### Issue Breakdown by Severity
- **Critical** (P0): 2 issues - Fix immediately
- **High** (P1): 3 issues - Fix within sprint
- **Medium** (P2): 2 issues - Address in next iteration
- **Low** (P3): 1 issue - Polish/enhancement
---
## Critical Issues (P0)
### 1. Insufficient Color Contrast on Form Labels
**Severity**: Critical
**Category**: Accessibility
**Viewport**: All viewports
**Screenshot**: `screenshots/current/contact-form-initial-2025-11-01T16-28-32.png`
**Description**:
Form input labels use light gray (#AAAAAA) on white background (#FFFFFF), resulting in a contrast ratio of only 2.6:1. WCAG 2.1 AA requires 4.5:1 for normal text.
**User Impact**:
Users with low vision or color blindness cannot read form labels, making the contact form unusable for accessibility-dependent users. Fails WCAG 2.1 criterion 1.4.3 (Contrast Minimum).
**Visual Evidence**:
In the screenshot, the "Name", "Email", and "Message" labels appear very faint and difficult to read against the white background.
**Affected Elements**:
- Name label
- Email label
- Message label
**Recommended Fix**: See fix-recommendations.md #1
---
### 2. Button Text Truncated on Mobile Viewport
**Severity**: Critical
**Category**: Layout / Responsive
**Viewport**: Mobile (375x667)
**Screenshot**: `screenshots/current/contact-form-filled-mobile-2025-11-01T16-29-15.png`
**Description**:
The "Send Message" button text is cut off mid-word on mobile viewport, displaying "Send Mes..." due to fixed width and large font size.
**User Impact**:
Users cannot see the full button text, creating confusion about the button's purpose and reducing trust in the interface.
**Visual Evidence**:
The submit button shows truncated text with an ellipsis, indicating the button width is insufficient for the text content at the current font size.
**Recommended Fix**: See fix-recommendations.md #2
---
## High Priority Issues (P1)
### 3. Navigation Menu Items Overlap on Tablet Viewport
**Severity**: High
**Category**: Layout
**Viewport**: Tablet (768x1024)
**Screenshot**: `screenshots/current/homepage-responsive-tablet-2025-11-01T16-27-45.png`
**Description**:
Navigation menu items in the header overlap each other at tablet breakpoint (768px), causing "About" and "Contact" links to partially obscure each other.
**User Impact**:
Users cannot click on navigation links reliably, potentially clicking the wrong link or missing links entirely.
**Visual Evidence**:
Screenshot shows "About" and "Contact" link text overlapping in the header navigation bar.
**Recommended Fix**: See fix-recommendations.md #3
---
### 4. Hero Section Image Stretched on Mobile
**Severity**: High
**Category**: Responsive / Layout
**Viewport**: Mobile (375x667)
**Screenshot**: `screenshots/current/homepage-responsive-mobile-2025-11-01T16-27-52.png`
**Description**:
Hero section background image appears stretched and distorted on mobile viewport. The 16:9 image is forced into a narrow vertical space, causing visible distortion.
**User Impact**:
Unprofessional appearance reduces user trust and brand perception. Image content may be unrecognizable when distorted.
**Visual Evidence**:
The hero image shows obvious stretching, with circular elements appearing oval-shaped and text in the image appearing compressed vertically.
**Recommended Fix**: See fix-recommendations.md #4
---
### 5. Missing Error State Indication
**Severity**: High
**Category**: Accessibility / UX
**Viewport**: All viewports
**Screenshot**: `screenshots/current/contact-form-validation-errors-2025-11-01T16-29-45.png`
**Description**:
Form validation errors are indicated only by a red border around inputs. No error text is visible, and there's no icon or other non-color indicator.
**User Impact**:
Users relying on screen readers won't hear error messages. Color-blind users may not notice the red border. Error messages are essential for understanding what went wrong.
**Visual Evidence**:
Screenshot shows inputs with red borders but no visible error text below them explaining what the error is.
**Recommended Fix**: See fix-recommendations.md #5
---
## Medium Priority Issues (P2)
### 6. Inconsistent Card Heights in Feature Section
**Severity**: Medium
**Category**: Layout
**Viewport**: Desktop (1280x720)
**Screenshot**: `screenshots/current/homepage-initial-load-2025-11-01T16-27-18.png`
**Description**:
Feature cards have varying heights due to different content lengths. The grid layout doesn't maintain consistent card heights, creating a jagged appearance.
**User Impact**:
Visually inconsistent and less professional. Makes the page feel unpolished.
**Visual Evidence**:
Three feature cards visible - first card is noticeably taller than the second, and third is somewhere in between, creating uneven rows.
**Recommended Fix**: See fix-recommendations.md #6
---
### 7. Footer Links Too Close Together on Mobile
**Severity**: Medium
**Category**: Responsive / Touch Targets
**Viewport**: Mobile (375x667)
**Screenshot**: `screenshots/current/homepage-responsive-mobile-2025-11-01T16-27-52.png`
**Description**:
Footer navigation links are spaced only 4-6px apart vertically on mobile, making them difficult to tap accurately. WCAG 2.1 recommends minimum 44x44px touch targets with 8px spacing.
**User Impact**:
Users frequently mis-tap links, requiring multiple attempts to navigate. Particularly frustrating for users with motor impairments or large fingers.
**Visual Evidence**:
Footer links appear very close together with minimal spacing between each link.
**Recommended Fix**: See fix-recommendations.md #7
---
## Low Priority Issues (P3)
### 8. Heading Sizes Not Progressively Smaller
**Severity**: Low
**Category**: Typography / Visual Hierarchy
**Viewport**: All viewports
**Screenshot**: `screenshots/current/about-page-loaded-2025-11-01T16-28-05.png`
**Description**:
H2 and H3 headings appear to be the same size (approximately 20px), reducing visual hierarchy and making it harder to scan content structure.
**User Impact**:
Minor impact on content scanability. Users may not immediately recognize the content hierarchy.
**Visual Evidence**:
Page title (H1) is clearly larger, but H2 section headings and H3 subsection headings are visually identical in size.
**Recommended Fix**: See fix-recommendations.md #8
---
## Summary Statistics
### By Severity
| Severity | Count | Percentage |
|----------|-------|------------|
| Critical | 2 | 25% |
| High | 3 | 37.5% |
| Medium | 2 | 25% |
| Low | 1 | 12.5% |
### By Category
| Category | Count |
|----------------|-------|
| Layout | 4 |
| Accessibility | 2 |
| Typography | 1 |
| Responsive | 1 |
### By Viewport
| Viewport | Issues |
|----------|--------|
| Mobile | 5 |
| Tablet | 1 |
| Desktop | 1 |
| All | 3 |
---
## Recommended Actions
1. **Immediate (Critical)**:
- Fix form label contrast (#1)
- Fix button text truncation on mobile (#2)
2. **This Sprint (High)**:
- Fix navigation overlap on tablet (#3)
- Fix hero image stretching (#4)
- Add visible error messages (#5)
3. **Next Iteration (Medium)**:
- Standardize feature card heights (#6)
- Increase footer link spacing on mobile (#7)
4. **Backlog (Low)**:
- Adjust heading size hierarchy (#8)
---
## Testing Recommendations
After fixes are implemented:
1. Re-run Playwright test suite to capture updated screenshots
2. Compare new screenshots with current baseline
3. Run accessibility audit with axe-core
4. Test on real devices (iOS Safari, Android Chrome)
5. Validate color contrast with WebAIM tool
6. Test with screen reader (VoiceOver, NVDA)
---
**Generated by**: playwright-e2e-automation skill
**Analysis Method**: LLM-powered visual screenshot analysis
**Reference Guides**: accessibility-checks.md, common-ui-bugs.md

View File

@@ -0,0 +1,134 @@
# CI/CD Integration
## GitHub Actions Example
```yaml
name: Playwright E2E Tests
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: 18
- name: Install dependencies
run: npm ci
- name: Install Playwright browsers
run: npx playwright install --with-deps
- name: Run Playwright tests
run: npm run test:e2e
- name: Upload screenshots
if: always()
uses: actions/upload-artifact@v3
with:
name: playwright-screenshots
path: screenshots/
- name: Upload HTML report
if: always()
uses: actions/upload-artifact@v3
with:
name: playwright-report
path: playwright-report/
```
## Baseline Management in CI
### 1. Store baselines in repository
```bash
git add screenshots/baselines/
git commit -m "chore: update visual regression baselines"
```
### 2. Update baselines on approval
1. Run tests locally: `npm run test:e2e`
2. Review diffs: `npx playwright show-report`
3. Update baselines: `npm run test:e2e:update-snapshots`
4. Commit updated baselines
### 3. Fail CI on visual regressions
- Configure threshold in playwright.config.ts
- Tests fail if diffs exceed threshold
- Review in CI artifacts before merging
## GitLab CI Example
```yaml
e2e-tests:
image: mcr.microsoft.com/playwright:v1.40.0-jammy
stage: test
script:
- npm ci
- npm run test:e2e
artifacts:
when: always
paths:
- screenshots/
- playwright-report/
expire_in: 7 days
```
## CircleCI Example
```yaml
version: 2.1
jobs:
e2e-tests:
docker:
- image: mcr.microsoft.com/playwright:v1.40.0-jammy
steps:
- checkout
- run: npm ci
- run: npm run test:e2e
- store_artifacts:
path: screenshots
- store_artifacts:
path: playwright-report
```
## Best Practices
### Screenshot Artifact Storage
- Always upload screenshots as artifacts
- Keep artifacts for at least 7 days
- Consider cloud storage for long-term retention
### Parallel Execution
```yaml
# Run tests across multiple shards
- name: Run Playwright tests
run: npx playwright test --shard=${{ matrix.shard }}/${{ strategy.job-total }}
strategy:
matrix:
shard: [1, 2, 3, 4]
```
### Caching
```yaml
- name: Cache Playwright browsers
uses: actions/cache@v3
with:
path: ~/.cache/ms-playwright
key: playwright-${{ hashFiles('package-lock.json') }}
```
### Notifications
Configure notifications for test failures:
- Slack integration
- Email alerts
- PR comments with screenshot diffs

View File

@@ -0,0 +1,186 @@
# Troubleshooting Guide
## Framework Version Errors
### Tailwind CSS v4 Syntax Mismatch
**Symptom**: Console error "Cannot apply unknown utility class" or "Utilities must be known at build time"
**Cause**: Tailwind v4 installed but CSS uses old v3 `@tailwind` directive syntax
**Root Cause**: Breaking change in Tailwind v4 - changed from `@tailwind` to `@import` syntax
**Detection**: Pre-flight health check catches this before running tests
**Auto-fix Available**: Yes - skill detects version and uses correct template
**Manual Fix**:
```css
// Old (v3):
@tailwind base;
@tailwind components;
@tailwind utilities;
// New (v4):
@import "tailwindcss";
```
**Also Update**: `postcss.config.js` - change `tailwindcss: {}` to `'@tailwindcss/postcss': {}`
**Prevention**: Skill consults `data/framework-versions.yaml` and selects appropriate template
**Documentation**: https://tailwindcss.com/docs/upgrade-guide
---
### PostCSS Plugin Not Found
**Symptom**: Build error "Plugin tailwindcss not found" or "Cannot find module 'tailwindcss'"
**Cause**: Tailwind v4 renamed PostCSS plugin but config uses old name
**Root Cause**: PostCSS plugin changed from `tailwindcss` to `@tailwindcss/postcss` in v4
**Detection**: Pre-flight check or build error
**Auto-fix Available**: Yes - version detection selects correct PostCSS template
**Manual Fix**:
```javascript
// postcss.config.js
// Old (v3):
export default {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
};
// New (v4):
export default {
plugins: {
'@tailwindcss/postcss': {},
autoprefixer: {},
},
};
```
**Verification**: Run `npm list @tailwindcss/postcss` to confirm installation
**Prevention**: Skill uses `templates/configs/postcss-tailwind-v4.js` for Tailwind v4
---
### Version Incompatibility Warning
**Symptom**: Skill warns "Unknown version detected" or "Version outside known ranges"
**Cause**: Framework version not in compatibility database
**Impact**: Skill may use outdated templates or incorrect syntax
**Solution**:
1. Check `data/framework-versions.yaml` for supported versions
2. If version is newer, skill uses latest known template (may need manual adjustment)
3. If version is older, skill may suggest upgrading
**Reporting**: Please report unknown versions as GitHub issues to improve skill
**Workaround**: Manually specify template paths if needed
---
## Common Issues
### Application not detected
**Cause**: Unrecognized framework or missing package.json
**Solution**: Ask user to specify app type and dev server command manually
**Fallback**: Use generic static site configuration
---
### Dev server not running
**Cause**: Application not started before running tests
**Solution**: Attempt to start server automatically using detected script (npm run dev)
**Fallback**: Prompt user to start server manually
---
### Playwright installation fails
**Cause**: Network issues, permissions, incompatible Node version
**Solution**:
- Check Node version (>=16)
- Retry with --force
- Suggest manual installation
**Debugging**: Show full error output, check npm logs
---
### Screenshot capture fails
**Cause**: Timeout waiting for page load, element not found, navigation error
**Solution**:
- Increase timeout
- Add explicit waits
- Capture partial screenshot on failure
**Recovery**: Continue with other tests, report failure with details
---
### No baselines exist for comparison
**Cause**: First test run, baselines deleted
**Solution**: Current screenshots become baselines automatically
**Message**: "No baselines found. Current screenshots saved as baselines."
---
### Visual analysis fails
**Cause**: LLM API error, screenshot file corruption, unsupported format
**Solution**:
- Retry analysis
- Skip corrupted images
- Validate PNG format
**Fallback**: Provide raw screenshots for manual inspection
---
## Performance Characteristics
### Execution Times (Typical React App)
| Phase | Time |
|-------|------|
| Application detection | ~5 seconds |
| Playwright installation | ~2-3 minutes (one-time) |
| Configuration generation | ~10 seconds |
| Test generation | ~30 seconds |
| Test execution (5 tests) | ~30-60 seconds |
| Screenshot capture | ~1-2 seconds per screenshot |
| Visual analysis (10 screenshots) | ~1-2 minutes |
| Regression comparison | ~10 seconds |
| Fix generation | ~30 seconds |
**Total end-to-end time**: ~5-8 minutes (excluding Playwright install)
### Resource Usage
- **Disk space**: ~500MB (Playwright browsers)
- **Memory**: ~500MB during test execution
- **Screenshots**: ~1-2MB per full-page screenshot

View File

@@ -0,0 +1,294 @@
# Helper Scripts
These scripts provide reference implementations for automating Playwright e2e testing workflows. They are meant to guide LLM implementation, not to be executed directly.
## Script Overview
### 1. `setup-playwright.sh`
**Purpose**: Automate Playwright installation and initial configuration
**What it does**:
- Checks Node.js version compatibility
- Installs Playwright via npm
- Installs browser binaries (Chromium, Firefox, WebKit)
- Generates initial playwright.config.ts
- Creates test directory structure
**Usage Pattern** (for LLM to implement):
```bash
#!/bin/bash
# Detect if Playwright is already installed
# If not, run: npm init playwright@latest -- --yes
# Create directory structure
# Generate config based on detected framework
```
### 2. `detect-framework.ts`
**Purpose**: Identify application framework and configuration
**What it does**:
- Reads package.json dependencies
- Checks for config files (vite.config.ts, next.config.js, etc.)
- Determines dev server command and port
- Returns framework metadata for test generation
**Usage Pattern**:
```typescript
// Read package.json
// Match against patterns in framework-detection-patterns.yaml
// Return framework object with baseURL, devCommand, etc.
```
### 3. `generate-tests.ts`
**Purpose**: Generate Playwright test specifications from templates
**What it does**:
- Takes framework metadata and user journeys as input
- Populates test templates with appropriate selectors
- Adds screenshot capture points
- Generates Page Object Models
- Creates test helper utilities
**Usage Pattern**:
```typescript
// Load templates from ../templates/
// Populate with framework-specific values
// Generate .spec.ts files in tests/specs/
// Generate .page.ts files in tests/pages/
```
### 4. `capture-screenshots.ts`
**Purpose**: Execute tests and capture organized screenshots
**What it does**:
- Runs Playwright test suite
- Captures screenshots at defined points
- Organizes by test name, viewport, timestamp
- Generates metadata JSON for each screenshot
**Usage Pattern**:
```typescript
// Run: npx playwright test
// Hook into test lifecycle to capture screenshots
// Save to screenshots/current/{test-name}-{viewport}-{state}-{timestamp}.png
// Generate screenshots/metadata.json
```
### 5. `analyze-visual.ts`
**Purpose**: LLM-powered visual analysis of screenshots
**What it does**:
- Reads all screenshots from current run
- Sends each to LLM vision API with analysis prompts
- Categorizes findings (UI bugs, accessibility, layout)
- Generates structured issue reports
- Assigns severity levels
**Usage Pattern**:
```typescript
// Read screenshots/current/*.png
// For each screenshot:
// - Send to LLM with prompts from ../data/common-ui-bugs.md
// - Extract identified issues
// - Categorize and rate severity
// Generate visual-analysis-report.md
```
### 6. `compare-regression.ts`
**Purpose**: Compare current screenshots with baselines
**What it does**:
- Loads baseline screenshots
- Performs pixel-level comparison with current screenshots
- Generates diff images highlighting changes
// Calculates difference percentages
- Classifies changes (expected, suspicious, critical)
**Usage Pattern**:
```typescript
// Load screenshots/baselines/*.png
// Load screenshots/current/*.png
// Use Playwright's comparison utilities
// Generate screenshots/diffs/*.png
// Create regression-report.md
```
### 7. `generate-fixes.ts`
**Purpose**: Generate code fix recommendations from visual issues
**What it does**:
- Takes visual analysis results as input
- Maps issues to source code locations
- Generates specific fix recommendations
- Provides before/after code snippets
- Prioritizes by severity
**Usage Pattern**:
```typescript
// Read visual-analysis-report.md
// For each issue:
// - Infer likely source file (component, styles)
// - Generate fix recommendation
// - Provide file:line reference
// - Create code snippet
// Generate fix-recommendations.md
```
### 8. `export-test-suite.ts`
**Purpose**: Package test suite for production use
**What it does**:
- Copies generated tests to project's tests/ directory
- Adds npm scripts to package.json
- Generates README with usage instructions
- Creates CI/CD workflow examples
- Validates everything works
**Usage Pattern**:
```typescript
// Copy tests/specs/ to project/tests/e2e/
// Copy tests/pages/ to project/tests/e2e/pages/
// Update project/package.json with test scripts
// Generate project/tests/e2e/README.md
// Validate: run tests to ensure they work
```
## Implementation Guidelines
### For LLMs Implementing These Scripts
**Don't execute scripts directly** - Instead, implement the logic inline:
```typescript
// Example: Instead of calling setup-playwright.sh
// Implement the logic directly in your response:
async function setupPlaywright() {
// 1. Check Node version
const nodeVersion = await execCommand('node --version');
if (!nodeVersion.startsWith('v16') && !nodeVersion.startsWith('v18')) {
throw new Error('Node 16+ required');
}
// 2. Install Playwright
await execCommand('npm init playwright@latest -- --yes');
// 3. Create directory structure
await createDirectories([
'tests/setup',
'tests/pages',
'tests/specs',
'tests/utils',
'screenshots/baselines',
'screenshots/current',
'screenshots/diffs',
]);
// 4. Generate config (using templates)
const config = await generatePlaywrightConfig(framework);
await writeFile('playwright.config.ts', config);
}
```
### Script Dependencies
All scripts should be standalone reference implementations:
- No external dependencies beyond Playwright
- Clear, commented code
- Error handling included
- TypeScript for type safety
### Data Flow Between Scripts
```
1. detect-framework.ts
↓ (framework metadata)
2. setup-playwright.sh
↓ (Playwright installed, config generated)
3. generate-tests.ts
↓ (test files created)
4. capture-screenshots.ts
↓ (screenshots captured)
5. analyze-visual.ts
↓ (issues identified)
6. compare-regression.ts
↓ (regressions detected)
7. generate-fixes.ts
↓ (fix recommendations created)
8. export-test-suite.ts
↓ (production-ready test suite)
```
## Testing the Scripts
To validate script logic (for human developers):
```bash
# 1. Framework detection
npx ts-node scripts/detect-framework.ts
# Should output: { framework: 'react_vite', baseURL: 'http://localhost:5173', ... }
# 2. Test generation
npx ts-node scripts/generate-tests.ts --framework react_vite
# Should create: tests/specs/*.spec.ts
# 3. Screenshot capture
npx ts-node scripts/capture-screenshots.ts
# Should create: screenshots/current/*.png
# 4. Visual analysis
npx ts-node scripts/analyze-visual.ts
# Should create: visual-analysis-report.md
# 5. Regression comparison
npx ts-node scripts/compare-regression.ts
# Should create: screenshots/diffs/*.png, regression-report.md
# 6. Fix generation
npx ts-node scripts/generate-fixes.ts
# Should create: fix-recommendations.md
# 7. Export
npx ts-node scripts/export-test-suite.ts
# Should copy files to project/tests/e2e/
```
## Error Handling
All scripts should handle common errors:
```typescript
try {
// Script logic
} catch (error) {
if (error.code === 'ENOENT') {
console.error('File not found. Check paths.');
} else if (error.message.includes('npm')) {
console.error('npm command failed. Check if npm is installed.');
} else {
console.error('Unexpected error:', error.message);
}
process.exit(1);
}
```
## Performance Considerations
- **Parallel execution**: Run tests in parallel where possible
- **Incremental screenshots**: Only capture screenshots for changed tests
- **Caching**: Cache framework detection results
- **Batch processing**: Process multiple screenshots in batches for LLM analysis
## Future Enhancements
Potential scripts to add:
- `update-baselines.ts` - Interactive baseline approval
- `optimize-selectors.ts` - Suggest better selectors
- `generate-performance-tests.ts` - Add Core Web Vitals checks
- `create-ci-config.ts` - Generate GitHub Actions workflow
- `analyze-flaky-tests.ts` - Detect and fix flaky tests
---
**Remember**: These are reference implementations to guide LLM development. The actual execution happens through LLM-generated code, not by running these scripts directly.

View File

@@ -0,0 +1,16 @@
/**
* PostCSS Configuration for Tailwind CSS v3
*
* Compatible with: Tailwind CSS >=3.0.0 <4.0.0
* Generated by: playwright-e2e-automation skill
*/
export default {
plugins: {
// Tailwind CSS v3 uses 'tailwindcss' as plugin name
tailwindcss: {},
// Autoprefixer adds vendor prefixes for browser compatibility
autoprefixer: {},
},
};

View File

@@ -0,0 +1,24 @@
/**
* PostCSS Configuration for Tailwind CSS v4
*
* Compatible with: Tailwind CSS >=4.0.0
*
* Breaking changes from v3:
* - Plugin name changed from 'tailwindcss' to '@tailwindcss/postcss'
* - Improved performance with new architecture
* - Better integration with build tools
*
* Migration guide: https://tailwindcss.com/docs/upgrade-guide
* Generated by: playwright-e2e-automation skill
*/
export default {
plugins: {
// Tailwind CSS v4 uses '@tailwindcss/postcss' as plugin name
// This is a BREAKING CHANGE from v3
'@tailwindcss/postcss': {},
// Autoprefixer adds vendor prefixes for browser compatibility
autoprefixer: {},
},
};

View File

@@ -0,0 +1,20 @@
/**
* Tailwind CSS v3 Syntax
*
* This template uses the classic @tailwind directive syntax
* Compatible with: Tailwind CSS >=3.0.0 <4.0.0
*
* Generated by: playwright-e2e-automation skill
*/
/* Base styles, CSS resets, and browser normalization */
@tailwind base;
/* Component classes and utilities */
@tailwind components;
/* Utility classes (spacing, colors, typography, etc.) */
@tailwind utilities;
/* ========== CUSTOM STYLES ========== */
/* Add your custom CSS below this line */

View File

@@ -0,0 +1,38 @@
/**
* Tailwind CSS v4 Syntax
*
* This template uses the new @import syntax introduced in Tailwind v4
* Compatible with: Tailwind CSS >=4.0.0
*
* Breaking changes from v3:
* - @tailwind directives replaced with single @import
* - PostCSS plugin changed to @tailwindcss/postcss
* - Improved performance and smaller bundle size
*
* Migration guide: https://tailwindcss.com/docs/upgrade-guide
* Generated by: playwright-e2e-automation skill
*/
/* Import Tailwind CSS (base, components, utilities all included) */
@import "tailwindcss";
/* ========== CUSTOM STYLES ========== */
/* Add your custom CSS below this line */
/* Example: Custom utility classes using @layer */
/*
@layer utilities {
.text-balance {
text-wrap: balance;
}
}
*/
/* Example: Custom components using @layer */
/*
@layer components {
.btn-primary {
@apply px-4 py-2 bg-blue-600 text-white rounded hover:bg-blue-700;
}
}
*/

View File

@@ -0,0 +1,171 @@
/**
* Vanilla CSS Template (No CSS Framework)
*
* This template is used when no CSS framework is detected
* or when Tailwind CSS is not installed.
*
* Generated by: playwright-e2e-automation skill
*/
/* ========== CSS RESET ========== */
*,
*::before,
*::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
/* ========== BASE STYLES ========== */
:root {
/* Color palette */
--color-primary: #3b82f6;
--color-primary-dark: #2563eb;
--color-text: #1f2937;
--color-text-light: #6b7280;
--color-background: #ffffff;
--color-border: #e5e7eb;
/* Spacing scale */
--spacing-xs: 0.25rem;
--spacing-sm: 0.5rem;
--spacing-md: 1rem;
--spacing-lg: 1.5rem;
--spacing-xl: 2rem;
/* Typography */
--font-sans: system-ui, -apple-system, sans-serif;
--font-mono: 'Courier New', monospace;
}
body {
font-family: var(--font-sans);
color: var(--color-text);
background-color: var(--color-background);
line-height: 1.6;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
/* ========== TYPOGRAPHY ========== */
h1,
h2,
h3,
h4,
h5,
h6 {
font-weight: 700;
line-height: 1.2;
margin-bottom: var(--spacing-md);
}
h1 {
font-size: 2.25rem;
}
h2 {
font-size: 1.875rem;
}
h3 {
font-size: 1.5rem;
}
h4 {
font-size: 1.25rem;
}
p {
margin-bottom: var(--spacing-md);
}
a {
color: var(--color-primary);
text-decoration: none;
}
a:hover {
color: var(--color-primary-dark);
text-decoration: underline;
}
/* ========== FORM ELEMENTS ========== */
button,
input,
select,
textarea {
font-family: inherit;
font-size: 100%;
}
button {
padding: var(--spacing-sm) var(--spacing-md);
background-color: var(--color-primary);
color: white;
border: none;
border-radius: 0.375rem;
cursor: pointer;
transition: background-color 0.2s;
}
button:hover {
background-color: var(--color-primary-dark);
}
button:disabled {
opacity: 0.5;
cursor: not-allowed;
}
input,
textarea,
select {
padding: var(--spacing-sm);
border: 1px solid var(--color-border);
border-radius: 0.375rem;
width: 100%;
}
input:focus,
textarea:focus,
select:focus {
outline: 2px solid var(--color-primary);
outline-offset: 2px;
}
/* ========== UTILITY CLASSES ========== */
.container {
max-width: 1200px;
margin: 0 auto;
padding: 0 var(--spacing-md);
}
.text-center {
text-align: center;
}
.mt-1 {
margin-top: var(--spacing-xs);
}
.mt-2 {
margin-top: var(--spacing-sm);
}
.mt-3 {
margin-top: var(--spacing-md);
}
.mt-4 {
margin-top: var(--spacing-lg);
}
.mb-1 {
margin-bottom: var(--spacing-xs);
}
.mb-2 {
margin-bottom: var(--spacing-sm);
}
.mb-3 {
margin-bottom: var(--spacing-md);
}
.mb-4 {
margin-bottom: var(--spacing-lg);
}
/* ========== CUSTOM STYLES ========== */
/* Add your custom CSS below this line */

View File

@@ -0,0 +1,127 @@
import { chromium, FullConfig } from '@playwright/test';
import * as fs from 'fs';
import * as path from 'path';
/**
* Global Setup
* Runs once before all tests
*
* Responsibilities:
* - Ensure dev server is ready
* - Create screenshot directories
* - Perform any global authentication
* - Set up test database (if needed)
*/
async function globalSetup(config: FullConfig) {
console.log('\n🚀 Starting global setup...\n');
// 1. Create screenshot directories
createScreenshotDirectories();
// 2. Verify dev server is accessible (webServer config handles startup)
const baseURL = config.projects[0].use.baseURL || 'http://localhost:{{PORT}}';
await verifyServer(baseURL);
// 3. Optional: Perform global authentication
// await performAuthentication(config);
console.log('✅ Global setup complete\n');
}
/**
* Create necessary screenshot directories
*/
function createScreenshotDirectories() {
const directories = [
'screenshots/current',
'screenshots/baselines',
'screenshots/diffs',
'test-results',
];
for (const dir of directories) {
const dirPath = path.join(process.cwd(), dir);
if (!fs.existsSync(dirPath)) {
fs.mkdirSync(dirPath, { recursive: true });
console.log(`📁 Created directory: ${dir}`);
}
}
}
/**
* Verify dev server is accessible
*/
async function verifyServer(baseURL: string, maxRetries = 30) {
console.log(`🔍 Verifying server at ${baseURL}...`);
for (let i = 0; i < maxRetries; i++) {
try {
const browser = await chromium.launch();
const page = await browser.newPage();
const response = await page.goto(baseURL, { timeout: 5000 });
if (response && response.ok()) {
console.log(`✅ Server is ready at ${baseURL}`);
await browser.close();
return;
}
await browser.close();
} catch (error) {
// Server not ready yet, wait and retry
if (i < maxRetries - 1) {
await new Promise((resolve) => setTimeout(resolve, 1000));
} else {
throw new Error(
`Server at ${baseURL} is not accessible after ${maxRetries} attempts`
);
}
}
}
}
/**
* Optional: Perform global authentication
* Saves authentication state to be reused across all tests
*/
async function performAuthentication(config: FullConfig) {
// Only run if authentication is needed
if (!process.env.AUTH_USERNAME || !process.env.AUTH_PASSWORD) {
console.log('⏭️ Skipping authentication (no credentials provided)');
return;
}
console.log('🔐 Performing global authentication...');
const baseURL = config.projects[0].use.baseURL || 'http://localhost:{{PORT}}';
const browser = await chromium.launch();
const context = await browser.newContext();
const page = await context.newPage();
try {
// Navigate to login page
await page.goto(`${baseURL}/login`);
// Fill in credentials
await page.getByLabel('Username').fill(process.env.AUTH_USERNAME);
await page.getByLabel('Password').fill(process.env.AUTH_PASSWORD);
await page.getByRole('button', { name: 'Sign in' }).click();
// Wait for successful login (adjust selector as needed)
await page.waitForURL(`${baseURL}/dashboard`, { timeout: 10000 });
// Save authentication state
await context.storageState({ path: 'auth.json' });
console.log('✅ Authentication successful, state saved to auth.json');
} catch (error) {
console.error('❌ Authentication failed:', error);
throw error;
} finally {
await browser.close();
}
}
export default globalSetup;

View File

@@ -0,0 +1,64 @@
import { FullConfig } from '@playwright/test';
import { generateManifest } from '../utils/screenshot-helper';
/**
* Global Teardown
* Runs once after all tests complete
*
* Responsibilities:
* - Generate screenshot manifest
* - Clean up temporary files (if needed)
* - Generate summary report
* - Perform any cleanup tasks
*/
async function globalTeardown(config: FullConfig) {
console.log('\n🏁 Starting global teardown...\n');
// 1. Generate screenshot manifest
try {
generateManifest();
} catch (error) {
console.error('⚠️ Failed to generate screenshot manifest:', error);
}
// 2. Generate test summary
generateSummary();
// 3. Optional: Clean up temporary files
// cleanupTempFiles();
console.log('\n✅ Global teardown complete\n');
}
/**
* Generate test execution summary
*/
function generateSummary() {
console.log('\n📊 Test Execution Summary:');
console.log('─'.repeat(50));
// Test results are available through Playwright's built-in reporters
// This is just a placeholder for custom summary logic
console.log('✅ Check playwright-report/ for detailed results');
console.log('✅ Screenshots available in screenshots/current/');
console.log('✅ Test results available in test-results/');
console.log('\n💡 Next steps:');
console.log(' 1. Review screenshots for visual issues');
console.log(' 2. Compare with baselines if available');
console.log(' 3. Run visual analysis: npm run analyze:visual');
console.log(' 4. Generate fix recommendations if issues found');
}
/**
* Optional: Clean up temporary files
*/
function cleanupTempFiles() {
// Add cleanup logic here if needed
// For example: remove old screenshots, clear cache, etc.
console.log('🧹 Cleaning up temporary files...');
}
export default globalTeardown;

View File

@@ -0,0 +1,84 @@
import { Page, Locator } from '@playwright/test';
/**
* {{PAGE_NAME}} Page Object Model
*
* Represents: {{PAGE_DESCRIPTION}}
* URL: {{PAGE_URL}}
* Generated: {{GENERATED_DATE}}
*/
export class {{PAGE_CLASS_NAME}} {
readonly page: Page;
// Locators - Using semantic selectors (getByRole, getByLabel, getByText)
{{#LOCATORS}}
readonly {{LOCATOR_NAME}}: Locator;
{{/LOCATORS}}
constructor(page: Page) {
this.page = page;
// Initialize locators
{{#LOCATORS}}
this.{{LOCATOR_NAME}} = page.{{SELECTOR}};
{{/LOCATORS}}
}
/**
* Navigate to this page
*/
async goto() {
await this.page.goto('{{PAGE_URL}}');
await this.page.waitForLoadState('networkidle');
}
/**
* Wait for page to be ready
*/
async waitForReady() {
await this.page.waitForLoadState('domcontentloaded');
{{#READY_INDICATORS}}
await this.{{INDICATOR}}.waitFor({ state: 'visible' });
{{/READY_INDICATORS}}
}
{{#METHODS}}
/**
* {{METHOD_DESCRIPTION}}
{{#PARAMS}}
* @param {{PARAM_NAME}} - {{PARAM_DESCRIPTION}}
{{/PARAMS}}
*/
async {{METHOD_NAME}}({{PARAMS_SIGNATURE}}) {
{{METHOD_BODY}}
}
{{/METHODS}}
/**
* Get page title
*/
async getTitle(): Promise<string> {
return await this.page.title();
}
/**
* Get current URL
*/
async getCurrentUrl(): Promise<string> {
return this.page.url();
}
/**
* Take screenshot of this page
* @param name - Screenshot filename
*/
async screenshot(name: string) {
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
await this.page.screenshot({
path: `screenshots/current/{{PAGE_NAME_KEBAB}}-${name}-${timestamp}.png`,
fullPage: true,
});
}
}

View File

@@ -0,0 +1,139 @@
import { defineConfig, devices } from '@playwright/test';
/**
* Playwright Configuration
* Generated by playwright-e2e-automation skill
*
* Framework: {{FRAMEWORK_NAME}}
* Base URL: {{BASE_URL}}
* Generated: {{GENERATED_DATE}}
*/
export default defineConfig({
testDir: './tests/specs',
/**
* Maximum time one test can run for
*/
timeout: {{TIMEOUT}},
/**
* Test execution settings
*/
fullyParallel: true,
forbidOnly: !!process.env.CI,
retries: process.env.CI ? 2 : 0,
workers: process.env.CI ? 1 : undefined,
/**
* Reporter configuration
* CI: GitHub Actions reporter
* Local: HTML reporter with screenshots
*/
reporter: process.env.CI
? 'github'
: [
['html', { outputFolder: 'playwright-report' }],
['json', { outputFile: 'test-results/results.json' }],
],
/**
* Shared settings for all projects
*/
use: {
/* Base URL for navigation */
baseURL: '{{BASE_URL}}',
/* Collect trace on first retry */
trace: 'on-first-retry',
/* Screenshot settings */
screenshot: {
mode: 'only-on-failure',
fullPage: true,
},
/* Video settings */
video: 'retain-on-failure',
/* Maximum time each action can take */
actionTimeout: 10000,
/* Navigation timeout */
navigationTimeout: 30000,
},
/**
* Browser and device configurations
*/
projects: [
{
name: 'chromium-desktop',
use: {
...devices['Desktop Chrome'],
viewport: { width: 1280, height: 720 },
},
},
{
name: 'firefox-desktop',
use: {
...devices['Desktop Firefox'],
viewport: { width: 1280, height: 720 },
},
},
{
name: 'webkit-desktop',
use: {
...devices['Desktop Safari'],
viewport: { width: 1280, height: 720 },
},
},
{
name: 'mobile-chrome',
use: {
...devices['Pixel 5'],
},
},
{
name: 'mobile-safari',
use: {
...devices['iPhone 13'],
},
},
{
name: 'tablet',
use: {
...devices['iPad Pro'],
},
},
],
/**
* Web server configuration
* Starts dev server before running tests
*/
webServer: {
command: '{{DEV_SERVER_COMMAND}}',
url: '{{BASE_URL}}',
reuseExistingServer: !process.env.CI,
timeout: 120 * 1000,
stdout: 'pipe',
stderr: 'pipe',
},
/**
* Output directories
*/
outputDir: 'test-results',
/**
* Global setup/teardown
*/
globalSetup: require.resolve('./tests/setup/global-setup.ts'),
globalTeardown: require.resolve('./tests/setup/global-teardown.ts'),
});

View File

@@ -0,0 +1,236 @@
import { Page } from '@playwright/test';
import * as fs from 'fs';
import * as path from 'path';
/**
* Screenshot Helper Utilities
* Provides consistent screenshot capture with metadata
*/
export interface ScreenshotMetadata {
path: string;
context: string;
timestamp: string;
viewport: {
width: number;
height: number;
};
url: string;
testName?: string;
}
/**
* Capture screenshot with context metadata
*
* @param page - Playwright page object
* @param name - Screenshot name (will be kebab-cased)
* @param context - Description of what the screenshot shows
* @returns Metadata about the captured screenshot
*/
export async function captureWithContext(
page: Page,
name: string,
context: string
): Promise<ScreenshotMetadata> {
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
const viewport = page.viewportSize() || { width: 1280, height: 720 };
const url = page.url();
// Ensure screenshots directory exists
const screenshotDir = path.join(process.cwd(), 'screenshots', 'current');
if (!fs.existsSync(screenshotDir)) {
fs.mkdirSync(screenshotDir, { recursive: true });
}
// Generate filename
const filename = `${name}-${timestamp}.png`;
const screenshotPath = path.join(screenshotDir, filename);
// Wait for network idle before capturing
await page.waitForLoadState('networkidle');
// Capture screenshot
await page.screenshot({
path: screenshotPath,
fullPage: true,
});
// Create metadata
const metadata: ScreenshotMetadata = {
path: screenshotPath,
context,
timestamp: new Date().toISOString(),
viewport,
url,
testName: process.env.PLAYWRIGHT_TEST_NAME,
};
// Save metadata alongside screenshot
const metadataPath = screenshotPath.replace('.png', '.json');
fs.writeFileSync(metadataPath, JSON.stringify(metadata, null, 2));
console.log(`📸 Screenshot captured: ${filename}`);
console.log(` Context: ${context}`);
return metadata;
}
/**
* Capture element screenshot with context
*
* @param page - Playwright page object
* @param selector - Element selector
* @param name - Screenshot name
* @param context - Description
*/
export async function captureElement(
page: Page,
selector: string,
name: string,
context: string
): Promise<ScreenshotMetadata> {
const element = page.locator(selector);
await element.waitFor({ state: 'visible' });
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
const viewport = page.viewportSize() || { width: 1280, height: 720 };
const url = page.url();
const screenshotDir = path.join(process.cwd(), 'screenshots', 'current');
if (!fs.existsSync(screenshotDir)) {
fs.mkdirSync(screenshotDir, { recursive: true });
}
const filename = `${name}-element-${timestamp}.png`;
const screenshotPath = path.join(screenshotDir, filename);
await element.screenshot({
path: screenshotPath,
});
const metadata: ScreenshotMetadata = {
path: screenshotPath,
context: `${context} (element: ${selector})`,
timestamp: new Date().toISOString(),
viewport,
url,
testName: process.env.PLAYWRIGHT_TEST_NAME,
};
const metadataPath = screenshotPath.replace('.png', '.json');
fs.writeFileSync(metadataPath, JSON.stringify(metadata, null, 2));
console.log(`📸 Element screenshot captured: ${filename}`);
return metadata;
}
/**
* Capture comparison screenshots (before/after)
*
* @param page - Playwright page object
* @param name - Base name for screenshots
* @param actionCallback - Action to perform between screenshots
*/
export async function captureComparison(
page: Page,
name: string,
actionCallback: () => Promise<void>
): Promise<{ before: ScreenshotMetadata; after: ScreenshotMetadata }> {
const before = await captureWithContext(page, `${name}-before`, 'State before action');
await actionCallback();
const after = await captureWithContext(page, `${name}-after`, 'State after action');
return { before, after };
}
/**
* Capture screenshots across multiple viewports
*
* @param page - Playwright page object
* @param name - Base name for screenshots
* @param viewports - Array of viewport configurations
*/
export async function captureViewports(
page: Page,
name: string,
viewports: Array<{ name: string; width: number; height: number }>
): Promise<ScreenshotMetadata[]> {
const screenshots: ScreenshotMetadata[] = [];
for (const viewport of viewports) {
await page.setViewportSize({ width: viewport.width, height: viewport.height });
// Wait for responsive changes to settle
await page.waitForTimeout(500);
const metadata = await captureWithContext(
page,
`${name}-${viewport.name}`,
`${viewport.width}x${viewport.height} viewport`
);
screenshots.push(metadata);
}
return screenshots;
}
/**
* Generate screenshot manifest
* Collects all screenshots and their metadata into a single manifest file
*/
export function generateManifest(): void {
const screenshotDir = path.join(process.cwd(), 'screenshots', 'current');
if (!fs.existsSync(screenshotDir)) {
console.log('No screenshots directory found');
return;
}
const files = fs.readdirSync(screenshotDir);
const metadataFiles = files.filter((f) => f.endsWith('.json'));
const manifest = metadataFiles.map((file) => {
const content = fs.readFileSync(path.join(screenshotDir, file), 'utf-8');
return JSON.parse(content);
});
const manifestPath = path.join(screenshotDir, 'manifest.json');
fs.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2));
console.log(`\n📋 Screenshot manifest generated: ${manifestPath}`);
console.log(` Total screenshots: ${manifest.length}`);
}
/**
* Compare screenshot with baseline
*
* @param currentPath - Path to current screenshot
* @param baselinePath - Path to baseline screenshot
* @param diffPath - Path to save diff image
* @param threshold - Difference threshold (0-1, default 0.2 = 20%)
*/
export async function compareWithBaseline(
currentPath: string,
baselinePath: string,
diffPath: string,
threshold: number = 0.2
): Promise<{ match: boolean; diffPercentage: number }> {
// Note: This requires pixelmatch or Playwright's built-in comparison
// For now, this is a placeholder showing the interface
console.log(`🔍 Comparing screenshots:`);
console.log(` Current: ${currentPath}`);
console.log(` Baseline: ${baselinePath}`);
// Implementation would use Playwright's toHaveScreenshot comparison
// or a library like pixelmatch for pixel-level comparison
return {
match: true,
diffPercentage: 0,
};
}

View File

@@ -0,0 +1,120 @@
import { test, expect } from '@playwright/test';
import { {{PAGE_OBJECT_CLASS}} } from '../pages/{{PAGE_OBJECT_FILE}}';
import { captureWithContext } from '../utils/screenshot-helper';
/**
* {{TEST_SUITE_NAME}}
*
* Tests: {{TEST_DESCRIPTION}}
* Generated: {{GENERATED_DATE}}
*/
test.describe('{{TEST_SUITE_NAME}}', () => {
let page: {{PAGE_OBJECT_CLASS}};
test.beforeEach(async ({ page: testPage }) => {
page = new {{PAGE_OBJECT_CLASS}}(testPage);
await page.goto();
// Capture initial page load
await captureWithContext(
testPage,
'{{TEST_SUITE_NAME_KEBAB}}-initial-load',
'Page loaded successfully'
);
});
{{#TESTS}}
test('{{TEST_NAME}}', async ({ page: testPage }) => {
// Arrange: {{ARRANGE_DESCRIPTION}}
{{#ARRANGE_STEPS}}
{{STEP}}
{{/ARRANGE_STEPS}}
// Capture pre-action state
await captureWithContext(
testPage,
'{{TEST_SUITE_NAME_KEBAB}}-{{TEST_NAME_KEBAB}}-before',
'Before {{ACTION_DESCRIPTION}}'
);
// Act: {{ACTION_DESCRIPTION}}
{{ACTION_CODE}}
// Capture post-action state
await captureWithContext(
testPage,
'{{TEST_SUITE_NAME_KEBAB}}-{{TEST_NAME_KEBAB}}-after',
'After {{ACTION_DESCRIPTION}}'
);
// Assert: {{ASSERT_DESCRIPTION}}
{{#ASSERTIONS}}
await expect({{SELECTOR}}).{{MATCHER}};
{{/ASSERTIONS}}
// Final state screenshot
await captureWithContext(
testPage,
'{{TEST_SUITE_NAME_KEBAB}}-{{TEST_NAME_KEBAB}}-final',
'{{FINAL_STATE_DESCRIPTION}}'
);
});
{{/TESTS}}
test('should not have accessibility violations', async ({ page: testPage }) => {
// Run accessibility audit
const AxeBuilder = (await import('@axe-core/playwright')).default;
const accessibilityScanResults = await new AxeBuilder({ page: testPage })
.withTags(['wcag2a', 'wcag2aa', 'wcag21a', 'wcag21aa'])
.analyze();
// Capture page with any violations highlighted
await captureWithContext(
testPage,
'{{TEST_SUITE_NAME_KEBAB}}-accessibility-check',
`Found ${accessibilityScanResults.violations.length} accessibility violations`
);
// Fail if there are critical violations
const criticalViolations = accessibilityScanResults.violations.filter(
(v) => v.impact === 'critical' || v.impact === 'serious'
);
expect(criticalViolations).toEqual([]);
});
test('should display correctly across viewports', async ({ page: testPage }) => {
const viewports = [
{ name: 'desktop', width: 1280, height: 720 },
{ name: 'tablet', width: 768, height: 1024 },
{ name: 'mobile', width: 375, height: 667 },
];
for (const viewport of viewports) {
await testPage.setViewportSize(viewport);
// Wait for any responsive changes to settle
await testPage.waitForTimeout(500);
// Capture screenshot for each viewport
await captureWithContext(
testPage,
`{{TEST_SUITE_NAME_KEBAB}}-responsive-${viewport.name}`,
`${viewport.width}x${viewport.height} viewport`
);
// Basic responsive checks
await expect(testPage.locator('body')).toBeVisible();
// No horizontal scroll on mobile/tablet
if (viewport.name !== 'desktop') {
const scrollWidth = await testPage.evaluate(() => document.body.scrollWidth);
const clientWidth = await testPage.evaluate(() => document.body.clientWidth);
expect(scrollWidth).toBeLessThanOrEqual(clientWidth + 1); // Allow 1px tolerance
}
}
});
});

View File

@@ -0,0 +1,72 @@
# Phase 1: Application Discovery & Version Detection
**Purpose**: Understand the application architecture, detect framework versions, and determine optimal Playwright setup
## Steps
### 1. Detect application type and versions
- Read package.json to identify frameworks (React, Vite, Next.js, Express, etc.)
- Check for common files (vite.config.ts, next.config.js, app.js, index.html)
- Identify build tools and dev server configuration
- Extract installed package versions for version-aware configuration
### 2. Consult version compatibility database
- Load `data/framework-versions.yaml` compatibility rules
- Match installed versions against version ranges using semver
- Determine appropriate templates for each framework version
- Identify potential breaking changes or incompatibilities
- **Example**: Tailwind v4 detected → use `@import` syntax, not `@tailwind`
### 3. Validate application access
- Check if dev server is running (ports 3000, 5173, 8080, etc.)
- If not running, determine how to start it (npm run dev, npm start, etc.)
- Verify application loads successfully
### 4. Map critical user journeys
- Identify key pages/routes from routing configuration
- Detect authentication flows
- Find form submissions and interactive elements
- Locate API integrations
## Version Detection Logic
```typescript
// Load compatibility database
const versionDb = parseYAML('data/framework-versions.yaml');
// Detect versions
const detectedVersions = {
tailwind: detectVersion(deps.tailwindcss, versionDb.tailwindcss),
react: detectVersion(deps.react, versionDb.react),
vite: detectVersion(deps.vite, versionDb.vite),
};
// Select appropriate templates
const templates = {
css: detectedVersions.tailwind?.templates.css || 'templates/css/vanilla.css',
postcss: detectedVersions.tailwind?.templates.postcss_config,
playwright: 'templates/playwright.config.template.ts',
};
```
## Output
Application profile with:
- Framework type and versions
- URLs and ports
- Test targets
- Selected templates
## Common Issues
**Unrecognized framework**
- Ask user to specify app type and dev server command manually
- Use generic static site configuration as fallback
**Missing package.json**
- Check for other indicators (index.html, etc.)
- Prompt user for application details
## Transition
Proceed to Phase 2 (Playwright Installation) with version-aware configuration

View File

@@ -0,0 +1,72 @@
# Phase 2: Playwright Installation & Setup
**Purpose**: Install Playwright and generate optimal configuration
## Steps
### 1. Install Playwright
```bash
npm init playwright@latest -- --yes
# Installs Playwright, test runners, and browsers (Chromium, Firefox, WebKit)
```
### 2. Generate playwright.config.ts
Configure based on app type:
- Set base URL (http://localhost:5173 for Vite, etc.)
- Configure viewport sizes:
- Desktop: 1280x720
- Tablet: 768x1024
- Mobile: 375x667
- Set screenshot directory: `screenshots/{test-name}/{timestamp}/`
- Enable trace on failure for debugging
- Configure retries (2 attempts) and timeout (30s)
### 3. Set up directory structure
```
tests/
├── setup/
│ └── global-setup.ts # Start dev server
├── pages/
│ └── *.page.ts # Page object models
├── specs/
│ └── *.spec.ts # Test specifications
└── utils/
└── screenshot-helper.ts
screenshots/
├── baselines/ # Reference images
├── current/ # Latest test run
└── diffs/ # Visual comparisons
```
### 4. Integrate with existing test setup
- Add playwright scripts to package.json
- Configure alongside Vitest/Jest (no conflicts)
- Set up TypeScript types for Playwright
## Output
Fully configured Playwright environment with version-appropriate templates
## Performance
~2-3 minutes for installation and setup (one-time)
## Common Issues
**Installation fails**
- Check Node version (>=16)
- Retry with --force
- Suggest manual installation if network issues
**Browser download fails**
- Check disk space (~500MB needed)
- Try installing specific browser: `npx playwright install chromium`
## Transition
Proceed to Phase 2.5 (Pre-flight Health Check)

View File

@@ -0,0 +1,113 @@
# Phase 2.5: Pre-flight Health Check
**Purpose**: Validate app loads correctly before running full test suite - catches configuration errors early
## Steps
### 1. Launch browser and attempt to load app
```typescript
const browser = await chromium.launch();
const page = await browser.newPage();
try {
const response = await page.goto(baseURL, { timeout: 30000 });
if (!response || !response.ok()) {
throw new Error(`App returned ${response?.status()}`);
}
} catch (error) {
// Analyze error and provide guidance
}
```
### 2. Monitor console for critical errors
- Listen for console errors during page load
- Collect all error messages for pattern analysis
- Wait 2-3 seconds to let errors surface
### 3. Analyze errors against known patterns
Load `data/error-patterns.yaml` error database and match against known patterns:
**Example patterns detected**:
- Tailwind v4 syntax mismatch: "Cannot apply unknown utility class"
- PostCSS plugin error: "Plugin tailwindcss not found"
- Missing dependencies: "Module not found"
### 4. Provide actionable diagnostics
```
❌ Pre-flight check failed: Critical errors detected
Issue: Tailwind CSS v4 syntax mismatch
Root cause: CSS file uses @tailwind directives but v4 requires @import
Fix:
1. Update src/index.css (or globals.css):
Change from: @tailwind base; @tailwind components; @tailwind utilities;
Change to: @import "tailwindcss";
2. Update postcss.config.js:
Change from: plugins: { tailwindcss: {} }
Change to: plugins: { '@tailwindcss/postcss': {} }
3. Restart dev server: npm run dev
Documentation: https://tailwindcss.com/docs/upgrade-guide
```
### 5. Auto-fix if possible, otherwise halt with guidance
- For known issues with clear fixes, offer to fix automatically
- For ambiguous issues, halt and require user intervention
- Prevent running 10+ tests that will all fail due to one config issue
## Error Pattern Analysis
```typescript
function analyzeErrors(consoleErrors) {
const errorPatterns = parseYAML('data/error-patterns.yaml');
const issues = [];
for (const error of consoleErrors) {
for (const [name, pattern] of Object.entries(errorPatterns.css_errors)) {
if (pattern.pattern.test(error) ||
pattern.alternative_patterns?.some(alt => alt.test(error))) {
issues.push({
name,
severity: pattern.severity,
diagnosis: pattern.diagnosis,
recovery_steps: pattern.recovery_steps,
documentation: pattern.documentation,
});
}
}
}
return {
critical: issues.filter(i => i.severity === 'critical'),
allIssues: issues,
};
}
```
## Benefits
- **Fast feedback**: 2-3 seconds vs 30+ seconds for full test suite
- **Clear guidance**: Specific fix steps, not generic "tests failed"
- **Prevents cascade failures**: One config error won't fail all 10 tests
- **Educational**: Explains what went wrong and why
## Output
Health check passed, or detailed error diagnostics with fix steps
## Performance
~2-5 seconds
## Transition
If health check passes, proceed to Phase 3 (Test Generation). If fails, provide fixes and halt.

View File

@@ -0,0 +1,68 @@
# Phase 3: Test Generation
**Purpose**: Create screenshot-enabled test suite covering critical workflows
## Steps
### 1. Generate page object models
- Create POM classes for each major page/component
- Define locators using best practices (getByRole, getByLabel, getByText)
- Add screenshot capture methods to each POM
### 2. Create test specifications
Generate tests for each critical user journey with screenshot capture at key points:
- Initial page load
- Before interaction (button click, form fill)
- After interaction
- Error states
- Success states
### 3. Add accessibility checks
- Integrate axe-core for automated a11y testing
- Capture accessibility violations in screenshots
- Generate accessibility reports
### 4. Set up screenshot helpers
```typescript
// templates/screenshot-helper.ts
export async function captureWithContext(
page: Page,
name: string,
context?: string
) {
const timestamp = new Date().toISOString();
const path = `screenshots/current/${name}-${timestamp}.png`;
await page.screenshot({ path, fullPage: true });
return { path, context, timestamp };
}
```
## Output
Complete test suite with screenshot automation
## Test Coverage
Aim for critical user journeys (80/20 rule):
- Core functionality tests
- Authentication flows
- Form submissions
- Key interactions
## Common Issues
**Too many tests generated**
- Focus on critical paths
- Prioritize user journeys over edge cases
**Locators not found**
- Use semantic locators (getByRole, getByLabel)
- Add test IDs as last resort
## Transition
Proceed to Phase 4 (Screenshot Capture & Execution)

View File

@@ -0,0 +1,65 @@
# Phase 4: Screenshot Capture & Execution
**Purpose**: Run tests and capture comprehensive visual data
## Steps
### 1. Execute test suite
```bash
npx playwright test --project=chromium --headed=false
```
### 2. Capture screenshots systematically
- Full-page screenshots for layout analysis
- Element-specific screenshots for component testing
- Different viewports (desktop, tablet, mobile)
- Different states (hover, focus, active, disabled)
### 3. Organize screenshot artifacts
- Group by test name
- Add timestamp and viewport metadata
- Generate index file for easy navigation
### 4. Handle failures gracefully
On test failure:
- Capture additional debug screenshots
- Save page HTML snapshot
- Record network activity
- Generate Playwright trace for replay
## Output
Organized screenshot directory with metadata:
```
screenshots/
├── current/
│ ├── home-page-load-2024-01-15T10-30-00.png
│ ├── home-page-after-click-2024-01-15T10-30-05.png
│ └── index.json (metadata)
└── ...
```
## Performance
~30-60 seconds for typical app (5-10 tests)
## Common Issues
**Screenshot capture fails**
- Increase timeout
- Add explicit waits
- Capture partial screenshot on failure
**Tests timeout**
- Check dev server is running
- Increase test timeout
- Add explicit wait conditions
## Transition
Proceed to Phase 5 (Visual Analysis)

View File

@@ -0,0 +1,67 @@
# Phase 5: Visual Analysis
**Purpose**: Use LLM vision capabilities to analyze screenshots and identify issues
## Steps
### 1. Batch screenshot analysis
Read all captured screenshots and ask LLM to identify:
- UI bugs (broken layouts, overlapping elements, cut-off text)
- Accessibility issues (low contrast, missing labels, improper heading hierarchy)
- Responsive problems (elements not scaling, overflow issues)
- Missing or misaligned elements
- Unexpected visual artifacts
### 2. Categorize findings
- **Critical**: App is broken/unusable (crashes, white screen, no content)
- **High**: Major UI bugs affecting core functionality
- **Medium**: Visual inconsistencies that impact UX
- **Low**: Minor alignment or styling issues
### 3. Generate issue descriptions
For each issue:
- Natural language description
- Screenshot reference with highlighted problem area
- Affected viewport/browser if relevant
- User impact assessment
## Output
Structured list of visual issues with severity ratings:
```markdown
## Visual Issues Found
### Critical (1)
- White screen on mobile viewport - App fails to render
### High (2)
- Button text cut off at 375px width
- Form labels overlap input fields
### Medium (3)
- Header alignment inconsistent
- ...
```
## Performance
~5-10 seconds per screenshot for LLM analysis
## Common Issues
**Analysis fails**
- Retry analysis
- Skip corrupted images
- Validate PNG format
**Too many false positives**
- Adjust analysis prompts
- Focus on critical issues first
## Transition
Proceed to Phase 6 (Regression Detection)

View File

@@ -0,0 +1,77 @@
# Phase 6: Regression Detection
**Purpose**: Compare current screenshots against baselines to detect changes
## Steps
### 1. Load baseline images
- Check if baselines exist in screenshots/baselines/
- If first run, current screenshots become baselines
- If baselines exist, proceed to comparison
### 2. Perform pixel-level comparison
```typescript
import { compareScreenshots } from 'playwright-core/lib/utils';
const diff = await compareScreenshots(
baselinePath,
currentPath,
diffPath,
{ threshold: 0.2 } // 20% difference threshold
);
```
### 3. Generate visual diff reports
- Create side-by-side comparison images
- Highlight changed regions in red
- Calculate difference percentage
- Classify changes:
- **Expected**: Intentional changes (new features, fixes)
- **Suspicious**: Unintended changes requiring review
- **Critical**: Major regressions (broken features)
### 4. Update baselines if approved
Ask user: "Accept these changes as new baseline?"
- If yes, copy current → baselines
- If no, flag as regressions needing fixes
## Output
Visual regression report with diff images:
```markdown
## Regression Report
### Changed Screenshots (3)
| Screenshot | Diff % | Classification |
|------------|--------|----------------|
| home-page | 5% | Expected |
| form-page | 25% | Suspicious |
| mobile-nav | 45% | Critical |
See screenshots/diffs/ for visual comparisons.
```
## Performance
~1-2 seconds per image pair
## Common Issues
**No baselines exist**
- Current screenshots become baselines automatically
- Message: "No baselines found. Current screenshots saved as baselines."
**False positive diffs**
- Adjust threshold (default 20%)
- Ignore dynamic content areas
- Use stable test data
## Transition
Proceed to Phase 7 (Fix Recommendation Generation)

View File

@@ -0,0 +1,72 @@
# Phase 7: Fix Recommendation Generation
**Purpose**: Map visual issues to source code and generate actionable fixes
## Steps
### 1. Correlate issues with source code
- Use test file metadata to identify component under test
- Search codebase for relevant files (component, styles, layout)
- Match visual issues to likely code locations
### 2. Generate fix recommendations
For each issue, provide:
- **Issue description**: Natural language explanation
- **File location**: `src/components/Button.tsx:45`
- **Current code**: Snippet showing problematic code
- **Recommended fix**: Specific code change
- **Reasoning**: Why this fix addresses the issue
### 3. Prioritize fixes
- Sort by severity (critical → low)
- Group related fixes (same component, same file)
- Estimate complexity (simple CSS tweak vs. complex refactor)
### 4. Format as actionable report
```markdown
# Visual Bug Fix Recommendations
## Critical Issues (2)
### 1. Button text cut off on mobile viewport
**Location**: `src/components/Button.tsx:45`
**Screenshot**: `screenshots/current/button-mobile-1234.png`
**Current Code**:
```tsx
<button className="px-4 py-2 text-lg">
{children}
</button>
```
**Recommended Fix**:
```tsx
<button className="px-4 py-2 text-sm sm:text-lg truncate max-w-full">
{children}
</button>
```
**Reasoning**: Fixed width and font size cause overflow on narrow viewports. Added responsive text sizing and truncation.
```
## Output
fix-recommendations.md with prioritized, actionable fixes
## Common Issues
**Can't find source file**
- Ask user for component location
- Search by component name patterns
**Multiple possible fixes**
- Present options with trade-offs
- Recommend simplest solution
## Transition
Proceed to Phase 8 (Test Suite Export)

View File

@@ -0,0 +1,82 @@
# Phase 8: Test Suite Export
**Purpose**: Provide production-ready test suite for ongoing use
## Steps
### 1. Export test files
- Copy generated tests to project's tests/ directory
- Ensure proper TypeScript types and imports
- Add comments explaining test purpose
### 2. Create README documentation
```markdown
# Playwright E2E Test Suite
## Running Tests
```bash
npm run test:e2e # Run all e2e tests
npm run test:e2e:headed # Run with browser UI
npm run test:e2e:debug # Run with Playwright Inspector
```
## Screenshot Management
- Baselines: `screenshots/baselines/`
- Current: `screenshots/current/`
- Diffs: `screenshots/diffs/`
## Updating Baselines
```bash
npm run test:e2e:update-snapshots
```
```
### 3. Add npm scripts
```json
{
"scripts": {
"test:e2e": "playwright test",
"test:e2e:headed": "playwright test --headed",
"test:e2e:debug": "playwright test --debug",
"test:e2e:update-snapshots": "playwright test --update-snapshots"
}
}
```
### 4. Document CI/CD integration
- Provide GitHub Actions workflow example
- Explain screenshot artifact storage
- Show how to update baselines in CI
- Configure Playwright HTML reporter for CI
See `reference/ci-cd-integration.md` for complete examples.
## Output
Complete, documented test suite ready for development workflow:
- Tests in `tests/` directory
- README with usage instructions
- npm scripts configured
- CI/CD documentation
## Common Issues
**Tests don't run after export**
- Check TypeScript types
- Verify imports are correct
- Ensure Playwright is in dependencies
**CI/CD integration issues**
- See `reference/ci-cd-integration.md`
- Check browser installation in CI
## Success Criteria
- [ ] Test suite exported to project
- [ ] All tests executable via npm run test:e2e
- [ ] README includes usage instructions
- [ ] CI/CD guidance documented