ScreenshotNeo

BlogHow-to

How to Run Visual Regression Tests for a WordPress Site in India Using GitHub Actions

Set up Playwright screenshot tests for WordPress in GitHub Actions, keep comparisons repeatable, review visual changes, and troubleshoot common CI failures.

By the ScreenshotNeo team4 October 202610 min read

To run visual regression tests for a WordPress site in India using GitHub Actions, use Playwright Test to capture a page or element and compare it with a reviewed screenshot baseline on each pull request. The runner must be able to reach the WordPress site: start a local test site in the workflow or target a reachable preview environment. The workflow is the same for developers in India as elsewhere; the research does not establish a special India-only runner, hosting plan, or hardware requirement.

This guide sets up a small Playwright project and GitHub Actions workflow, explains how to make captures repeatable, and covers baseline review and common failures. For a plugin or theme repository, WordPress Playground’s E2E guide is a WordPress-oriented route to setting up the environment. The sample below assumes you already have a site running locally at http://127.0.0.1:8888; adapt that server command and URL to your project.

1. Create a Playwright visual test

Install Playwright Test and add a test that visits a representative route. Playwright creates a baseline when the screenshot assertion first runs and compares later captures against it. Inspect and commit the initial image only after confirming it represents the intended design.

npm init -y
npm install --save-dev @playwright/test
npx playwright install chromium

Add a configuration file to fix the viewport and browser project. A fixed viewport keeps the captured layout consistent across runs.

// playwright.config.ts
import { defineConfig } from '@playwright/test';

export default defineConfig({
  testDir: './tests',
  use: {
    baseURL: process.env.BASE_URL || 'http://127.0.0.1:8888',
    browserName: 'chromium',
    viewport: { width: 1440, height: 900 },
    // Keep animations from creating transient screenshot differences.
    reducedMotion: 'reduce',
  },
  reporter: [['html', { open: 'never' }]],
});

Create a homepage assertion. Waiting for a meaningful heading helps avoid capturing before the route has rendered. Replace the route and locator with content that exists on your site.

// tests/homepage.spec.ts
import { test, expect } from '@playwright/test';

test('homepage visual appearance', async ({ page }) => {
  await page.goto('/');
  await expect(page.getByRole('heading', { name: /welcome/i })).toBeVisible();
  await expect(page).toHaveScreenshot('homepage.png', {
    fullPage: true,
    animations: 'disabled',
  });
});

If your homepage does not have a stable “Welcome” heading, use a locator for a stable landmark or wait for a page-specific element. Avoid waiting an arbitrary long time unless the application genuinely needs it; a selector-based wait makes the expected condition explicit.

Add a script so local and CI runs use the same command:

// package.json (merge the script into the existing file)
{
  "scripts": {
    "test:visual": "playwright test"
  }
}

Run the test locally with the WordPress site available:

BASE_URL=http://127.0.0.1:8888 npm run test:visual

The first run writes a reference screenshot under Playwright’s snapshot directory. Review it, then commit it with the test. Subsequent runs compare against that committed image. Keep the baseline and the code change together when accepting an intentional visual change.

2. Run the test on GitHub Actions pull requests

Use a lockfile so CI installs the same dependency graph as local development. For npm, commit package-lock.json and use npm ci. The following workflow installs Chromium, starts your project’s local WordPress environment, waits for it to become ready, runs tests, and retains the HTML report and test results after success or failure.

The server command is intentionally a project-specific placeholder: replace it with the command or action that starts your reproducible WordPress site and loads its fixtures. Do not run the tests until that environment is reachable from the runner.

# .github/workflows/visual-regression.yml
name: Visual regression

on:
  pull_request:
  push:
    branches: [main]

jobs:
  visual:
    runs-on: ubuntu-latest
    timeout-minutes: 20
    steps:
      - uses: actions/checkout@v4

      - uses: actions/setup-node@v4
        with:
          node-version: 22
          cache: npm

      - run: npm ci
      - run: npx playwright install --with-deps chromium

      # Replace with your reproducible WordPress setup command.
      - name: Start WordPress test site
        run: |
          ./scripts/start-test-site.sh &

      - name: Wait for WordPress
        run: npx wait-on http://127.0.0.1:8888

      - name: Run visual tests
        run: npm run test:visual
        env:
          BASE_URL: http://127.0.0.1:8888

      - name: Upload Playwright report and results
        if: always()
        uses: actions/upload-artifact@v4
        with:
          name: visual-regression-results
          path: |
            playwright-report/
            test-results/
          if-no-files-found: ignore

