QA Engineering • DevOps

The Playwright Test Results Badge: Monitor Your CI/CD Directly from Chrome

Table of Contents

Overview

As a developer or QA engineer, how much time do you lose switching between your IDE and your CI/CD dashboard? The Playwright Browser Extension is a lightweight Chrome extension that helps you quickly see your Playwright test status directly from your browser toolbar. Version 1.8 introduces failed test details directly in the popup and dark/light mode support, alongside AWS S3 and GitHub Pages integration, giving you complete flexibility in where you store your test results.

Who is this for?
Perfect for DEVs, QA engineers, and students learning automated testing or CI/CD pipelines.
Privacy First
No accounts, no tracking, and no external servers. Your data stays between your browser and your source.
Auto Refresh
The extension polls your summary file every 1 minute in the background, keeping you always up to date.

What the Extension Does

Live Toolbar Badge

The badge stays visible at all times, providing a quick health check of your project:

Green 42 / 0 (All Passed)
Red 41 / 1 (Failure Detected)
Gray ? (No Data / Syncing)

Detailed Popup View

Click the extension icon in your browser toolbar to see a comprehensive breakdown:

Playwright Test Results Extension Popup in Dark Mode

Quick Setup Guide (6 Steps)

Follow these steps to integrate the badge with your Playwright project today.

Step 1: Install Playwright

If you are starting a new project, run: npm init playwright@latest

  • Choose TypeScript for the best experience.
  • Choose true for "Add a GitHub Actions workflow".

This will create example tests and a default .yml file that you will replace in Step 4.

Step 2: Create the Summary Reporter

Create summary-reporter.js in your project root. This converts heavy reports into a lightweight 1KB JSON.

summary-reporter.js
const fs = require('fs');

class SummaryReporter {
  onBegin(config, suite) {
    this.rootSuite = suite;
  }

  onEnd(result) {
    const summary = {
      schemaVersion: 1,
      passed: 0,
      failed: 0,
      flaky: 0,
      total: 0,
      startTime: new Date().toISOString(),
      isSummary: true,
      failedTests: [] // 👈 NEW: Added array to hold names
    };

    if (this.rootSuite) {
      for (const test of this.rootSuite.allTests()) {
        const status = test.outcome();
        if (status === 'expected') summary.passed++;
        if (status === 'unexpected') {
          summary.failed++;
          summary.failedTests.push(test.title); // 👈 NEW: Push failing test name
        }
        if (status === 'flaky') summary.flaky++;
      }
    }

    summary.total = summary.passed + summary.failed + summary.flaky;
    fs.writeFileSync('test-summary.json', JSON.stringify(summary, null, 2));
    console.log('✅ Automatic summary created: test-summary.json');
  }
}

module.exports = SummaryReporter;

Step 3: Configure Playwright

Update your playwright.config.ts to include your new custom reporter.

playwright.config.ts
// ... other imports
reporters: [
  ['./summary-reporter.js'],  // Add this line
  ['html'],                   // Keep your existing reporters
  ['list']
],

Step 4: Create GitHub Actions Workflow

Automate the test run and summary update. Ensure contents: write permission is enabled.

.github/workflows/playwright.yml
name: Playwright Tests
on: [push, pull_request]

permissions:
  contents: write

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v4
    - uses: actions/setup-node@v4
    - name: Run tests
      run: npx playwright test
    - name: Update test results
      if: always()
      run: |
        git config user.name "Actions Bot"
        git config user.email "actions@github.com"
        git add test-summary.json
        git diff --staged --quiet || git commit -m "chore: update results [skip ci]"
        git push
Working with a Private Repo? See the Private Repository Guide below for the secure deployment method.

Step 5: Initialize Your Summary

Run your tests locally one time (npx playwright test) to create the test-summary.json file, then commit and push it to your repository. Tip: You can use the example tests that come with Playwright to verify the badge immediately!

Step 6: Connect & Test

Copy the "Raw" URL of your test-summary.json and paste it into the extension settings.

Pro Tip: GitHub UI links are supported too! The extension automatically converts them to raw links for you.

Monitoring Private Repositories

By design, this extension does not request your GitHub Personal Access Tokens. This keeps your account secure but means it cannot directly access private repository files.

The Solution: Use GitHub Pages to host your summary file. GitHub Pages can be enabled even on private repositories and GitHub Organizations, allowing you to serve the 1KB summary file publicly while keeping your source code and test results private.

Step-by-Step Configuration

1. Enable GitHub Pages

Go to your repository Settings → Pages. Under "Build and deployment", set the Source to GitHub Actions as shown below.

