Back to Playwright & UI Automation
Q5Question 5 of 7

(Scenario) A Playwright suite passes locally but fails in CI

Key Concepts
  • Identical test code behaving differently means the environments differ, so debugging is a comparison exercise, not a guess-and-edit exercise
  • Gather CI evidence first: logs, on-failure screenshots, video, and the trace which shows DOM, network, and console at the failure point
  • Compare dimensions: Node/Playwright/browser versions, env vars, OS, test data, network access, auth, time zone, file paths, CPU/memory
  • Common culprits: missing env vars, time zone differences (UTC vs local), missing CI secrets, Linux vs macOS font rendering
  • Reproduce the difference locally by matching CI: run in a Linux container with the same versions, variables, and time zone
  • Randomly editing the test to make CI green hides real defects, is fragile, and loosens assertions that destroy coverage

Definition

A pass-locally-fail-in-CI suite is an environmental difference, so compare versions, env vars, OS, data, network, auth, time zone, paths, and resources and read the CI traces, videos, and screenshots, and fix the real difference rather than randomly editing the test, which only hides the cause.
  • Root principle – Identical code behaving differently means the environments differ. Debug by comparison, not by guess-and-edit.
  • CI evidence – Gather logs, on-failure screenshots, video, and the Playwright trace showing DOM, network, and console at the failure point.
  • Comparison dimensions – Node/Playwright/browser versions, environment variables, OS, test data, network access, authentication, time zone, file paths, CPU/memory.
  • Reproduce locally – Match CI by running in a Linux container with the same versions, variables, and time zone.
  • Never randomly edit – Adding sleeps, loosening assertions, or retrying hides real defects, is fragile, and destroys coverage.

Imagine the National Bank of India tests run perfectly on your own desk but fail on the shared night-shift machine. Randomly changing the tests until the night machine goes green is like adjusting your car engine until a warning light turns off without knowing why. The right approach is to compare the two machines carefully: same browser version, same settings, same time zone, can the night machine reach the server, does it have the login secret. Then look at its evidence to see exactly what went wrong.

Understanding the Concepts

What is the key debugging principle?

Identical test code behaving differently in two places means the environments differ. This is a comparison exercise, not a guess-and-edit exercise.

TEXT
  Local: macOS, Chrome 121, IST, secrets in .env, 16GB RAM
  CI:    Linux, Chromium 121, UTC, secrets from vault, 4GB RAM

  Same test code. Different result.
  -> The difference IS the bug.

How do you gather CI evidence?

Playwright produces rich diagnostics. Start with the trace, which usually reveals the failure's nature.

TYPESCRIPT
// Enable diagnostics in config
export default defineConfig({
  use: {
    trace: 'retain-on-failure',
    screenshot: 'only-on-failure',
    video: 'retain-on-failure',
  },
});
YAML
# CI: upload artifacts for diagnosis
- name: Upload test artifacts
  uses: actions/upload-artifact@v4
  if: always()
  with:
    name: test-results
    path: test-results/
BASH
# Open the trace locally
npx playwright show-trace test-results/transfer-test/trace.zip
# The trace shows DOM snapshots, network, and console at each step

What dimensions should you compare?

Check each dimension systematically.

| Dimension | Local | CI | How to Fix |
|---|---|---|---|
| Node version | 20.10 | 18.17 | Pin in .nvmrc or CI config |
| Playwright version | 1.42 | 1.40 | Use same lockfile |
| Browser | Chrome 121 | Chromium 121 | Use Playwright's bundled browsers |
| OS | macOS | Linux | Account for font, rendering differences |
| Env vars | .env file | CI vault | Ensure all vars are injected |
| Base URL | localhost:3000 | staging.bank.in | Parameterize via config |
| Time zone | IST (UTC+5:30) | UTC | Set TZ explicitly |
| Auth secrets | .env | CI secrets | Verify injection |
| Test data | Seeded locally | Empty DB in CI | Create data per test |
| Network | Direct access | Behind firewall | Check connectivity |
| CPU/Memory | 16GB, 8 cores | 4GB, 2 cores | Right-size workers |

How do you fix common culprits?

Time zone: set it explicitly in CI.

YAML
# Fix time zone in CI
env:
  TZ: Asia/Kolkata
TYPESCRIPT
// Or write timezone-aware assertions
const expected = new Date().toLocaleDateString('en-IN', {
  timeZone: 'Asia/Kolkata',
});
await expect(page.getByTestId('date')).toContainText(expected);

