AgentSkillsCN

Visual Qa Testing

采用全面的屏幕截图捕捉、对比与验证技术,对多个状态与断点进行视觉QA测试。

SKILL.md
--- frontmatter
description: Visual QA testing with comprehensive screenshot capture, comparison, and verification across multiple states and breakpoints

Visual QA Testing Skill

Use this skill to perform visual quality assurance by capturing screenshots of UI states, comparing layouts, and verifying visual correctness across different scenarios.

When to Use

  • After UI implementation
  • After CSS/styling changes
  • Before deployments
  • For regression testing
  • When user reports visual bugs
  • During comprehensive QA validation

What This Skill Does

1. State-Based Screenshot Capture

Captures screenshots for ALL UI states:

  • Initial/Empty State - No data loaded
  • Loading State - Spinners, skeletons active
  • Data Loaded State - Content displayed
  • Error State - Error messages shown
  • Validation State - Form validation errors
  • Success State - Success messages
  • Hover State - Interactive element hover
  • Focus State - Keyboard focus indicators
  • Active/Selected State - Selected items
  • Disabled State - Disabled elements

2. Multi-Breakpoint Screenshot Matrix

For EACH state, capture at ALL breakpoints:

  • Mobile Portrait (375px)
  • Mobile Large (414px)
  • Tablet (768px)
  • Desktop (1280px)
  • Large Desktop (1920px)

Result: Comprehensive screenshot matrix (10 states × 5 breakpoints = 50 screenshots per page)

3. Component-Level Capture

Screenshot specific components:

  • Forms (empty, filled, with errors)
  • Modals/Dialogs
  • Navigation menus
  • Data tables/grids
  • Cards and lists
  • Buttons (all variants)
  • Input fields (all states)

4. User Workflow Capture

Screenshot each step of user journeys:

  • Login flow
  • Create/Edit/Delete workflows
  • Multi-step forms
  • Checkout processes
  • Onboarding flows

5. Visual Regression Detection

Compare screenshots to identify:

  • Layout shifts
  • Broken layouts
  • Missing styles
  • Color inconsistencies
  • Font rendering issues
  • Icon/image problems

Available Playwright MCP Tools

  • mcp__playwright__browser_navigate - Navigate to pages
  • mcp__playwright__browser_resize - Change viewport CRITICAL
  • mcp__playwright__browser_take_screenshot - Capture screenshots PRIMARY TOOL
  • mcp__playwright__browser_click - Trigger interactions
  • mcp__playwright__browser_type - Fill inputs
  • mcp__playwright__browser_fill_form - Fill forms
  • mcp__playwright__browser_evaluate - Manipulate DOM/trigger states
  • mcp__playwright__browser_wait_for - Wait for state changes
  • mcp__playwright__browser_snapshot - Get accessibility tree

Testing Workflow

Step 1: Initialize Test Session

javascript
const testSession = {
  feature: 'Schedule Calendar',
  url: 'http://172.18.0.5:80/schedules',
  breakpoints: [375, 414, 768, 1280, 1920],
  states: ['empty', 'loading', 'loaded', 'error'],
  screenshots: []
}

// Navigate
mcp__playwright__browser_navigate(testSession.url)

Step 2: Capture Empty State

javascript
// For each breakpoint
for (const width of testSession.breakpoints) {
  // Resize
  mcp__playwright__browser_resize(width, 800)
  await mcp__playwright__browser_wait_for(timeout: 300)

  // Screenshot
  await mcp__playwright__browser_take_screenshot(
    filename: `empty_state_${width}px.png`,
    fullPage: true
  )

  testSession.screenshots.push({
    state: 'empty',
    breakpoint: width,
    filename: `empty_state_${width}px.png`
  })
}

Step 3: Capture Loading State

javascript
// Trigger loading state (if possible)
await mcp__playwright__browser_evaluate(`
  // Show loading skeletons or spinners
  document.querySelector('.loading-indicator')?.classList.add('visible')
  // Or trigger a slow API call
`)

// Capture at all breakpoints
for (const width of testSession.breakpoints) {
  mcp__playwright__browser_resize(width, 800)
  await mcp__playwright__browser_wait_for(timeout: 300)

  await mcp__playwright__browser_take_screenshot(
    filename: `loading_state_${width}px.png`,
    fullPage: true
  )
}

Step 4: Capture Data Loaded State

