# Playwright MCP Integration

## Overview
DesignFast通过Model Context Protocol (MCP)集成Playwright进行自动化测试。这允许AI代理直接调用Playwright功能来测试生成的原型。

## MCP Server Setup

### Installation

```bash
# Install Playwright MCP server (if using standalone MCP server)
npm install @modelcontextprotocol/server-playwright
```

### Configuration

```json
{
  "mcpServers": {
    "playwright": {
      "command": "npx",
      "args": [
        "-y",
        "@modelcontextprotocol/server-playwright"
      ]
    }
  }
}
```

## Available Tools

### 1. playwright_navigate
Navigate to a URL

```json
{
  "tool": "playwright_navigate",
  "parameters": {
    "url": "file:///d:/AICODE/DesignFast/DesignFast/workspace/prototypes/proto-001.html"
  }
}
```

### 2. playwright_screenshot
Take a screenshot

```json
{
  "tool": "playwright_screenshot",
  "parameters": {
    "name": "homepage-mobile",
    "width": 375,
    "height": 667
  }
}
```

### 3. playwright_click
Click an element

```json
{
  "tool": "playwright_click",
  "parameters": {
    "selector": "button.add-to-cart"
  }
}
```

### 4. playwright_fill
Fill a form field

```json
{
  "tool": "playwright_fill",
  "parameters": {
    "selector": "input#email",
    "value": "user@example.com"
  }
}
```

### 5. playwright_evaluate
Execute JavaScript in page context

```json
{
  "tool": "playwright_evaluate",
  "parameters": {
    "script": "document.querySelectorAll('img').length"
  }
}
```

## Integration Patterns

### Test Scenario Execution

```javascript
// Playwright Integrator Agent execution flow

async function executeTestScenario(scenario) {
  const mcpClient = await initializeMCPClient();
  
  // 1. Navigate to prototype
  await mcpClient.callTool('playwright_navigate', {
    url: scenario.prototypeUrl
  });
  
  // 2. Execute test steps
  for (const step of scenario.testCases) {
    switch (step.action) {
      case 'click':
        await mcpClient.callTool('playwright_click', {
          selector: step.selector
        });
        break;
        
      case 'fill':
        await mcpClient.callTool('playwright_fill', {
          selector: step.selector,
          value: step.value
        });
        break;
        
      case 'screenshot':
        await mcpClient.callTool('playwright_screenshot', {
          name: step.name,
          width: step.width,
          height: step.height
        });
        break;
        
      case 'evaluate':
        const result = await mcpClient.callTool('playwright_evaluate', {
          script: step.script
        });
        // Validate result
        break;
    }
  }
  
  // 3. Collect results
  return {
    status: 'completed',
    results: testResults
  };
}
```

### Accessibility Testing via MCP

```javascript
async function runAccessibilityTests(prototypeUrl) {
  const mcpClient = await initializeMCPClient();
  
  // Navigate to prototype
  await mcpClient.callTool('playwright_navigate', { url: prototypeUrl });
  
  // Inject axe-core
  await mcpClient.callTool('playwright_evaluate', {
    script: `
      const script = document.createElement('script');
      script.src = 'https://cdn.jsdelivr.net/npm/axe-core@4.7.0/axe.min.js';
      document.head.appendChild(script);
    `
  });
  
  // Wait for axe to load
  await new Promise(resolve => setTimeout(resolve, 1000));
  
  // Run accessibility scan
  const violations = await mcpClient.callTool('playwright_evaluate', {
    script: `
      new Promise((resolve) => {
        axe.run(document, {
          runOnly: ['wcag2a', 'wcag2aa', 'wcag21a', 'wcag21aa']
        }).then(results => {
          resolve(results.violations);
        });
      })
    `
  });
  
  return {
    passed: violations.length === 0,
    violations: violations,
    report: generateAccessibilityReport(violations)
  };
}
```

### Responsive Testing via MCP

```javascript
async function runResponsiveTests(prototypeUrl) {
  const mcpClient = await initializeMCPClient();
  
  const viewports = [
    { name: 'mobile', width: 375, height: 667 },
    { name: 'tablet', width: 768, height: 1024 },
    { name: 'desktop', width: 1920, height: 1080 }
  ];
  
  const screenshots = [];
  
  for (const viewport of viewports) {
    // Set viewport
    await mcpClient.callTool('playwright_evaluate', {
      script: `window.resizeTo(${viewport.width}, ${viewport.height})`
    });
    
    // Navigate
    await mcpClient.callTool('playwright_navigate', { url: prototypeUrl });
    
    // Take screenshot
    const screenshot = await mcpClient.callTool('playwright_screenshot', {
      name: `${viewport.name}-view`,
      width: viewport.width,
      height: viewport.height
    });
    
    screenshots.push({
      viewport: viewport.name,
      screenshot: screenshot,
      path: `workspace/reports/${viewport.name}-view.png`
    });
    
    // Check for horizontal scroll
    const hasHScroll = await mcpClient.callTool('playwright_evaluate', {
      script: `document.documentElement.scrollWidth > document.documentElement.clientWidth`
    });
    
    if (hasHScroll) {
      console.warn(`⚠️  ${viewport.name}: Horizontal scrollbar detected`);
    }
  }
  
  return {
    passed: true,
    screenshots: screenshots,
    report: generateResponsiveReport(screenshots)
  };
}
```

### Performance Testing via MCP

