agentsclimarketplace

Browser testing with devtools

Skill codexskills/agent-forge/skills/browser-testing-with-devtools

Production-grade AI agent skills — enhanced alternative to addyosmani/agent-skills. 23 lifecycle skills for Define, Plan, Build, Verify, Review, Ship. Works with OpenCode, Claude Code, Codex, Cursor, Gemini CLI.

Install
npx -y skills add codexskills/agent-forge --skill browser-testing-with-devtools

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

2 things to look at

  • no licenseNo license file was found in the repository. Code published without one is not open source by default, so using it at work is a question for whoever answers licensing questions where you are.
  • 1 stars1 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

What its author says it does

Copied from the file, not written here

Test web applications in real browsers via Chrome DevTools Protocol including DOM inspection, console analysis, network profiling, performance auditing, Lighthouse CI, and visual regression.

SKILL.md

14.9 KB, as published. Nobody here has run it

Browser Testing with DevTools

Overview

Browser testing with Chrome DevTools is the practice of using browser-native debugging and auditing tools to verify web application correctness, performance, accessibility, and reliability. It bridges the gap between unit tests (which verify logic) and real user experiences (which depend on rendering, networking, and runtime behavior).

DevTools-based testing catches issues that unit tests never will: layout shifts, console errors from third-party scripts, network waterfalls, memory leaks, accessibility violations, and real rendering performance. It is the most direct way to test what users actually experience.

When to Use

  • Before shipping any user-facing feature
  • Debugging layout, styling, or rendering issues
  • Investigating performance regressions
  • Auditing accessibility compliance
  • Analyzing network requests and API calls
  • Debugging console errors or warnings
  • Profiling JavaScript execution and memory usage
  • Verifying responsive design across device sizes
  • Testing service workers and offline behavior
  • Auditing third-party script impact
  • Comparing before/after screenshots for visual regression
  • Setting up CI/CD gates for performance and accessibility

Process

Step 1: Session Initialization

Launch the browser with DevTools-specific configurations:

const browser = await puppeteer.launch({
  headless: false,  // false for visual debugging, 'new' for CI
  args: [
    '--auto-open-devtools-for-tabs',
    '--window-size=1440,900',
    '--disable-notifications',
    '--disable-geolocation',
    '--disable-speech-api',
    '--disable-extensions',
    '--no-sandbox',  // required in CI
    '--disable-setuid-sandbox',
  ],
  defaultViewport: { width: 1440, height: 900 }
});

Essential CDP domains to enable:

  • Network: Request/response capture, throttling
  • Page: Navigation, DOM events, lifecycle
  • DOM: Element inspection and modification
  • CSS: Style computation, coverage
  • Runtime: JavaScript execution, console
  • Performance: Timeline and metrics
  • Audits: Lighthouse integration

Step 2: DOM Inspection and Verification

Test that the DOM matches expected structure:

async function verifyDOMStructure(page) {
  // Verify component exists
  const selector = '[data-testid="product-card"]';
  await page.waitForSelector(selector, { timeout: 5000 });

  // Verify text content
  const text = await page.$eval(selector, el => el.textContent);
  expect(text).toContain('Expected Product Name');

  // Verify attributes
  const href = await page.$eval('a.cta-button', el => el.getAttribute('href'));
  expect(href).toBe('/checkout');

  // Verify computed styles
  const color = await page.$eval('h1', el =>
    getComputedStyle(el).getPropertyValue('color')
  );
  expect(color).toBe('rgb(33, 37, 41)');

  // Verify visibility (not just existence)
  const isVisible = await page.$eval('.mobile-menu', el => {
    const style = getComputedStyle(el);
    return style.display !== 'none' && style.visibility !== 'hidden' && style.opacity !== '0';
  });
  expect(isVisible).toBe(true);
}

DOM verification checklist:

  • All critical elements exist with correct attributes
  • Text content matches expected values
  • Computed styles match design tokens
  • Elements are visible and interactable (not just present in DOM)
  • ARIA attributes are present on custom interactive elements
  • Focus order matches visual order
  • No duplicate IDs

Step 3: Console Error Capture

Console errors are the #1 indicator of runtime problems. Capture every one:

async function captureConsoleErrors(page) {
  const errors = [];

  page.on('console', msg => {
    if (msg.type() === 'error' || msg.type() === 'warning') {
      errors.push({
        type: msg.type(),
        text: msg.text(),
        location: msg.location(),
        timestamp: Date.now(),
        stack: msg.stackTrace ? msg.stackTrace().callFrames : []
      });
    }
  });

  page.on('pageerror', err => {
    errors.push({
      type: 'page_error',
      text: err.message,
      stack: err.stack,
      timestamp: Date.now()
    });
  });

  await page.goto('http://localhost:3000');
  await page.waitForLoadState('networkidle');

  return errors;
}

// In test:
const errors = await captureConsoleErrors(page);
expect(errors.filter(e => e.type === 'error')).toHaveLength(0);

