本文へ移動
cccskills
無料GitHub で公開

workbox-tests

Test Workbox-built service workers - pin behavior of the named recipes (`pageCache`, `staticResourceCache`, `imageCache`, `googleFontsCache`, `offlineFallback`, `warmStrategyCache`) per [developer.chrome.com/docs/workbox/modules/workbox-recipes][wb-recipes]; validate `workbox-precaching` manifest injection (`__WB_MANIFEST` revisioning); assert `workbox-routing` route handler matches; assert `workbox-expiration` and `workbox-cacheable-response` plugin gates; and verify the `workbox-window` registration helper events (`installed`, `waiting`, `controlling`, `activated`). Cache-strategy design (which strategy per route type, TTL + version-bump invalidation, migrating a hand-rolled sw.js to Workbox) lives in references/cache-strategy-design.md. Use when a project ships (or is adopting) a Workbox service worker and its strategy choices or recipe behavior need pinning against refactors or a version upgrade.

日本語の概要は準備中です。原文の説明を表示しています。

testland/qaスター 13(GitHub 13 + cccskills 0)
連携ツールPlaywright

実行スコア 未検証

インストール方法を見る

含まれるファイル(4)

  • SKILL.md14.1 KB
  • .tessl-plugin/plugin.json1.0 KB
  • references/cache-strategy-design.md7.2 KB
  • references/recipe-tests.md3.6 KB

SKILL.md(原文)

インストールする前に、エージェントに与えられる指示の中身を確認できます。

workbox-tests

Overview

This skill tests Workbox-built service workers: assert that an already-shipped Workbox SW behaves the way its recipes claim, using the workbox-precaching / workbox-routing / workbox-strategies / workbox-recipes / workbox-window packages per developer.chrome.com/docs/workbox/modules. The design-side counterpart - choosing a strategy per route type and authoring the Workbox strategy code before pinning it - is in references/cache-strategy-design.md.

Pinned version

Time-sensitive pin - re-check at wb-gh on upgrade: Workbox v7.4.1 (released May 2026).

When to use

  • A PWA already uses Workbox and tests need to lock its behavior against future refactors.
  • Migrating from Workbox v6 → v7 - assert each recipe behaves the same on the new release.
  • A "stale forever" bug report - pin the workbox-expiration plugin's TTL with a test before patching.
  • A workbox-precaching injection drifted (build emits wrong __WB_MANIFEST) - assert the precache manifest shape in CI.

Choosing a cache strategy

When the SW under test doesn't exist yet, or a hand-rolled sw.js is being migrated to Workbox, start with references/cache-strategy-design.md: the strategy-per-route-type table (CacheFirst / NetworkFirst / StaleWhileRevalidate / NetworkOnly), worked Workbox strategy code with ExpirationPlugin + CacheableResponsePlugin, the matching Playwright assertions, an audit table classifying existing hand-rolled fetch handlers, and invalidation locking (SW_VERSION bump + user-opt-in skipWaiting).

Authoring

Step 1 - Install test dependencies

Workbox ships no first-party test runner; the canonical pairing is Playwright (for runtime SW assertions) plus a unit-test runner (Vitest or Jest) for the workbox-window page-side helper:

npm install --save-dev @playwright/test vitest
# Workbox itself is already a runtime dep at this point

Step 2 - Decide where each assertion lives

SubjectRunnerWhy
Precache manifest shape (__WB_MANIFEST)Vitest reading the built sw.js artifactStatic; no browser needed
Recipe behavior at runtime (pageCache, imageCache)PlaywrightNeeds a real caches API + fetch interception
workbox-window events on the pagePlaywright (page side)Listens on wb.addEventListener(...) from page code
Plugin TTL / quota (workbox-expiration)Playwright with clock manipulationNeeds the SW to actually call the plugin's pruning logic

Step 3 - Author the precache-manifest static assertion

// tests/precache-manifest.spec.ts
import { describe, it, expect } from 'vitest';
import { readFileSync } from 'node:fs';