javascript
// Wait for data to load
await mcp__playwright__browser_wait_for(
  selector: '.schedule-calendar',
  state: 'visible'
)

// Capture at all breakpoints
for (const width of testSession.breakpoints) {
  mcp__playwright__browser_resize(width, 800)
  await mcp__playwright__browser_wait_for(timeout: 300)

  await mcp__playwright__browser_take_screenshot(
    filename: `loaded_state_${width}px.png`,
    fullPage: true
  )

  console.log(`✅ Captured loaded state at ${width}px`)
}

Step 5: Capture Error State

javascript
// Trigger error state
// Option 1: Fill form with invalid data
await mcp__playwright__browser_fill_form({
  'employee': '',  // Required field left empty
  'date': 'invalid-date'
})
await mcp__playwright__browser_click(selector: 'button[type="submit"]')

// Wait for error to appear
await mcp__playwright__browser_wait_for(
  selector: '.error-message',
  state: 'visible'
)

// Capture at all breakpoints
for (const width of testSession.breakpoints) {
  mcp__playwright__browser_resize(width, 800)
  await mcp__playwright__browser_wait_for(timeout: 300)

  await mcp__playwright__browser_take_screenshot(
    filename: `error_state_${width}px.png`,
    fullPage: true
  )

  console.log(`✅ Captured error state at ${width}px`)
}

Step 6: Capture Form Validation States

javascript
// Click into each input field to trigger validation
const formFields = ['employee', 'date', 'startTime', 'endTime']

for (const field of formFields) {
  // Click field
  await mcp__playwright__browser_click(selector: `[name="${field}"]`)

  // Click outside to blur (trigger validation)
  await mcp__playwright__browser_click(selector: 'body')

  // Wait for validation message
  await mcp__playwright__browser_wait_for(timeout: 200)

  // Screenshot at mobile only (375px)
  mcp__playwright__browser_resize(375, 667)
  await mcp__playwright__browser_take_screenshot(
    filename: `validation_${field}_375px.png`
  )

  // Screenshot at desktop (1280px)
  mcp__playwright__browser_resize(1280, 720)
  await mcp__playwright__browser_take_screenshot(
    filename: `validation_${field}_1280px.png`
  )
}

Step 7: Capture Interactive States

javascript
// Hover state (desktop only)
mcp__playwright__browser_resize(1280, 720)

// Find interactive elements
const interactiveElements = await mcp__playwright__browser_evaluate(`
  Array.from(document.querySelectorAll('button, a, .clickable')).map(el => ({
    selector: el.className || el.tagName,
    text: el.textContent?.substring(0, 20)
  }))
`)

for (const element of interactiveElements.slice(0, 5)) {  // First 5 elements
  // Hover
  await mcp__playwright__browser_evaluate(`
    const el = document.querySelector('${element.selector}')
    el.dispatchEvent(new MouseEvent('mouseenter', { bubbles: true }))
  `)

  await mcp__playwright__browser_take_screenshot(
    filename: `hover_${element.selector}_1280px.png`
  )

  console.log(`✅ Captured hover state: ${element.text}`)
}

// Focus state (keyboard navigation)
for (const element of interactiveElements.slice(0, 5)) {
  // Focus
  await mcp__playwright__browser_evaluate(`
    document.querySelector('${element.selector}')?.focus()
  `)

  await mcp__playwright__browser_take_screenshot(
    filename: `focus_${element.selector}_1280px.png`
  )

  console.log(`✅ Captured focus state: ${element.text}`)
}

Step 8: Capture Modal/Dialog States

javascript
// Open modal
await mcp__playwright__browser_click(selector: 'button[data-testid="create-schedule"]')

// Wait for modal to appear
await mcp__playwright__browser_wait_for(
  selector: '[role="dialog"]',
  state: 'visible'
)

// Capture modal at all breakpoints
for (const width of [375, 768, 1280]) {
  mcp__playwright__browser_resize(width, 800)
  await mcp__playwright__browser_wait_for(timeout: 300)

  await mcp__playwright__browser_take_screenshot(
    filename: `modal_create_${width}px.png`,
    fullPage: true
  )

  console.log(`✅ Captured modal at ${width}px`)
}

// Close modal
await mcp__playwright__browser_click(selector: '[aria-label="Close"]')

Step 9: Capture Scroll States

javascript
// Long page content - capture top, middle, bottom
const positions = ['top', 'middle', 'bottom']