Error severity classification:

  • CRITICAL: Uncaught exceptions, security errors, API failures
  • HIGH: Deprecation warnings, unhandled promise rejections
  • MEDIUM: Third-party script errors, DevTools protocol errors
  • LOW: Verbose warnings, experimental feature notices

Rules:

  • Zero CRITICAL errors allowed
  • Zero HIGH errors allowed in production
  • MEDIUM errors must be documented with tracking issue
  • Ignore LOW errors after verification (but log them)

Step 4: Network Analysis

Test request/response behavior, not just status codes:

async function analyzeNetwork(page) {
  const requests = [];

  page.on('request', req => {
    requests.push({
      url: req.url(),
      method: req.method(),
      type: req.resourceType(),
      headers: req.headers(),
      startTime: performance.now()
    });
  });

  page.on('response', res => {
    const req = requests.find(r => r.url === res.url());
    if (req) {
      req.status = res.status();
      req.responseHeaders = res.headers();
      req.duration = performance.now() - req.startTime;
      req.size = parseInt(res.headers()['content-length'] || '0');
    }
  });

  await page.goto('http://localhost:3000');
  await page.waitForLoadState('networkidle');

  return requests;
}

Network analysis checklist:

  • No 4xx/5xx responses (except expected 404s)
  • No API calls to unauthorized endpoints (check CORS, auth headers)
  • Request payloads match documented API contracts
  • Response times within budget (< 200ms for critical API calls)
  • Asset sizes within budget (JS < 300KB, CSS < 50KB)
  • Proper caching headers present (Cache-Control, ETag)
  • No mixed content (HTTP on HTTPS page)
  • No duplicate or redundant requests
  • Compression enabled (Content-Encoding: gzip/brotli)
  • Request waterfalls show minimal blocking

Step 5: Performance Profiling

Profile runtime performance to identify bottlenecks:

async function profilePerformance(page) {
  // Start profiling
  await page.tracing.start({
    path: 'trace.json',
    categories: [
      'devtools.timeline',
      'loading',
      'blink.user_timing',
      'disabled-by-default-devtools.timeline',
      'disabled-by-default-devtools.timeline.frame',
      'disabled-by-default-devtools.timeline.stack'
    ]
  });

  // Perform user interaction
  await page.goto('http://localhost:3000', { waitUntil: 'networkidle' });
  await page.click('[data-testid="search-input"]');
  await page.type('[data-testid="search-input"]', 'test query');
  await page.click('[data-testid="search-button"]');
  await page.waitForSelector('[data-testid="search-results"]', { timeout: 5000 });

  // Stop profiling
  await page.tracing.stop();

  // Parse trace
  const trace = JSON.parse(fs.readFileSync('trace.json', 'utf8'));
  return trace;
}

Performance metrics to extract from traces:

  • Main thread idle time / busy time ratio
  • Long tasks (>50ms) count and duration
  • Layout and style recalc frequency and duration
  • JavaScript execution time (parse, compile, execute)
  • GC pause time and frequency
  • Frame rate (target: 60fps for animations)
  • Paint and composite time

Performance budget gates:

  • FCP < 1.5s
  • LCP < 2.5s
  • TBT < 200ms
  • CLS < 0.1
  • No long tasks on interaction
  • No forced reflows (layout thrashing)
  • All animations run at 60fps
  • Memory usage stable (no leaks over time)

Step 6: Lighthouse Audits

Run automated Lighthouse audits as CI gates:

const lighthouse = require('lighthouse');
const chromeLauncher = require('chrome-launcher');

async function runLighthouseAudit(url) {
  const chrome = await chromeLauncher.launch({
    chromeFlags: ['--headless', '--no-sandbox']
  });

  const runnerResult = await lighthouse(url, {
    port: chrome.port,
    output: 'json',
    logLevel: 'info',
    onlyCategories: [
      'performance',
      'accessibility',
      'best-practices',
      'seo',
      'pwa'
    ],
    throttling: {
      // Mobile simulation
      rttMs: 150,
      throughputKbps: 1600,
      cpuSlowdownMultiplier: 4
    }
  });

  await chrome.kill();

  return runnerResult.lhr.categories;
}

// In test:
const scores = await runLighthouseAudit('http://localhost:3000');
expect(scores.performance.score).toBeGreaterThanOrEqual(0.9);
expect(scores.accessibility.score).toBeGreaterThanOrEqual(0.95);
expect(scores['best-practices'].score).toBeGreaterThanOrEqual(0.9);

Lighthouse score thresholds:

  • Performance: ≥ 90 (mobile simulated)
  • Accessibility: ≥ 95
  • Best Practices: ≥ 90
  • SEO: ≥ 95
  • PWA: ≥ 80 (if applicable)

Step 7: DevTools Accessibility Audit

Beyond Lighthouse, run programmatic accessibility checks:

async function runAccessibilityAudit(page) {
  // Inject axe-core
  await page.addScriptTag({
    path: require.resolve('axe-core/axe.min.js')
  });

  // Run audit
  const results = await page.evaluate(() => {
    return axe.run({
      runOnly: {
        type: 'tag',
        values: ['wcag2a', 'wcag2aa', 'wcag21a', 'wcag21aa']
      }
    });
  });

  return results;
}