describe('workbox-precaching manifest', () => {
  it('emits the __WB_MANIFEST entries with revision strings', () => {
    const sw = readFileSync('./dist/sw.js', 'utf8');
    // workbox-precaching tokens; per wb-modules
    expect(sw).toMatch(/precacheAndRoute\s*\(/);
    // Build-tool injects __WB_MANIFEST as an array of { url, revision } records
    const manifest = sw.match(/self\.__WB_MANIFEST\s*=\s*(\[[^;]+\])/)?.[1];
    expect(manifest).toBeDefined();
    const entries = JSON.parse(manifest!);
    expect(Array.isArray(entries)).toBe(true);
    for (const entry of entries) {
      expect(typeof entry.url).toBe('string');
      // Hashed filenames carry revision: null; non-hashed must have a revision string
      const isHashed = /\.[a-f0-9]{8,}\./.test(entry.url);
      if (!isHashed) expect(typeof entry.revision).toBe('string');
    }
  });
});

precacheAndRoute() is the entry point exported from workbox-precaching per wb-modules - it precaches a file set and manages updates to those files.

Step 4 - Author per-recipe runtime tests

Each named recipe has a documented default per wb-recipes; pin those defaults with tests. imageCache() is a cache-first strategy with defaults of 60 images cached for 30 days - pin the 60-entry cap:

import { test, expect } from '@playwright/test';

test('imageCache() applies the 60-entry default cap', async ({ context, page }) => {
  await page.goto('https://localhost:3000/gallery');
  await page.waitForLoadState('networkidle');

  // Force 61 distinct image requests
  for (let i = 0; i < 61; i++) {
    await page.evaluate((n) => fetch(`/img/test-${n}.png`).catch(() => {}), i);
  }

  // Wait for ExpirationPlugin to prune (it runs async)
  await page.waitForTimeout(500);

  let [sw] = context.serviceWorkers();
  if (!sw) sw = await context.waitForEvent('serviceworker');

  const count = await sw.evaluate(async () => {
    const cacheName = (await caches.keys()).find(k => k.includes('image'));
    if (!cacheName) return 0;
    return (await (await caches.open(cacheName)).keys()).length;
  });
  expect(count).toBeLessThanOrEqual(60);
});

The other five recipe tests - pageCache() (network-first, 3s network-timeout default), offlineFallback() (offline.html default), googleFontsCache() (30 fonts / 1 year), staticResourceCache() (stale-while-revalidate), and warmStrategyCache() (warms declared URLs on install) - are in references/recipe-tests.md, each retaining its test-invariant default.

Step 5 - Author workbox-window event tests

workbox-window is the page-side companion for registering the SW, managing updates, and responding to lifecycle events per wb-modules. It emits installed, waiting, controlling, activated, and redundant. Listen on each from the page context:

test('wb.addEventListener installed fires after register()', async ({ page }) => {
  await page.goto('https://localhost:3000/');

  const events = await page.evaluate(() => new Promise<string[]>((resolve) => {
    // @ts-expect-error workbox-window global from the page bundle
    const wb = new window.Workbox('/sw.js');
    const fired: string[] = [];
    wb.addEventListener('installed',   () => fired.push('installed'));
    wb.addEventListener('waiting',     () => fired.push('waiting'));
    wb.addEventListener('controlling', () => fired.push('controlling'));
    wb.addEventListener('activated',   () => fired.push('activated'));
    wb.register();
    setTimeout(() => resolve(fired), 3000);
  }));

  // First install fires installed + activated; waiting only fires on update with a controller already present
  expect(events).toContain('installed');
  expect(events).toContain('activated');
});

The five-event vocabulary is enumerated in wb-modules under workbox-window.

Step 6 - Test the cacheable-response plugin gate

Per wb-modules, workbox-cacheable-response restricts which requests are cached by response status code or headers. A common config is statuses: [200]. Assert that a 404 is not cached:

test('CacheableResponsePlugin excludes non-200 from cache', async ({ context, page }) => {
  await page.goto('https://localhost:3000/');
  await page.evaluate(() => fetch('/api/known-404').catch(() => {}));
  await page.waitForTimeout(300);

  let [sw] = context.serviceWorkers();
  if (!sw) sw = await context.waitForEvent('serviceworker');

  const cachedKnown404 = await sw.evaluate(async () => {
    for (const name of await caches.keys()) {
      const c = await caches.open(name);
      for (const req of await c.keys()) {
        if (req.url.endsWith('/api/known-404')) return true;
      }
    }
    return false;
  });
  expect(cachedKnown404).toBe(false);
});

Running

Locally

npm run build           # produces dist/sw.js with __WB_MANIFEST injected
npx vitest run tests/precache-manifest.spec.ts
npx playwright test tests/workbox-recipes.spec.ts

The build step is non-optional - workbox-precaching only emits the precache manifest at build time per wb-modules (workbox-build / workbox-webpack-plugin / workbox-cli).

In CI

jobs:
  workbox-tests:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v5
      - uses: actions/setup-node@v4
        with:
          node-version: 20
      - run: npm ci
      - run: npm run build
      - run: npx playwright install --with-deps chromium
      - run: npx vitest run tests/precache-manifest.spec.ts
      - run: npx playwright test tests/workbox-recipes.spec.ts

The unit (Vitest) step gates fast; the Playwright recipe step catches the runtime-only regressions.

Parsing results

Workbox runtime caches are observable via three surfaces:

SurfaceWhat it showsHow to read
caches.keys()All cache namespaces (e.g. workbox-precache-v2, pages, images)sw.evaluate(() => caches.keys())
caches.open(name).keys()URLs cached in a namespaceFilter by URL pattern to assert what the recipe captured
Playwright page.on('request')Network egress per requestEmpty for cache-hit served paths = recipe working

When an assertion fails on the cache-content surface, also check the namespace name: Workbox v7 uses workbox-precache-v2 for precaching and recipe-default names (pages, images, static-resources, google-fonts-stylesheets, google-fonts-webfonts) for recipes unless overridden via cacheName option per wb-recipes.

CI integration

For projects that ship Workbox: lock both the precache manifest and one runtime recipe behavior per PR.

- name: Workbox unit + e2e
  run: |
    npm run build
    npx vitest run tests/precache-manifest.spec.ts
    npx playwright test tests/workbox-recipes.spec.ts

For projects that publish a service worker as part of a release artifact (separate from app deploy), gate the release on the same two steps - a Workbox regression that escapes to prod usually manifests as stale-forever or never-installed, both invisible without test coverage.

Anti-patterns

Anti-patternWhy it failsFix
Assert recipe behavior only on first page loadCache is empty; SW hasn't been installed yetPre-warm by visiting twice (or use await page.waitForLoadState('networkidle'))
Assert caches.keys() includes a fixed namespace namePer wb-recipes, default names can be overridden via cacheNameMatch by suffix substring (name.endsWith('-precache-v2'))
Use Vitest with a JSDOM-mocked caches for recipe behaviorJSDOM does not implement Cache Storage faithfully; ServiceWorkerRegistration is absentUse Playwright for runtime recipe assertions (Step 4)
Assume precacheAndRoute(self.__WB_MANIFEST) works without a bundler__WB_MANIFEST is injected at build time per wb-modules; CDN-served workbox-sw skips itIf using workbox-sw (CDN loader per wb-modules), drop the precache assertion
Test 60-entry cap by checking caches.match returnsExpirationPlugin prunes async; tight await returns stale stateAdd await page.waitForTimeout(500) after the trigger (Step 4 imageCache() test)
Skip the workbox-window event tests entirelyThe page-side "update available" UX is built on these events; breaks silentlyStep 5 covers the five-event vocabulary

Limitations

  • Per-recipe defaults can drift across Workbox majors. The 60-image / 30-day / 1-year numbers cited above are the v7.x defaults per wb-recipes; consult the recipe page at the pinned Workbox version before treating the numbers as test invariants.
  • __WB_MANIFEST is build-tool-injected. Tests against the static dist/sw.js only pass when the bundler ran - local vitest run against src/sw.js will fail to find the array.
  • Cache Storage quota is browser-internal. Workbox's ExpirationPlugin maxEntries is asserted here; the browser's own quota (Step 4 of offline-fallback-tests) is a separate ceiling not testable from workbox-* alone.
  • workbox-window's waiting event only fires on update. A test that asserts waiting on first install will fail by design - see wb-modules for the per-event firing conditions.
  • CDN-served workbox-sw sidesteps precaching entirely per wb-modules; this skill's Step 3 assertion does not apply to CDN-loader projects.

References

  • Workbox overview ("Production-ready service worker libraries and tooling") - wb-overview.
  • Workbox modules (per-package one-line descriptions; the workbox-precaching / workbox-window / workbox-routing / workbox-strategies / workbox-recipes family) - wb-modules.
  • Workbox recipes (pageCache, staticResourceCache, imageCache, googleFontsCache, offlineFallback, warmStrategyCache with defaults) - wb-recipes.
  • Workbox repo (v7.4.1 release, May 2026) - wb-gh.
  • Strategy design + authoring (per-route strategy table, audit of hand-rolled SWs, invalidation locking) - references/cache-strategy-design.md.
  • Generic context.serviceWorkers() Playwright patterns - the service-worker-lifecycle-tests references.
  • Sibling skills: offline-fallback-tests, service-worker-lifecycle-tests.

レビュー

まだレビューはありません。使ってみた感想をお寄せください。

コメント

ログインなしで書き込めます。使ってみた感想、質問、動かなかった環境などを気軽にどうぞ。

まだコメントはありません。最初のコメントを書いてみませんか。

送信するとすぐに公開されます。メールアドレスや電話番号などの個人情報は書かないでください。URL は 2 つまでです。

0 / 1000 文字

このフォームは reCAPTCHA で保護されており、Google のプライバシーポリシーと利用規約が適用されます。

同じリポジトリのスキル

Builds a CI gate that fails the build on **new** WCAG / a11y violations introduced by a PR while grandfathering pre-existing violations on a per-rule / per-page baseline. Aggregates verdicts from axe-core / pa11y / Lighthouse a11y / WAVE / IBM Equal Access scans. Use when a project has accumulated a11y debt and a strict "zero violations" gate would block every PR - the ratchet pattern lets the team ship while preventing regressions.

日本語の概要は準備中です。原文の説明を表示しています。

testland/qa132026年10月20日 更新

Workflow skill that builds an A/B-test validity checklist from an experiment proposal, walking the canonical design-correctness gates - pre-registered OEC/power/guardrails, randomization unit + SRM check, assignment integrity, telemetry, peeking discipline, novelty/primacy, post-experiment SRM re-check - into a per-experiment checklist + sign-off form. Use when launching, auditing, or governing an experiment. For pitfall mechanics (guardrails, peeking) see experiment-results-interpreter's references; to read an already-valid result use experiment-results-interpreter; for per-SDK harness tests use experiment-sdk-testing - this gates DESIGN, not SDK code.

日本語の概要は準備中です。原文の説明を表示しています。

testland/qa132026年10月20日 更新

The single install-flow skill: the reference contract for the PWA install flow (installability gate fields, `beforeinstallprompt` handshake, per-platform paths - Android WebAPK / iOS Share menu / Firefox no-op - and the `display-mode` post-install signal, in references/install-flow-reference.md) plus the build-an-X workflow that emits the Add-to-Home-Screen suite. Walks the four-stage timeline, emitting one test per gate cell per [web.dev/articles/install-criteria][install-criteria], the deferred-prompt → `prompt()` → `userChoice` chain per [web.dev/articles/customize-install][customize-install], the iOS Safari manual-metadata branch (`apple-touch-icon`) per [web.dev/learn/pwa/installation][learn-pwa], and the post-install `(display-mode: standalone)` MQ assertion. Output: a Playwright spec with per-stage cells plus a coverage matrix. Use when a PWA's manifest, icons, or install handler change, when install conversion drops at an unknown stage, or when triaging a flaky install assertion.

日本語の概要は準備中です。原文の説明を表示しています。

testland/qa132026年10月20日 更新

Build-an-X workflow that uses an LLM to map existing tests to spec sections - given a spec doc + the test suite, the LLM identifies which tests cover which sections, surfaces uncovered sections (gap), and recommends specific tests to add. Output is a coverage matrix per spec ID. Scope is mapping tests that already exist and naming the gaps, not authoring tests for new acceptance criteria. Use when a spec doc and a test suite both exist but nobody can say which requirements are actually covered - before a release sign-off, an audit, or a decision about where to spend the next round of test effort.

日本語の概要は準備中です。原文の説明を表示しています。

testland/qa132026年10月20日 更新

Generates tests from natural-language specs (acceptance criteria, user stories, requirements) using an LLM, with confidence scoring per test case (LLM self-assessment plus heuristics: assertion quality, naming, completeness), batching uncertain cases for human review, and integration with the team's existing test framework. Use when the user asks to generate unit tests from acceptance criteria, convert user stories to test cases, automate test creation from requirements, or augment a spec-driven test suite with AI-generated stubs that are then curated before merge.

日本語の概要は準備中です。原文の説明を表示しています。

testland/qa132026年10月20日 更新

Configures Allure Report (test-runner adapter install, `allure-results` directory wiring, `categories.json` for failure classification, `history-trend.json` retention via the copy-history-between-runs pattern), runs the Allure CLI to convert `allure-results` to a static HTML site, and uploads the report as a CI artifact. Use when the team needs richer test reporting than JUnit XML - step-level attachments, per-test history, retry tracking, and severity / epic / feature labeling across framework-agnostic adapters (pytest, Jest, JUnit, TestNG, NUnit, Mocha). As a rich static HTML report generator, it is the open-source alternative to the sunset ExtentReports (JVM/.NET per-test HTML narrative); for hosted cross-run flakiness analytics rather than a static per-run report use currents-integration.

日本語の概要は準備中です。原文の説明を表示しています。

testland/qa132026年10月20日 更新

testland のスキルをすべて見る

このスキルの問題を報告する