Test Management > skipper
Real-time test execution control via Google Spreadsheet, enabling instant toggle without code changes.
Skipper
Enable and disable tests directly from a Google Spreadsheet — no code changes required.
Skipper integrates with your existing test suite via a minimal config. Each test is identified by its file path + title. Set a disabledUntil date in the spreadsheet to skip a test until that date; leave it empty (or set a past date) to run it normally.
Packages
| Package | Framework |
|---|---|
@get-skipper/playwright |
Playwright |
@get-skipper/jest |
Jest |
@get-skipper/vitest |
Vitest |
@get-skipper/cypress |
Cypress |
@get-skipper/nightwatch |
Nightwatch.js |
How It Works
read-onlymode (default): Skipper reads the spreadsheet at the start of the test run and skips any test whosedisabledUntildate is in the future.syncmode (SKIPPER_MODE=sync): Same as read-only, plus after the run Skipper reconciles the spreadsheet — adding rows for new tests and, whenSKIPPER_SYNC_ALLOW_DELETE=true, removing rows for tests no longer in the suite.
Spreadsheet Schema
Create a Google Spreadsheet with the following columns in the first row (header):
testId |
disabledUntil |
notes |
|---|---|---|
tests/auth/login.spec.ts > login > should log in |
||
tests/checkout/payment.spec.ts > payment > stripe |
2026-04-01 |
Flaky until fix |
tests/payments/refund.spec.ts > refund |
2099-12-31 |
Disabled indefinitely |
testId—{relative file path} > {describe blocks} > {test name}, automatically generated by the plugin.disabledUntil— ISO 8601 date (e.g.2026-04-01). Empty or past date = test runs. Future date = test is skipped.notes— optional free-text field, ignored by the plugin.
Configuration Reference
All Skipper plugins accept the same SkipperConfig object:
| Option | Type | Required | Default | Description |
|---|---|---|---|---|
spreadsheetId |
string |
✅ | — | The Google Spreadsheet ID (from the URL) |
credentials |
object | ✅ | — | Service account credentials — see below |
sheetName |
string |
first tab | Name of the sheet tab to read/write | |
referenceSheets |
string[] |
[] |
Additional sheet tabs to read (read-only); merged with the primary sheet | |
testIdColumn |
string |
"testId" |
Column header for the test identifier | |
disabledUntilColumn |
string |
"disabledUntil" |
Column header for the disable date |
Credentials
// Base64-encoded JSON (recommended for CI)
credentials: { credentialsBase64: process.env.GOOGLE_CREDS_B64! }
// Path to a local JSON file (recommended for local dev)
credentials: { credentialsFile: './service-account.json' }
// Raw service account object
credentials: { client_email: '...', private_key: '...' }
sheetName
By default Skipper uses the first tab of the spreadsheet. Use sheetName to target a specific tab:
{
spreadsheetId: '...',
credentials: { credentialsBase64: '...' },
sheetName: 'E2E Tests', // reads and writes to this tab only
}
referenceSheets
Additional sheet tabs to read (never written to). Useful for sharing a common list of disabled tests across multiple suites. When the same testId appears in more than one sheet, the most restrictive (latest future) disabledUntil wins:
{
spreadsheetId: '...',
credentials: { credentialsBase64: '...' },
sheetName: 'E2E Tests', // primary sheet (read + write in sync mode)
referenceSheets: ['Shared'], // additional sheets (read-only)
}
Google Sheets Setup
1. Create a Google Cloud Project and enable the API
- Go to Google Cloud Console
- Create a new project (or select an existing one)
- Navigate to APIs & Services → Library
- Search for Google Sheets API and enable it
2. Create a Service Account
- Navigate to APIs & Services → Credentials
- Click Create Credentials → Service Account
- Give it a name (e.g.
skipper-bot) and click Done - Click on the service account → Keys tab → Add Key → Create new key → JSON
- Download the JSON file — keep it secret
3. Share the Spreadsheet
Open your Google Spreadsheet and share it with the service account email (found in the JSON file as client_email):
- Viewer role for
read-onlymode - Editor role for
syncmode
4. Prepare Credentials for CI
Convert the JSON file to a base64 string for use as a CI secret:
base64 -i service-account.json | tr -d '\n'
Save the output as a secret named GOOGLE_CREDS_B64 in your CI environment (e.g. GitHub Actions secrets).
For local development, keep the JSON file and reference it via credentialsFile:
credentials: { credentialsFile: './service-account.json' }
5. Populate the Spreadsheet for the First Time
Run your test suite in sync mode once to auto-populate all test rows:
SKIPPER_MODE=sync SKIPPER_SPREADSHEET_ID=<your-spreadsheet-id> pnpm test
All tests will be added to the spreadsheet with an empty disabledUntil (enabled by default). You can then set dates in the spreadsheet to disable specific tests.
Environment Variables
| Variable | Default | Description |
|---|---|---|
SKIPPER_MODE |
read-only |
Set to sync to enable spreadsheet reconciliation after the test run |
SKIPPER_FAIL_OPEN |
true |
When true, runs all tests if the API is unreachable and no valid cache exists. Set to false to crash instead |
SKIPPER_CACHE_TTL |
300 |
Seconds a local .skipper-cache.json is considered valid. Skipper writes this file after each successful fetch and reads it as a fallback on API failure |
SKIPPER_SYNC_ALLOW_DELETE |
false |
When false, orphaned rows (tests removed from the suite) are only warned about. Set to true to delete them |
SKIPPER_DEBUG |
— | Set to any truthy value to enable verbose logging |
Modes
read-only (default)
No env var needed. Tests with a future disabledUntil are skipped.
SKIPPER_SPREADSHEET_ID=<id> pnpm test
sync (CI on merge to main)
SKIPPER_MODE=sync SKIPPER_SPREADSHEET_ID=<id> pnpm test
After the run:
- New tests → added to the spreadsheet with empty
disabledUntil - Removed tests → warned (set
SKIPPER_SYNC_ALLOW_DELETE=trueto also delete them)
CI Example (GitHub Actions)
# .github/workflows/test.yml
name: Test
on:
pull_request:
push:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v3
- uses: actions/setup-node@v4
with:
node-version: 20
cache: pnpm
- run: pnpm install
- name: Test (read-only on PR)
if: github.event_name == 'pull_request'
env:
SKIPPER_SPREADSHEET_ID: ${{ secrets.SKIPPER_SPREADSHEET_ID }}
GOOGLE_CREDS_B64: ${{ secrets.GOOGLE_CREDS_B64 }}
run: pnpm test
- name: Test + sync (on merge to main)
if: github.ref == 'refs/heads/main'
env:
SKIPPER_MODE: sync
SKIPPER_SPREADSHEET_ID: ${{ secrets.SKIPPER_SPREADSHEET_ID }}
GOOGLE_CREDS_B64: ${{ secrets.GOOGLE_CREDS_B64 }}
run: pnpm test
License
MIT — see LICENSE.