for (const width of [375, 1280]) {
  mcp__playwright__browser_resize(width, 800)

  // Top
  await mcp__playwright__browser_evaluate(`window.scrollTo(0, 0)`)
  await mcp__playwright__browser_wait_for(timeout: 300)
  await mcp__playwright__browser_take_screenshot(
    filename: `scroll_top_${width}px.png`,
    fullPage: false  // Viewport only
  )

  // Middle
  await mcp__playwright__browser_evaluate(`
    window.scrollTo(0, document.documentElement.scrollHeight / 2)
  `)
  await mcp__playwright__browser_wait_for(timeout: 300)
  await mcp__playwright__browser_take_screenshot(
    filename: `scroll_middle_${width}px.png`,
    fullPage: false
  )

  // Bottom
  await mcp__playwright__browser_evaluate(`
    window.scrollTo(0, document.documentElement.scrollHeight)
  `)
  await mcp__playwright__browser_wait_for(timeout: 300)
  await mcp__playwright__browser_take_screenshot(
    filename: `scroll_bottom_${width}px.png`,
    fullPage: false
  )

  console.log(`✅ Captured scroll positions at ${width}px`)
}

Step 10: Generate Visual QA Report

javascript
const report = {
  feature: testSession.feature,
  test_date: new Date().toISOString(),
  total_screenshots: testSession.screenshots.length,
  breakpoints_tested: testSession.breakpoints,
  states_captured: testSession.states,
  screenshots: testSession.screenshots,
  visual_issues: [],
  status: 'PASS'
}

console.log('=== Visual QA Test Report ===')
console.log(`Feature: ${report.feature}`)
console.log(`Screenshots: ${report.total_screenshots}`)
console.log(`Breakpoints: ${report.breakpoints_tested.join(', ')}`)
console.log(`States: ${report.states_captured.join(', ')}`)

Docker IP Configuration

CRITICAL: Always use Docker container IP, not localhost.

Get Container IP:

bash
docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' {container}_ui

Use in Tests:

code
# Wrong:
http://localhost:3003

# Correct:
http://172.18.0.5:80

Screenshot Naming Convention

Use consistent naming for easy organization:

code
{state}_{component}_{breakpoint}px_{variant}.png

Examples:
- empty_state_calendar_375px.png
- loading_state_table_1280px.png
- error_state_form_768px_validation.png
- hover_state_button_1280px_primary.png
- focus_state_input_375px_email.png

Expected Inputs

  • URL to test (Docker IP)
  • Feature/component name
  • Specific states to capture
  • Breakpoints to test
  • User workflows to capture

Deliverables

  • Complete screenshot matrix (all states × all breakpoints)
  • Organized screenshot files with descriptive names
  • Visual QA report listing all captures
  • List of visual issues detected
  • Comparison notes (if baseline exists)
  • Recommendations for fixes

Validation Checklist

Layout

  • No overlapping elements
  • Proper spacing and alignment
  • Grid/flexbox layouts correct
  • Responsive breakpoints work
  • No cut-off content

Typography

  • Font sizes appropriate (min 16px mobile)
  • Line height readable
  • Text colors have sufficient contrast (WCAG AA)
  • No text overflow
  • Headings hierarchy correct

Colors & Theming

  • Colors match design system
  • Theme consistency throughout
  • Proper contrast ratios
  • Brand colors used correctly
  • Error/warning/success colors distinct

Interactive Elements

  • Hover states visible
  • Focus indicators present
  • Active states clear
  • Disabled states distinguishable
  • Click feedback appropriate

Forms

  • Input fields properly sized
  • Labels aligned correctly
  • Validation messages clear
  • Error states highlighted
  • Success feedback shown

Images & Icons

  • Images scale correctly
  • No broken images
  • Icons sized appropriately
  • Alt text present (check snapshot)
  • Aspect ratios maintained

Modals & Overlays

  • Centered on screen
  • Backdrop visible
  • Close button accessible
  • Scrollable if content long
  • Mobile-friendly size

Example: Complete Visual QA Flow

javascript
// Test configuration
const visualQATest = {
  feature: 'Schedule Calendar Page',
  url: 'http://172.18.0.5:80/schedules',
  container: 'scheduling_ui',
  breakpoints: [375, 768, 1280],
  states: [
    'empty',
    'loading',
    'loaded_with_data',
    'create_modal',
    'edit_modal',
    'delete_confirmation',
    'validation_errors',
    'success_message',
    'error_message'
  ]
}