GitHub Pages Settings - Configure Build and Deployment for Playwright Results

2. Use the Complete Workflow

Copy and paste this workflow into .github/workflows/playwright.yml. It handles testing, result commitment, and Pages deployment automatically while generating a beautiful HTML dashboard.

playwright.yml
name: Deploy results.json to GitHub Pages

on:
  push:
    branches: [main]
  workflow_dispatch:

permissions:
  contents: write
  pages: write
  id-token: write

jobs:
  deploy:
    environment:
      name: github-pages
      url: ${{ steps.deployment.outputs.page_url }}
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Install dependencies
        run: npm ci

      - name: Install Playwright Browsers
        run: npx playwright install --with-deps

      - name: Run Playwright tests
        run: npx playwright test

      - name: Commit and push updated results
        if: always()
        run: |
          git config user.name "github-actions[bot]"
          git config user.email "github-actions[bot]@users.noreply.github.com"
          git add test-summary.json
          git commit -m "Update test summary [skip ci]" || echo "No changes"
          git push

      - name: Prepare Pages artifact (Summary and results)
        if: always()
        run: |
          mkdir -p public
          if [ -f test-summary.json ]; then
            cp test-summary.json public/test-summary.json
            cp test-summary.json public/results.json
          else
            echo '{"schemaVersion":1,"passed":0,"failed":0,"flaky":0,"total":0,"startTime":"","isSummary":true,"failedTests":["Summary file not generated"]}' > public/test-summary.json
            cp public/test-summary.json public/results.json
          fi

          node - <<'NODE'
          const fs = require('fs');
          const path = 'public/test-summary.json';
          const summary = JSON.parse(fs.readFileSync(path, 'utf8'));

          const passed = Number(summary.passed || 0);
          const failed = Number(summary.failed || 0);
          const flaky = Number(summary.flaky || 0);
          const total = Number(summary.total || 0);
          const failedTests = Array.isArray(summary.failedTests) ? summary.failedTests : [];
          const generatedAt = summary.startTime || new Date().toISOString();
          const status = failed > 0 ? 'Failing' : 'Passing';

          const escapeHtml = (value) => String(value)
            .replace(/&/g, '&amp;')
            .replace(/</g, '&lt;')
            .replace(/>/g, '&gt;')
            .replace(/"/g, '&quot;')
            .replace(/'/g, '&#39;');

          const failedList = failedTests.length
            ? failedTests.map((name) => `<li>${escapeHtml(name)}</li>`).join('')
            : '<li>No failed tests reported</li>';

          const html = `<!doctype html>
          <html lang="en">
          <head>
            <meta charset="utf-8" />
            <meta name="viewport" content="width=device-width, initial-scale=1" />
            <title>Playwright Test Summary</title>
            <style>
              :root { color-scheme: light; }
              body {
                margin: 0;
                font-family: Segoe UI, Arial, sans-serif;
                background: #F5F7FB;
                color: #111827;
              }
              .container {
                max-width: 860px;
                margin: 40px auto;
                padding: 0 16px;
              }
              .card {
                background: #FFFFFF;
                border: 1px solid #DBE3EF;
                border-radius: 12px;
                padding: 20px;
                box-shadow: 0 8px 20px rgba(17, 24, 39, 0.06);
              }
              h1 {
                margin-top: 0;
                margin-bottom: 8px;
              }
              .meta {
                color: #475569;
                margin-bottom: 16px;
              }
              .grid {
                display: grid;
                grid-template-columns: repeat(4, minmax(0, 1fr));
                gap: 10px;
                margin-bottom: 20px;
              }
              .metric {
                border: 1px solid #E5E7EB;
                border-radius: 10px;
                padding: 10px;
                background: #F8FAFC;
              }
              .metric b {
                display: block;
                font-size: 22px;
                margin-top: 4px;
              }
              ul {
                padding-left: 20px;
              }
              a {
                color: #0F766E;
                text-decoration: none;
              }
              a:hover { text-decoration: underline; }
              @media (max-width: 640px) {
                .grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
              }
            </style>
          </head>
          <body>
            <div class="container">
              <div class="card">
                <h1>Playwright Test Summary</h1>
                <div class="meta">Status: <b>${escapeHtml(status)}</b> | Generated: ${escapeHtml(generatedAt)}</div>
                <div class="grid">
                  <div class="metric">Total<b>${total}</b></div>
                  <div class="metric">Passed<b>${passed}</b></div>
                  <div class="metric">Failed<b>${failed}</b></div>
                  <div class="metric">Flaky<b>${flaky}</b></div>
                </div>
                <h2>Failed Tests</h2>
                <ul>${failedList}</ul>
                <p><a href="results.json">View raw results.json</a></p>
              </div>
            </div>
          </body>
          </html>`;

          fs.writeFileSync('public/index.html', html, 'utf8');
          NODE

          touch public/.nojekyll

      - name: Configure Pages
        if: always()
        uses: actions/configure-pages@v4

      - name: Upload artifact
        if: always()
        uses: actions/upload-pages-artifact@v3
        with:
          path: public

      - name: Deploy
        id: deployment
        if: always()
        uses: actions/deploy-pages@v4

The workflow above generates an automated dashboard like this:

Playwright Browser Extension HTML Dashboard Summary - v1.8 Free Release

3. Initialize Your Summary

Run npx playwright test locally, then commit and push the generated test-summary.json file to GitHub.

4. Connect & Verify

Once the workflow finishes, you can verify the deployment status in your repository settings as shown below:

GitHub Pages Deployment Success
Important: Provide the JSON URL to the extension, not the HTML dashboard URL.
• ❌ Incorrect: https://username.github.io/repo/
• ✅ Correct: https://username.github.io/repo/results.json

Paste the results.json URL into the extension settings and click Save.

Alternative: Deploy to AWS S3

If you prefer AWS over GitHub Pages, you can deploy your test results to an S3 bucket. This gives you full control over your infrastructure and works great for teams already using AWS.

Step-by-Step AWS S3 Configuration

1. Create an S3 Bucket

Create a new S3 bucket (e.g., my-test-results) in your preferred AWS region.

2. Configure S3 Bucket Permissions

a) Block Public Access: Go to your bucket → Permissions tab → Block public access → Edit → Uncheck all boxes → Save → Confirm

b) Bucket Policy: Go to Bucket policy → Edit → Paste this policy (replace YOUR-BUCKET-NAME):