Install wait-on in your project if you use the sample readiness step: npm install --save-dev wait-on. Pin GitHub Action versions and the Playwright runtime to versions your project has verified. The action versions and Node version shown are examples; review current compatibility and your repository’s policy before adopting them. Playwright’s CI guide documents browser installation, report artifacts, container use for a consistent runtime, and running tests after deployment status.

Use a preview site instead of starting WordPress in CI

If your pull request deploys a preview site, wait for the deployment to finish and pass its URL to Playwright as BASE_URL. Ensure the runner can access it and that the URL points to the commit under test. Treat authentication credentials as repository or environment secrets, and do not expose them in logs or artifacts. A production-specific deployment recipe depends on your hosting provider, which this general workflow does not prescribe.

3. Make screenshot comparisons useful

Screenshot assertions are sensitive to rendered pixels, so stabilize both the environment and the page data. Keep browser versions consistent, use fixed viewport dimensions, load deterministic fixtures, and avoid live APIs or changing production content in snapshot tests. Playwright documents container-based CI as an option when consistent rendering across operating systems matters.

  • Choose representative coverage. Start with high-value templates and flows: the homepage or landing page, a representative post or page, navigation, and important forms or checkout if relevant. WordPress’s E2E guidance advises reserving end-to-end coverage for critical flows rather than every possible scenario.
  • Cover responsive layouts. Add a separate project or test with a mobile viewport if mobile presentation matters to your audience. Pick widths that match the site’s actual design breakpoints; there is no universal WordPress breakpoint.
  • Capture important states. For key controls, consider focused, hover, or active states. Use a locator screenshot when the component itself is the subject, and a page screenshot when surrounding layout matters.
  • Control changing regions. Prefer stable fixtures and test data. If a region is inherently dynamic, consider masking that locator in the screenshot assertion rather than allowing unrelated changes to trigger noisy diffs.
  • Keep the browser and operating system stable. A pinned container is an option when local and CI rendering differs. Keep its Playwright version aligned with the package installed by the lockfile.

For example, a mobile check can specify its viewport directly:

import { test, expect } from '@playwright/test';

test('mobile navigation layout', async ({ page }) => {
  await page.setViewportSize({ width: 390, height: 844 });
  await page.goto('/');
  await expect(page.getByRole('button', { name: /menu/i })).toBeVisible();
  await expect(page).toHaveScreenshot('homepage-mobile.png', {
    fullPage: true,
    animations: 'disabled',
  });
});

To focus on one component, take a locator screenshot:

const navigation = page.getByRole('navigation', { name: /primary/i });
await expect(navigation).toHaveScreenshot('primary-navigation.png');

Playwright’s assertion options include controls such as full-page capture, masking, animation handling, and thresholds for acceptable pixel differences. Use thresholds sparingly: a generous threshold can hide a real regression. Consult the current Playwright visual comparisons documentation for supported options and platform behavior.

4. Review and update baselines

When a test fails, first compare the actual screenshot with the committed baseline. Open the HTML report, inspect the expected, actual, and diff images, then use the trace to examine browser actions, DOM state, and network behavior. WordPress Playground’s guide covers the Playwright Inspector, Trace Viewer, UI mode, and failure screenshots.

  1. Determine whether the difference is caused by a real design change, unstable data, timing, or a changed rendering environment.
  2. If the UI change is intended, update the screenshot using Playwright’s snapshot update mode, for example npx playwright test --update-snapshots.
  3. Inspect every changed baseline image and include it in the pull request with the related code change.
  4. If the difference is unintended, fix the code or test setup; do not accept the new screenshot just to make CI green.

Baseline updates are review decisions. Do not automatically rewrite references on every pull request. A third-party action can capture base and pull-request screenshots, store artifacts, compare them, and comment a diff on the pull request. The cited Visual Regression Action repository is an implementation example, not an endorsement; review its maintenance, permissions, secrets, storage exposure, and fit before adopting it. Native Playwright snapshots keep baseline files in your repository and avoid a separate comparison service, while a hosted or action-based review pattern may change artifact storage and reviewer workflow.

5. Troubleshoot common CI failures