// Step 1: Navigate
mcp__playwright__browser_navigate(visualQATest.url)

// Step 2: Empty state
console.log('Capturing empty state...')
for (const bp of visualQATest.breakpoints) {
  mcp__playwright__browser_resize(bp, 800)
  await mcp__playwright__browser_wait_for(timeout: 300)
  await mcp__playwright__browser_take_screenshot(
    filename: `empty_${bp}px.png`,
    fullPage: true
  )
}

// Step 3: Loading state (if available)
console.log('Capturing loading state...')
// Trigger loading...
for (const bp of visualQATest.breakpoints) {
  mcp__playwright__browser_resize(bp, 800)
  await mcp__playwright__browser_take_screenshot(
    filename: `loading_${bp}px.png`,
    fullPage: true
  )
}

// Step 4: Loaded with data
console.log('Capturing loaded state...')
await mcp__playwright__browser_wait_for(selector: '.schedule-item', state: 'visible')
for (const bp of visualQATest.breakpoints) {
  mcp__playwright__browser_resize(bp, 800)
  await mcp__playwright__browser_take_screenshot(
    filename: `loaded_${bp}px.png`,
    fullPage: true
  )
}

// Step 5: Create modal
console.log('Capturing create modal...')
await mcp__playwright__browser_click(selector: 'button:has-text("Create Schedule")')
await mcp__playwright__browser_wait_for(selector: '[role="dialog"]', state: 'visible')
for (const bp of visualQATest.breakpoints) {
  mcp__playwright__browser_resize(bp, 800)
  await mcp__playwright__browser_take_screenshot(
    filename: `create_modal_${bp}px.png`,
    fullPage: true
  )
}

// Step 6: Validation errors
console.log('Capturing validation errors...')
await mcp__playwright__browser_click(selector: 'button[type="submit"]')
await mcp__playwright__browser_wait_for(selector: '.error-message', state: 'visible')
for (const bp of visualQATest.breakpoints) {
  mcp__playwright__browser_resize(bp, 800)
  await mcp__playwright__browser_take_screenshot(
    filename: `validation_errors_${bp}px.png`,
    fullPage: true
  )
}

// Step 7: Success state (fill form correctly)
console.log('Capturing success state...')
await mcp__playwright__browser_fill_form({
  'employee': 'John Doe',
  'date': '2025-01-20',
  'startTime': '09:00',
  'endTime': '17:00'
})
await mcp__playwright__browser_click(selector: 'button[type="submit"]')
await mcp__playwright__browser_wait_for(selector: '.success-message', state: 'visible')
for (const bp of visualQATest.breakpoints) {
  mcp__playwright__browser_resize(bp, 800)
  await mcp__playwright__browser_take_screenshot(
    filename: `success_message_${bp}px.png`,
    fullPage: true
  )
}

// Close browser
mcp__playwright__browser_close()

console.log('✅ Visual QA testing complete!')
console.log(`Total screenshots captured: ${visualQATest.states.length * visualQATest.breakpoints.length}`)

Integration with QA Agents

The QA Frontend Engineer agent should use this skill to:

  1. Capture screenshots of ALL UI states
  2. Test at multiple breakpoints (not just one)
  3. Verify visual consistency
  4. Check interactive element states
  5. Document visual issues with screenshots
  6. Provide evidence of testing

Common Visual Issues Detected

Layout Breaks

  • Overlapping elements on mobile
  • Text overflow outside containers
  • Fixed widths breaking responsive design
  • Grid/flexbox misconfiguration

Color Issues

  • Insufficient contrast (< 4.5:1)
  • Inconsistent brand colors
  • Wrong theme applied
  • Missing hover/focus colors

Typography Problems

  • Font too small on mobile (< 16px)
  • Line height too tight
  • Text not wrapping
  • Font weight inconsistent

Interactive State Issues

  • No hover indication
  • Missing focus outline
  • Disabled state not clear
  • Active state same as inactive

Responsive Issues

  • Desktop layout on mobile
  • Mobile menu not appearing
  • Touch targets too small
  • Horizontal scrolling

This skill provides comprehensive visual documentation and verification that catches visual bugs, layout issues, and responsive design problems through systematic screenshot capture and analysis.