S3 Bucket Policy
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "PublicReadGetObject",
            "Effect": "Allow",
            "Principal": "*",
            "Action": "s3:GetObject",
            "Resource": "arn:aws:s3:::YOUR-BUCKET-NAME/*"
        }
    ]
}

c) CORS Configuration: Go to CORS configuration → Edit → Paste:

CORS Configuration
[
    {
        "AllowedHeaders": ["*"],
        "AllowedMethods": ["GET", "HEAD"],
        "AllowedOrigins": ["*"],
        "ExposeHeaders": [],
        "MaxAgeSeconds": 3000
    }
]

3. Set Up AWS Credentials in GitHub Secrets

Go to your repository → Settings → Secrets and variables → Actions → New repository secret

Add these three secrets:

  • AWS_ACCESS_KEY_ID
  • AWS_SECRET_ACCESS_KEY
  • AWS_DEFAULT_REGION (e.g., us-east-1)

4. Create AWS S3 Deployment Workflow

Create .github/workflows/playwright.yml with this content:

.github/workflows/playwright.yml
name: Deploy Test Results to AWS S3

on:
  push:
    branches: [main]
  workflow_dispatch:

jobs:
  test-and-deploy:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v4
      
      - name: Install dependencies
        run: npm ci
      
      - name: Install Playwright
        run: npx playwright install --with-deps
      
      - name: Run Playwright tests
        run: npx playwright test
      
      - name: Upload results to AWS S3
        if: always()
        run: |
          aws s3 cp test-summary.json s3://YOUR-BUCKET-NAME/results.json --acl public-read --content-type application/json
        env:
          AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
          AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          AWS_DEFAULT_REGION: ${{ secrets.AWS_DEFAULT_REGION }}

5. Connect & Monitor

Your S3 URL will be: https://YOUR-BUCKET-NAME.s3.REGION.amazonaws.com/results.json

Example: https://my-test-results.s3.us-east-2.amazonaws.com/results.json

Paste this URL into the extension popup and click Save!

Note: The extension supports both GitHub Pages and AWS S3 URLs. Choose whichever deployment method works best for your team's infrastructure.

Workflow & Best Practices

The extension is designed to work with any CI tool including GitHub Actions, GitLab CI, Jenkins, and CircleCI.

Important: Set the workflow to run if: always(). This ensures the summary file is updated even when tests fail, which is exactly when you need the badge most!

Permissions Explained

Troubleshooting common issues

Badge shows gray ?

Check if test-summary.json exists and contains "isSummary": true. Verify the URL is public or reachable.

Tests not updating?

Confirm your GitHub Workflow has contents: write permissions and that [skip ci] is used to avoid infinite loops.

Boost Your Testing Productivity

Get the extension for free and keep your test health visible at all times.

Add to Chrome View Demo Repo