Symptom Likely cause Fix
Navigation fails or times out The WordPress server did not start, the URL is wrong, or the runner cannot reach the preview. Check server logs, confirm BASE_URL, and add a readiness check that waits for the actual site route before running Playwright.
Browser executable is missing Chromium was not installed in the CI job, or the installed browser does not match the Playwright package. Run npx playwright install --with-deps chromium after npm ci; keep package and browser installation in the same job.
Screenshot differs on every run Dynamic content, animation, fonts, timestamps, network responses, or inconsistent browser/OS rendering affect pixels. Use fixed fixtures and viewport, wait for stable content, disable animations, and use a consistent runtime. Mask only truly irrelevant dynamic regions.
Baseline is missing in CI The initial reference was never committed, or its path/name differs by operating system. Generate the baseline locally with the project’s intended browser setup, inspect it, and commit the exact snapshot file.
Mobile screenshot shows desktop layout The test kept the configured desktop viewport or the site’s responsive state did not settle. Set a mobile viewport before navigation and assert a mobile-specific element or state before capturing.
WordPress returns an error page The test database, plugins, theme, environment configuration, or fixtures are incomplete. Inspect the server output and response, then make the CI environment reproduce the required site setup and content before screenshot tests begin.
Artifact upload has no files The report was not generated, or output paths differ from the configured artifact paths. Check the Playwright reporter and results directory; retain the upload step with if: always() so failures still preserve available evidence.

6. Performance, reliability, and cost

Visual tests use more time and resources than a static code check because each test launches a browser, loads a site, and captures pixels. Keep the suite focused on representative templates and critical states; parallelize only after checking the capacity and stability of the chosen runner and site environment. Cache browser downloads where useful, as shown in WordPress Playground’s Actions example, but keep browser versions aligned with the Playwright package. Upload reports and failure artifacts so a failed comparison can be reviewed without rerunning blindly.

GitHub Actions usage and runner availability depend on your account, organization configuration, and selected runner. No India-specific pricing or service availability is established by the sources used here. Check your own GitHub plan and runner settings. For reliability, test against a deterministic local environment or a preview tied to the pull request, and ensure that the runner has the required network access.

Or skip the browser setup

If you need screenshots of pages for review or content monitoring without maintaining a browser runner, ScreenshotNeo is a website screenshot API and MCP server from Yorker Media. Its one-call API returns PNG, JPEG, WebP, or PDF output; its features include viewport presets, full-page capture, and CSS selector capture. For visual regression, you still need to define and review your expected images and comparison process.

See the ScreenshotNeo API documentation. Example request:

curl -G "https://api.screenshotneo.com/v1/shot" \
  -d access_key=YOUR_API_KEY \
  --data-urlencode url=https://example.com \
  -o shot.webp
import requests

r = requests.get(
    "https://api.screenshotneo.com/v1/shot",
    params={"access_key": "YOUR_API_KEY", "url": "https://example.com"},
    timeout=90,
)
r.raise_for_status()
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({
  access_key: 'YOUR_API_KEY',
  url: 'https://example.com',
});
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`Screenshot request failed: ${res.status}`);
await Bun.write('shot.webp', res);
  • Cookie banners are accepted and removed before the capture; known newsletter popups and chat widgets are removed too, and each cleanup step can be turned off.
  • Bot checks, blank pages, timeouts, failed loads, and cache hits are not billed; response headers identify the page verdict and billing status.
  • An MCP server lets AI agents use screenshot, page information, and PDF capture tools.
  • 1,000 screenshots per month are free with no card; paid plans start at $5 for 3,000 screenshots.

Create a free ScreenshotNeo account to get 1,000 screenshots a month with no card.

FAQ

Does a WordPress site in India need a different GitHub Actions setup?

The documented workflow uses standard Node.js, Playwright, WordPress, and GitHub Actions configuration. Check your organization’s runner, network, hosting, and account settings for the actual project; the research did not establish a regional requirement.

Can I compare screenshots of a deployed site?

Yes. Use a reachable preview URL and start tests after deployment is ready. Keep the preview tied to the commit under test and make any required access credentials available securely to the workflow.

Should every page have a screenshot test?

No. Begin with representative templates and important user flows, then add coverage where a visual failure would matter. This keeps the suite easier to maintain and review.

Can screenshots be compared across different operating systems?

Rendering differences can create noise. Keep the browser and runtime consistent, or use a project container when that helps make CI rendering reproducible.

Sources