Your Playwright smoke suite ran on every deployment for two months, then silently stopped when a GitHub Actions runner hit its billing limit. The alert only came when a critical payment flow broke in production three days later. Playwright had no errors to surface — it just wasn't being run.
This is the silent test automation failure problem. HTTP monitors can't help because there's no endpoint to poll for test completion. The fix is heartbeat monitoring: Playwright pings a unique URL at the end of every successful run, and if Vigilmon doesn't receive that ping within the expected window, you get alerted.
This tutorial shows you how to instrument Playwright with Vigilmon heartbeat monitors so you always know when your test automation stops running.
What You'll Cover
- Heartbeat monitoring for scheduled Playwright test runs
- Catching test pipelines that stop triggering entirely
- Alerting on stuck or hanging browser workers
- Multi-project and multi-browser heartbeat monitoring
- Detecting test drift in CI/CD environments
Prerequisites
- A Playwright test suite (
@playwright/testv1.20+) - A free account at vigilmon.online
The Problem: What Standard Playwright Monitoring Misses
Playwright's built-in reporters track test results — but they won't alert you when:
- The CI job invoking Playwright is accidentally removed or disabled
- A shard configuration change causes some browsers to be skipped silently
- A
playwright.config.tschange setstestMatchto an empty pattern, running zero tests - Browser download failures cause the runner to exit 0 with no tests executed
- The scheduled pipeline pauses without any visible error
Vigilmon's heartbeat pattern closes all of these gaps.
Step 1: Create a Heartbeat Monitor in Vigilmon
A heartbeat monitor expects a ping on a regular interval. No ping → alert.
- Log in to Vigilmon and click New Monitor → Heartbeat.
- Give it a name like
Playwright — Nightly Regression. - Set the Expected interval to match your test schedule. For a nightly run, use 24 hours. For pre-merge smoke tests, use 2 hours with a 20-minute grace period.
- Set the Grace period to 1 hour for long cross-browser suites.
- Save and copy the Ping URL — it looks like
https://vigilmon.online/api/heartbeat/<unique-id>.
Step 2: Add a Global Teardown to Playwright
Playwright's globalTeardown hook runs after all tests complete and receives the full results object — the ideal place for a heartbeat ping:
// playwright/global-teardown.ts
import { FullResult } from "@playwright/test/reporter";
import * as https from "https";
function pingVigilmon(url: string): Promise<void> {
return new Promise((resolve, reject) => {
const req = https.request(url, { method: "POST" }, () => resolve());
req.on("error", reject);
req.setTimeout(10000, () => {
req.destroy(new Error("Vigilmon ping timed out"));
});
req.end();
});
}
async function globalTeardown(config: unknown, result: FullResult) {
const heartbeatUrl = process.env.VIGILMON_HEARTBEAT_URL;
if (!heartbeatUrl) return;
// Only ping when every test passed
if (result.status === "passed") {
try {
await pingVigilmon(heartbeatUrl);
console.log("Vigilmon heartbeat sent");
} catch (e) {
console.error("Vigilmon heartbeat failed:", (e as Error).message);
}
}
}
export default globalTeardown;
Register it in playwright.config.ts:
// playwright.config.ts
import { defineConfig, devices } from "@playwright/test";
export default defineConfig({
globalTeardown: require.resolve("./playwright/global-teardown"),
projects: [
{ name: "chromium", use: { ...devices["Desktop Chrome"] } },
{ name: "firefox", use: { ...devices["Desktop Firefox"] } },
{ name: "webkit", use: { ...devices["Desktop Safari"] } },
],
reporter: [["html", { open: "never" }], ["list"]],
});
The result.status is "passed" only when every test across every project passes — failures, timeouts, or interrupted runs suppress the ping.
Step 3: Run Playwright with the Heartbeat Environment Variable
Local and shell
VIGILMON_HEARTBEAT_URL="https://vigilmon.online/api/heartbeat/<id>" \
npx playwright test
GitHub Actions
# .github/workflows/playwright.yml
name: Playwright Tests
on:
schedule:
- cron: "0 1 * * *" # 01:00 UTC nightly
push:
branches: [main]
jobs:
playwright:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install Node.js
uses: actions/setup-node@v4
with:
node-version: "20"
cache: "npm"
- name: Install dependencies
run: npm ci
- name: Install Playwright browsers
run: npx playwright install --with-deps
- name: Run Playwright tests
env:
VIGILMON_HEARTBEAT_URL: ${{ secrets.VIGILMON_PW_HEARTBEAT_URL }}
run: npx playwright test
# Heartbeat fires automatically via globalTeardown on full success.
Store VIGILMON_PW_HEARTBEAT_URL under Settings → Secrets and variables → Actions.
GitLab CI
playwright:nightly:
stage: test
image: mcr.microsoft.com/playwright:v1.44.0-jammy
rules:
- if: '$CI_PIPELINE_SOURCE == "schedule"'
variables:
VIGILMON_HEARTBEAT_URL: $VIGILMON_PW_HEARTBEAT_URL
script:
- npm ci
- npx playwright test
Step 4: Monitor Individual Playwright Projects
For large suites spanning multiple browsers and devices, create one heartbeat per project:
// playwright/global-teardown.ts (multi-project version)
async function globalTeardown(config: unknown, result: FullResult) {
if (result.status !== "passed") return;
const projectHeartbeats: Record<string, string | undefined> = {
chromium: process.env.VIGILMON_CHROMIUM_HEARTBEAT_URL,
firefox: process.env.VIGILMON_FIREFOX_HEARTBEAT_URL,
webkit: process.env.VIGILMON_WEBKIT_HEARTBEAT_URL,
};
await Promise.allSettled(
Object.entries(projectHeartbeats)
.filter(([, url]) => !!url)
.map(([name, url]) =>
pingVigilmon(url!).then(() =>
console.log(`Vigilmon heartbeat sent for ${name}`)
)
)
);
}
Map monitors in Vigilmon:
| Project | Monitor name | Interval |
|---|---|---|
| chromium (nightly) | Playwright — Chrome — Nightly | 25 h |
| firefox (nightly) | Playwright — Firefox — Nightly | 25 h |
| webkit (nightly) | Playwright — Safari — Nightly | 25 h |
| Mobile Chrome (smoke) | Playwright — Mobile — Smoke | 4 h |
Step 5: Custom Reporter for Per-Test Heartbeats
For high-frequency monitoring needs, write a custom Playwright reporter that pings after specific critical test files pass:
// playwright/vigilmon-reporter.ts
import type {
Reporter,
FullConfig,
Suite,
TestCase,
TestResult,
FullResult,
} from "@playwright/test/reporter";
import * as https from "https";
class VigilmonReporter implements Reporter {
private criticalTests = new Map<string, boolean>();
private heartbeatUrls: Record<string, string> = {};
onBegin(config: FullConfig, suite: Suite) {
// Map test file → heartbeat URL via env vars
this.heartbeatUrls = {
"checkout.spec.ts": process.env.VIGILMON_CHECKOUT_HEARTBEAT || "",
"auth.spec.ts": process.env.VIGILMON_AUTH_HEARTBEAT || "",
};
}
onTestEnd(test: TestCase, result: TestResult) {
const file = test.location.file.split("/").pop() || "";
if (this.heartbeatUrls[file]) {
const passed = result.status === "passed";
this.criticalTests.set(file, (this.criticalTests.get(file) ?? true) && passed);
}
}
async onEnd(result: FullResult) {
const pings = Object.entries(this.heartbeatUrls)
.filter(([file, url]) => url && this.criticalTests.get(file) === true)
.map(([, url]) => this.ping(url));
await Promise.allSettled(pings);
}
private ping(url: string): Promise<void> {
return new Promise((resolve) => {
const req = https.request(url, { method: "POST" }, () => resolve());
req.on("error", () => resolve());
req.end();
});
}
}
export default VigilmonReporter;
Register in config:
reporter: [
["list"],
["./playwright/vigilmon-reporter.ts"],
],
Step 6: Alert Channels
In Vigilmon, go to Notifications → New Channel and configure:
- Email — immediate alert when a heartbeat is missed
- Slack webhook — ping your
#playwright-alertsor#qachannel
When a Playwright suite stops pinging:
🔴 MISSED HEARTBEAT: Playwright — Nightly Regression
Last ping: 25 hours ago
Expected interval: 24 hours
When it recovers:
✅ HEARTBEAT RECOVERED: Playwright — Nightly Regression
Gap: 25 hours
What You're Now Catching
| Silent failure mode | How Vigilmon detects it |
|---|---|
| CI job invoking Playwright disabled | No ping arrives → alert after grace period |
| testMatch glob returns zero specs | No ping on 0-test run → alert |
| Browser binary download fails silently | No tests run → no ping → alert |
| Runner machine hits billing/quota limit | No ping → alert after grace period |
| Shard configuration skips entire browser | No ping for that project → alert |
| Suite hangs on a network-flaky test | Runner timeout → no ping → alert |
Playwright is fast, reliable, and cross-browser — until it quietly stops being invoked. Vigilmon's heartbeat monitors give you the external signal that Playwright itself can't provide: a definitive alert when your test automation hasn't run in longer than expected.
Add heartbeat monitoring to your Playwright suite today — register free at vigilmon.online.