```javascript
async function runPerformanceTests(prototypeUrl) {
  const mcpClient = await initializeMCPClient();
  
  const startTime = Date.now();
  
  // Navigate and measure load time
  await mcpClient.callTool('playwright_navigate', { url: prototypeUrl });
  
  const loadTime = Date.now() - startTime;
  
  // Get performance metrics
  const metrics = await mcpClient.callTool('playwright_evaluate', {
    script: `
      JSON.stringify({
        fcp: performance.getEntriesByType('paint')
          .find(e => e.name === 'first-contentful-paint')?.startTime || 0,
        resources: performance.getEntriesByType('resource').length,
        totalSize: performance.getEntriesByType('resource')
          .reduce((sum, r) => sum + (r.transferSize || 0), 0)
      })
    `
  });
  
  const parsed = JSON.parse(metrics);
  
  return {
    passed: loadTime < 2000 && parsed.fcp < 1000 && parsed.totalSize < 3 * 1024 * 1024,
    loadTime: loadTime,
    fcp: parsed.fcp,
    resourceCount: parsed.resources,
    totalSize: parsed.totalSize,
    budgets: {
      loadTime: { actual: loadTime, target: 2000, unit: 'ms' },
      fcp: { actual: parsed.fcp, target: 1000, unit: 'ms' },
      totalSize: { actual: parsed.totalSize, target: 3 * 1024 * 1024, unit: 'bytes' }
    }
  };
}
```

## Test Generation

### Generate Playwright Test File

```javascript
function generatePlaywrightTest(testScenario) {
  const testCode = `
import { test, expect } from '@playwright/test';

test.describe('${testScenario.name}', () => {
  test.beforeEach(async ({ page }) => {
    await page.goto('${testScenario.prototypeUrl}');
  });

  ${testScenario.testCases.map(testCase => `
  test('${testCase.description}', async ({ page }) => {
    ${testCase.steps.map(step => {
      switch (step.action) {
        case 'click':
          return `await page.locator('${step.selector}').click();`;
        case 'fill':
          return `await page.locator('${step.selector}').fill('${step.value}');`;
        case 'expect-visible':
          return `await expect(page.locator('${step.selector}')).toBeVisible();`;
        case 'expect-text':
          return `await expect(page.locator('${step.selector}')).toContainText('${step.text}');`;
        default:
          return `// ${step.action}`;
      }
    }).join('\n    ')}
  });
  `).join('\n')}
});
`;

  return testCode;
}
```

### Example Generated Test

```javascript
import { test, expect } from '@playwright/test';

test.describe('产品浏览功能测试', () => {
  test.beforeEach(async ({ page }) => {
    await page.goto('file:///workspace/prototypes/ecommerce.html');
  });

  test('用户应该能够查看产品列表', async ({ page }) => {
    await expect(page.locator('.product-card')).toHaveCount(12);
    await expect(page.locator('h1')).toContainText('产品列表');
  });

  test('用户应该能够点击产品查看详情', async ({ page }) => {
    await page.locator('.product-card').first().click();
    await expect(page.locator('.product-details')).toBeVisible();
  });

  test('用户应该能够添加产品到购物车', async ({ page }) => {
    await page.locator('.add-to-cart').first().click();
    await expect(page.locator('.cart-notification')).toBeVisible();
    await expect(page.locator('.cart-count')).toContainText('1');
  });
});
```

## Error Handling

```javascript
async function executeMCPTool(tool, params) {
  try {
    const result = await mcpClient.callTool(tool, params);
    return { success: true, data: result };
  } catch (error) {
    console.error(`MCP tool ${tool} failed:`, error.message);
    
    // Retry logic
    if (error.code === 'TIMEOUT') {
      console.log('Retrying after timeout...');
      await new Promise(resolve => setTimeout(resolve, 1000));
      return executeMCPTool(tool, params);
    }
    
    return { 
      success: false, 
      error: error.message,
      suggestions: getErrorSuggestions(error)
    };
  }
}

function getErrorSuggestions(error) {
  const suggestions = [];
  
  if (error.message.includes('selector')) {
    suggestions.push('检查CSS选择器是否正确');
    suggestions.push('确保元素在页面中存在');
  }
  
  if (error.message.includes('timeout')) {
    suggestions.push('增加超时时间');
    suggestions.push('检查页面是否正确加载');
  }
  
  return suggestions;
}
```

## Performance Requirements

- MCP connection: < 1秒
- Single test execution: < 30秒
- Full test suite (all types): < 3分钟
- Screenshot generation: < 2秒 per viewport

## Constitutional Compliance

✅ **Playwright MCP Integration**: Direct MCP tool usage for testing
✅ **Accessibility Testing**: Automated WCAG compliance checks
✅ **Responsive Testing**: Multi-viewport validation
✅ **Performance Testing**: Load time and resource budgets
✅ **File-Based Results**: Test reports saved to workspace/reports/
✅ **Quality Gates**: Tests must pass before deployment

## PowerShell Integration

```powershell
# run-playwright-tests.ps1
param(
    [string]$PrototypePath,
    [string]$TestType = "all"
)

$mcpConfig = @{
    server = "playwright"
    url = "file:///$PrototypePath"
}

# Call Node.js script with MCP client
node -e "
const { executeTests } = require('./src/lib/playwright-mcp-client.js');
executeTests('$TestType', '$($mcpConfig.url)').then(results => {
    console.log(JSON.stringify(results));
});
" | ConvertFrom-Json

Write-Host "✓ 测试完成" -ForegroundColor Green
```
