# 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`](packages/playwright/README.md) | Playwright | | [`@get-skipper/jest`](packages/jest/README.md) | Jest | | [`@get-skipper/vitest`](packages/vitest/README.md) | Vitest | | [`@get-skipper/cypress`](packages/cypress/README.md) | Cypress | | [`@get-skipper/nightwatch`](packages/nightwatch/README.md) | Nightwatch.js | ## How It Works - **`read-only` mode** (default): Skipper reads the spreadsheet at the start of the test run and skips any test whose `disabledUntil` date is in the future. - **`sync` mode** (`SKIPPER_MODE=sync`): Same as read-only, plus after the run Skipper reconciles the spreadsheet — adding rows for new tests and, when `SKIPPER_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 ```ts // 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: ```ts { 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: ```ts { 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 1. Go to [Google Cloud Console](https://console.cloud.google.com/) 2. Create a new project (or select an existing one) 3. Navigate to **APIs & Services → Library** 4. Search for **Google Sheets API** and enable it ### 2. Create a Service Account 1. Navigate to **APIs & Services → Credentials** 2. Click **Create Credentials → Service Account** 3. Give it a name (e.g. `skipper-bot`) and click **Done** 4. Click on the service account → **Keys** tab → **Add Key → Create new key → JSON** 5. 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-only` mode - **Editor** role for `sync` mode ### 4. Prepare Credentials for CI Convert the JSON file to a base64 string for use as a CI secret: ```bash 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`: ```ts 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: ```bash SKIPPER_MODE=sync SKIPPER_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. ```bash SKIPPER_SPREADSHEET_ID= pnpm test ``` ### sync (CI on merge to main) ```bash SKIPPER_MODE=sync SKIPPER_SPREADSHEET_ID= pnpm test ``` After the run: - New tests → added to the spreadsheet with empty `disabledUntil` - Removed tests → warned (set `SKIPPER_SYNC_ALLOW_DELETE=true` to also delete them) ## CI Example (GitHub Actions) ```yaml # .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](LICENSE).