---
name: validating
description: Linting, type-checking, and visual regression
metadata:
  tags: validation, lint, typescript, screenshots
---

# Validation Checklist

## Goals

- Catch type errors early
- Enforce formatting and lint rules
- Confirm visual parity with screenshots

## Recommended Commands

- `pnpm lint` or `npm run lint`
- `pnpm tsc --noEmit` or `npm run typecheck`
- `pnpm format` or `npm run format` if configured

## Screenshot Comparison Workflow

Use the built-in `compareScreenshots` utility to validate visual accuracy:

```ts
import {renderPage, compareScreenshots, saveScreenshot} from './plugin';

// Step 1: Capture original website
const original = await renderPage({
  url: 'https://example.com',
  viewport: {width: 1440, height: 900},
  extraWaitMs: 3000,
});
await saveScreenshot(original.screenshot, './validation/original.png');

// Step 2: Start your Next.js dev server
// npm run dev (runs on http://localhost:3000)

// Step 3: Capture clone
const clone = await renderPage({
  url: 'http://localhost:3000',
  viewport: {width: 1440, height: 900},
  extraWaitMs: 3000,
});
await saveScreenshot(clone.screenshot, './validation/clone.png');

// Step 4: Compare
const result = await compareScreenshots(
  original.screenshot,
  clone.screenshot,
  './validation/diff.png'
);

console.log(`Visual similarity: ${(result.similarity * 100).toFixed(2)}%`);
console.log(`Different pixels: ${result.diffPixels} / ${result.totalPixels}`);

// Step 5: Interpret results
if (result.similarity >= 0.95) {
  console.log('✓ Clone is pixel-perfect (>95% match)');
} else if (result.similarity >= 0.85) {
  console.log('⚠ Clone is close but needs refinement');
  console.log('  Check diff.png to identify differences');
} else {
  console.log('✗ Clone has significant visual differences');
  console.log('  Review diff.png and re-examine components');
}
```

### Requirements

Screenshot comparison requires optional packages:

```bash
npm install pixelmatch pngjs
```

### Similarity Thresholds

| Score | Rating | Action |
|-------|--------|--------|
| > 98% | Excellent | Ship it |
| 95-98% | Good | Minor tweaks needed |
| 85-95% | Fair | Review fonts, colors, spacing |
| < 85% | Poor | Major layout/style issues |

### Common Issues Detected

1. **Font rendering differences** - Ensure Google Fonts are imported correctly
2. **Color mismatches** - Use exact hex values, not Tailwind approximations
3. **Spacing differences** - Use exact pixel values with `[value]` syntax
4. **Missing images** - Check that assets downloaded to `/public/assets/`
5. **Layout shifts** - Verify absolute positioning and z-index values

## Validation Output

Record the results in a summary JSON:

```json
{
  "eslint": { "passed": true, "errors": [] },
  "typescript": { "passed": true, "errors": [] },
  "format": { "applied": false },
  "screenshot": { 
    "similarity": 0.985,
    "diffPixels": 1250,
    "totalPixels": 1296000,
    "diffImagePath": "./validation/diff.png"
  }
}
```

## Full Validation Script

```ts
import {
  renderPage,
  renderPageWithExtraction,
  compareScreenshots,
  saveScreenshot,
} from './plugin';
import {exec} from 'child_process';
import {promisify} from 'util';

const execAsync = promisify(exec);

async function validate(originalUrl: string, cloneUrl: string) {
  const results = {
    lint: {passed: false, output: ''},
    typescript: {passed: false, output: ''},
    screenshot: {similarity: 0, diffPixels: 0, totalPixels: 0},
  };

  // Run linting
  try {
    const {stdout} = await execAsync('npm run lint');
    results.lint = {passed: true, output: stdout};
  } catch (error: any) {
    results.lint = {passed: false, output: error.stdout || error.message};
  }

  // Run type checking
  try {
    const {stdout} = await execAsync('npm run tsc --noEmit');
    results.typescript = {passed: true, output: stdout};
  } catch (error: any) {
    results.typescript = {passed: false, output: error.stdout || error.message};
  }

  // Screenshot comparison
  const [original, clone] = await Promise.all([
    renderPage({url: originalUrl}),
    renderPage({url: cloneUrl}),
  ]);

  const comparison = await compareScreenshots(
    original.screenshot,
    clone.screenshot,
    './validation/diff.png'
  );

  results.screenshot = {
    similarity: comparison.similarity,
    diffPixels: comparison.diffPixels,
    totalPixels: comparison.totalPixels,
  };

  return results;
}
```