// In test:
const results = await runAccessibilityAudit(page);
expect(results.violations.filter(v => v.impact === 'critical')).toHaveLength(0);
expect(results.violations.filter(v => v.impact === 'serious')).toHaveLength(0);

Key WCAG checks beyond automated tools:

  • Manual keyboard navigation test (tab through all interactive elements)
  • Screen reader test (VoiceOver / NVDA reads content correctly)
  • Focus indicator visibility (2px+ outline, 3:1 contrast ratio)
  • Zoom to 200% without loss of content or functionality
  • Color contrast verification for all text elements
  • Touch target size (44×44px minimum)
  • prefers-reduced-motion respected
  • Skip navigation link functional

Step 8: Screenshot Comparison

Capture and compare screenshots for visual regression:

async function captureScreenshot(page, name) {
  await page.screenshot({
    path: `screenshots/${name}.png`,
    fullPage: true,
    type: 'png'
  });
}

async function compareScreenshots(baseline, current) {
  const pixelmatch = require('pixelmatch');
  const PNG = require('pngjs').PNG;

  const img1 = PNG.sync.read(fs.readFileSync(baseline));
  const img2 = PNG.sync.read(fs.readFileSync(current));
  const { width, height } = img1;

  const diff = new PNG({ width, height });
  const mismatched = pixelmatch(img1.data, img2.data, diff.data, width, height, {
    threshold: 0.1
  });

  const diffPercent = (mismatched / (width * height)) * 100;
  fs.writeFileSync('diff.png', PNG.sync.write(diff));

  return { mismatched, diffPercent };
}

// In CI:
const result = await compareScreenshots('baseline.png', 'screenshot.png');
expect(result.diffPercent).toBeLessThan(1); // Less than 1% pixel difference

Screenshot comparison rules:

  • Baseline screenshots stored in version control (or S3 for large projects)
  • Always run with the same viewport size
  • Disable animations during capture (consistent rendering)
  • Use fullPage: true for comprehensive comparison
  • Set appropriate pixel threshold (0.1-0.5% for CI, 0% for pixel-perfect)
  • Automatically approve baseline updates when intentional changes are made

Anti-Rationalization Table

ExcuseRebuttal
"Unit tests are enough, I don't need browser testing"Unit tests verify logic in isolation. They do not verify rendering, layout, network behavior, or real browser APIs. A component can pass every unit test and still be broken in the browser.
"We'll catch issues in manual QA"Manual QA is too slow and too inconsistent. It catches maybe 60% of visual/behavioral issues. Automated browser testing catches 95%+ and runs in minutes, not days.
"DevTools testing is flaky"Flakiness is a symptom of poor test design, not a limitation of the tool. Use waitForSelector, networkidle, and retry logic. If your tests are flaky, fix the tests, don't abandon the practice.
"Lighthouse scores are just guidelines"They're guidelines backed by real user experience data. Sites with Lighthouse scores < 50 have 3x higher bounce rates. Improve the score, improve the experience.
"Screenshot tests are too brittle"Use pixel thresholds (0.1%), exclude dynamic content regions, and auto-approve baseline updates on intentional changes. The brittleness is manageable and the value (catching visual regressions) is enormous.
"Accessibility is the design team's responsibility"Accessibility is everyone's responsibility. Engineers implement the UI; engineers must verify it works with assistive technology. Design provides the intent; engineering validates the implementation.
"We don't have time to set up all this infrastructure"You don't have time not to. A single production visual regression costs more to debug than setting up screenshot comparison for the entire app. Start with console error capture — it's 10 lines of code and catches 50% of issues.

Red Flags

  • No console error capture in CI: Silent failures shipping to production
  • No Lighthouse audit in CI: Performance and accessibility regressions unchecked
  • All tests pass but page has visible layout issues: Missing visual regression testing
  • Network tab shows 4xx/5xx errors that "don't affect functionality": They affect user trust and SEO
  • JavaScript errors in console that are "expected": If they're expected, handle them. If not, fix them.
  • No throttling in performance tests: Real users on mobile networks have very different experiences
  • Screenshots not stored in version control: Unable to detect visual regressions over time
  • Accessibility violations present but "no one has complained": Users with disabilities don't complain; they leave

Verification

  • Test: Run console capture across all pages and verify zero errors
  • Test: Run Lighthouse audit and verify all category scores meet thresholds
  • Test: Run axe-core audit and verify zero critical/serious violations
  • Test: Capture and compare screenshots before/after change
  • Test: Throttle to Slow 3G and verify FCP < 3s
  • Test: Simulate mobile device (375×812) and verify responsive layout
  • Test: Keyboard-navigate the entire page (Tab, Shift+Tab, Enter, Escape)
  • Test: Verify no network request failures (all responses 2xx or expected)
  • Test: Profile memory usage over 10 page interactions and verify stability
  • Test: Run the same test suite headless and headed to verify consistency

Keep looking

Skills are one crate of 328,083. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.