Missing env vars: verify they are injected.

YAML
# Ensure all secrets are available
env:
  BASE_URL: ${{ vars.STAGING_URL }}
  TEST_USER: ${{ secrets.TEST_USER }}
  TEST_PASS: ${{ secrets.TEST_PASS }}

Version mismatch: pin versions.

JSON
// package.json: pin Playwright version
{
  "devDependencies": {
    "@playwright/test": "1.42.1"
  }
}

How do you reproduce the CI environment locally?

Run your tests in a container that matches CI.

BASH
# Run in a Playwright Docker container matching CI
docker run --rm -it \
  -v $(pwd):/work -w /work \
  -e TZ=UTC \
  -e BASE_URL=https://staging.nationalbank.in \
  -e TEST_USER=$TEST_USER \
  -e TEST_PASS=$TEST_PASS \
  mcr.microsoft.com/playwright:v1.42.1 \
  npx playwright test

Why is randomly editing the test a bad fix?

Three bad outcomes from random edits.

TEXT
1. HIDES REAL DEFECTS
   A genuine timezone bug also affects users.
   Adding a sleep hides it from the test but not from customers.

2. IS FRAGILE
   A sleep that happens to work today breaks under different load.
   A loosened assertion passes on a broken page.

3. DESTROYS COVERAGE
   Loosening "expect exact amount" to "expect page loaded"
   removes the financial-correctness check that gave the test value.

What is the systematic debugging process?

  1. Gather CI evidence: logs, screenshot, video, trace.
  2. Open the trace viewer: identify what failed and how.
  3. Compare environments dimension by dimension.
  4. Reproduce locally in a matching container.
  5. Fix the real environmental difference.
  6. Verify the fix in CI.

What are common interview follow-up questions?

  • "What is the most common cause?" Missing environment variables or time zone differences.
  • "How do you prevent this from happening?" Pin versions, use containers, inject all vars, set time zone explicitly.
  • "What if you cannot reproduce locally?" Add extra logging keyed by a correlation ID and wait for the next CI failure with full diagnostics.

Visual Overview

DIAGRAM
  Debugging Local-Pass-CI-Fail
  ────────────────────────────

  ┌─────────────────────────┐
  │ 1. GATHER CI EVIDENCE   │
  │    Logs, screenshot,    │
  │    video, trace         │
  └───────────┬─────────────┘
              │
  ┌───────────▼─────────────┐
  │ 2. OPEN TRACE VIEWER    │
  │    DOM snapshots        │
  │    Network requests     │
  │    Console logs         │
  │    -> Identify failure  │
  └───────────┬─────────────┘
              │
  ┌───────────▼─────────────┐
  │ 3. COMPARE ENVIRONMENTS │
  │    Versions, env vars,  │
  │    OS, TZ, data, auth,  │
  │    network, resources   │
  └───────────┬─────────────┘
              │
  ┌───────────▼─────────────┐
  │ 4. REPRODUCE LOCALLY    │
  │    Docker container     │
  │    matching CI config   │
  └───────────┬─────────────┘
              │
  ┌───────────▼─────────────┐
  │ 5. FIX THE REAL CAUSE   │
  │    Not the test!        │
  └───────────┬─────────────┘
              │
  ┌───────────▼─────────────┐
  │ 6. VERIFY IN CI         │
  └─────────────────────────┘

Key Takeaway

When a Playwright suite passes locally but fails in CI, I start from the principle that identical code behaving differently means the environments differ. I gather CI evidence first: logs, screenshots, video, and the trace. I compare environments dimension by dimension: versions, env vars, OS, data, network, auth, time zone, paths, and resources. I reproduce the failure locally by matching CI in a container. And I fix the real cause.

Randomly editing the test to make CI green is never the answer. It hides real defects, is fragile under different conditions, and loosens assertions that destroy coverage.

At the National Bank of India a transfer suite passed on a developer's Mac but failed in Linux CI. The trace showed a date assertion mismatch. Comparing environments revealed the runner was UTC while the laptop was IST, and the CI was missing the test password. I pinned the time zone, wrote timezone-aware assertions, and injected the secret. It then passed everywhere.

Remember one sentence: a local-pass-CI-fail is an environmental difference, so compare the environments and fix the real cause rather than randomly editing the test.

---

Share
Use ← → arrow keys to navigate