Your WebdriverIO nightly suite has been reporting green for weeks. What nobody realized was that it stopped actually running two weeks ago — a YAML indentation error in the CI config caused the job to be skipped silently. All tests passed because no tests ran. The regression was discovered in production.
This is the silent test automation failure problem. Status badges only reflect the last run; they can't tell you when runs stop happening. The fix is heartbeat monitoring: WebdriverIO 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 WebdriverIO with Vigilmon heartbeat monitors so you always know when your browser automation stops running.
What You'll Cover
- Heartbeat monitoring for scheduled WebdriverIO test runs
- Catching suites that stop triggering entirely
- Alerting on hanging browser sessions
- Multi-capability and multi-environment monitoring
- Detecting suite drift in remote grid and CI environments
Prerequisites
- A WebdriverIO test suite (WDIO v8+)
- A free account at vigilmon.online
The Problem: What Standard WebdriverIO Monitoring Misses
WebdriverIO's built-in reporters record test outcomes — but they won't alert you when:
- The cron job or CI trigger invoking
wdiois removed or disabled - A misconfigured
specsglob pattern matches zero files (suite exits 0) - A Selenium Grid or Appium server goes offline and WDIO fails to queue work
- Browser session acquisition hangs indefinitely without a runner timeout
- A test environment variable is missing, causing silent capability fallback
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
WebdriverIO — Nightly Regression. - Set the Expected interval to match your test schedule. For a nightly suite, use 24 hours. For pre-deploy 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: Create a Vigilmon Reporter Plugin
WebdriverIO's service and reporter hooks are the cleanest integration points. Create a custom reporter:
// wdio/vigilmon-reporter.js
const https = require("https");
function pingVigilmon(url) {
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();
});
}
class VigilmonReporter {
constructor(options) {
this.heartbeatUrl = process.env.VIGILMON_HEARTBEAT_URL;
this.hasFailed = false;
}
onTestFail() {
this.hasFailed = true;
}
onTestError() {
this.hasFailed = true;
}
async onRunnerEnd(stats) {
if (!this.heartbeatUrl) return;
const allPassed =
!this.hasFailed &&
stats.failures === 0 &&
(stats.retries === undefined || stats.retries === 0);
if (allPassed) {
try {
await pingVigilmon(this.heartbeatUrl);
console.log("\nVigilmon heartbeat sent");
} catch (e) {
console.error("\nVigilmon heartbeat failed:", e.message);
}
}
}
}
module.exports = VigilmonReporter;
Register the reporter in wdio.conf.js:
// wdio.conf.js
const VigilmonReporter = require("./wdio/vigilmon-reporter");
exports.config = {
runner: "local",
specs: ["./test/specs/**/*.js"],
capabilities: [
{
browserName: "chrome",
"goog:chromeOptions": { args: ["--headless", "--no-sandbox"] },
},
],
reporters: [
"spec",
[VigilmonReporter, {}],
],
framework: "mocha",
mochaOpts: {
ui: "bdd",
timeout: 60000,
},
};
Step 3: Use the WDIO Service Hook (Alternative Approach)
For suites where individual test outcomes are less important than overall suite health, a WDIO service is simpler:
// wdio/services/VigilmonService.js
const https = require("https");
class VigilmonService {
constructor(options, capabilities, config) {
this.heartbeatUrl = process.env.VIGILMON_HEARTBEAT_URL;
this.failed = false;
}
beforeTest(test, context) {}
afterTest(test, context, { error }) {
if (error) this.failed = true;
}
onComplete(exitCode, config, capabilities, results) {
if (!this.heartbeatUrl) return;
if (exitCode !== 0 || this.failed) return;
// Synchronous ping using Node child_process since onComplete may not await
const { execSync } = require("child_process");
try {
execSync(
`curl -fsS -X POST "${this.heartbeatUrl}" --max-time 10 --retry 3`,
{ stdio: "ignore" }
);
console.log("Vigilmon heartbeat sent");
} catch (e) {
console.error("Vigilmon heartbeat failed:", e.message);
}
}
}
module.exports = VigilmonService;
Register in wdio.conf.js:
services: [
["./wdio/services/VigilmonService", {}],
],
Step 4: Add Heartbeat Monitoring to CI Pipelines
GitHub Actions
# .github/workflows/wdio.yml
name: WebdriverIO Tests
on:
schedule:
- cron: "0 1 * * *" # 01:00 UTC nightly
push:
branches: [main]
jobs:
wdio:
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 Chrome
uses: browser-actions/setup-chrome@latest
- name: Start application
run: npm run start:ci &
- name: Wait for app
run: npx wait-on http://localhost:3000 --timeout 30000
- name: Run WebdriverIO tests
env:
VIGILMON_HEARTBEAT_URL: ${{ secrets.VIGILMON_WDIO_HEARTBEAT_URL }}
run: npx wdio run wdio.conf.js
# Heartbeat fires automatically via the VigilmonReporter on full success.
Store VIGILMON_WDIO_HEARTBEAT_URL under Settings → Secrets and variables → Actions.
Running against Selenium Grid
For remote Selenium Grid execution, also monitor the Grid hub directly with an HTTP monitor (see the Selenium Grid tutorial):
// wdio.conf.js (remote grid)
exports.config = {
hostname: process.env.SELENIUM_GRID_HOST || "localhost",
port: 4444,
path: "/wd/hub",
capabilities: [
{ browserName: "chrome" },
{ browserName: "firefox" },
],
// ... rest of config
};
Set the heartbeat URL per capability run:
SELENIUM_GRID_HOST=grid.internal \
VIGILMON_HEARTBEAT_URL="https://vigilmon.online/api/heartbeat/<id>" \
npx wdio run wdio.conf.js
Step 5: Multi-Capability Heartbeat Monitoring
For suites that test multiple browsers or devices, create one heartbeat monitor per capability group:
| Capability | Monitor name | Interval |
|---|---|---|
| Chrome desktop (nightly) | WDIO — Chrome — Nightly | 25 h |
| Firefox desktop (nightly) | WDIO — Firefox — Nightly | 25 h |
| Mobile emulation (nightly) | WDIO — Mobile — Nightly | 25 h |
| Chrome smoke (pre-deploy) | WDIO — Smoke — Chrome | 2 h |
Parameterize with environment variables:
// wdio.conf.chrome.js
process.env.VIGILMON_HEARTBEAT_URL = process.env.VIGILMON_CHROME_HEARTBEAT_URL;
const base = require("./wdio.conf");
base.config.capabilities = [{ browserName: "chrome" }];
module.exports = base;
# Chrome run
VIGILMON_CHROME_HEARTBEAT_URL="https://vigilmon.online/api/heartbeat/<chrome-id>" \
npx wdio run wdio.conf.chrome.js
# Firefox run
VIGILMON_FIREFOX_HEARTBEAT_URL="https://vigilmon.online/api/heartbeat/<firefox-id>" \
npx wdio run wdio.conf.firefox.js
Step 6: Detect Test Suite Drift with Spec Count Validation
Protect against the "zero specs matched" silent success by adding a spec count check to the reporter:
// Add to VigilmonReporter
async onRunnerEnd(stats) {
if (!this.heartbeatUrl) return;
const specsRan = stats.specs || 0;
const minSpecs = parseInt(process.env.VIGILMON_MIN_SPECS || "1", 10);
if (specsRan < minSpecs) {
console.warn(
`Vigilmon: skipping heartbeat — only ${specsRan} spec(s) ran (minimum: ${minSpecs})`
);
return;
}
if (!this.hasFailed && stats.failures === 0) {
await pingVigilmon(this.heartbeatUrl).catch(console.error);
}
}
Set the minimum expected spec count:
VIGILMON_MIN_SPECS=50 VIGILMON_HEARTBEAT_URL="..." npx wdio run wdio.conf.js
If the spec glob breaks and zero files are found, the heartbeat is suppressed even though WDIO exits 0 — Vigilmon will alert after the grace period.
Step 7: Alert Channels
In Vigilmon, go to Notifications → New Channel and configure:
- Email — immediate alert when a heartbeat is missed
- Slack webhook — ping your
#qa-alertsor#wdio-testschannel
When a suite stops pinging:
🔴 MISSED HEARTBEAT: WebdriverIO — Nightly Regression
Last ping: 26 hours ago
Expected interval: 24 hours
When it recovers:
✅ HEARTBEAT RECOVERED: WebdriverIO — Nightly Regression
Gap: 26 hours
What You're Now Catching
| Silent failure mode | How Vigilmon detects it |
|---|---|
| CI trigger for WDIO is removed/disabled | No ping arrives → alert after grace period |
| Specs glob matches zero files (exits 0) | VIGILMON_MIN_SPECS check suppresses ping → alert |
| Selenium Grid unreachable, sessions queue forever | Suite timeout → no ping → alert |
| Browser session hangs on a slow network | Runner timeout → no ping → alert |
| Capability fallback silently skips browsers | Per-capability monitors catch missing pings |
| Test environment variable missing | Suite fails → no ping → alert |
WebdriverIO gives you flexible, multi-browser test automation — until the runner quietly disappears from your pipeline. Vigilmon's heartbeat monitors give you the external signal that WebdriverIO itself can't provide: a definitive alert when your browser automation hasn't run in longer than expected.
Add heartbeat monitoring to your WebdriverIO suite today — register free at vigilmon.online.