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

offline-fallback-tests

Build-an-X workflow that emits the offline-fallback test suite. Walks the eight Jake Archibald offline recipes per [web.dev/articles/offline-cookbook][off-cookbook] (`Cache only`, `Network only`, `Cache, falling back to network`, `Cache and network race`, `Network falling back to cache`, `Cache then network`, `Generic fallback`, `Service Worker side templating`), maps each recipe to its assertion shape, layers the Workbox `offlineFallback()` recipe per [developer.chrome.com/docs/workbox/modules/workbox-recipes][wb-recipes], and pins the offline storage strategy (Cache Storage vs IndexedDB vs Storage Manager `persist()`/`estimate()`) per [web.dev/learn/pwa/offline-data][off-data]. Output: a Playwright spec file with one test per route's chosen recipe + a coverage matrix mapping recipes to URL patterns. Use when a route's SW caching behavior is being chosen or changed, or when a "doesn't work offline" report needs the failing route's recipe localized.

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

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

実行スコア 未検証

インストール方法を見る

含まれるファイル(3)

  • SKILL.md15.2 KB
  • .tessl-plugin/plugin.json1.1 KB
  • references/recipes.md4.0 KB

SKILL.md(原文)

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

offline-fallback-tests

Overview

Offline behavior fragments into eight named recipes per Jake Archibald's offline cookbook off-cookbook - and most PWAs use three or four of them across different routes. A test suite that asserts "the site works offline" misses the per-route recipe contract: a route on Cache only must serve from cache even when network is fine; a route on Network falling back to cache must prefer network when both are present.

This skill emits the per-route test suite. The output is a Playwright spec file plus a coverage YAML that maps each URL pattern to its recipe and its assertion. The suite is distinct from the strategy authoring covered by workbox-tests (references/cache-strategy-design.md); that side authors the strategy, this builder generates the verification.

Composes with:

  • workbox-tests - when the SW uses Workbox's offlineFallback() recipe, the workbox-tests spec covers the recipe's runtime; this builder covers the per-route decision and the page-side assertion.
  • service-worker-lifecycle-tests - every recipe assumes an active SW; the lifecycle spec is the prerequisite for this one.
  • add-to-homescreen-flow-tests (references/install-flow-reference.md) - install-gate Stage 1's service-worker prerequisite cell is the same SW this builder tests offline behavior of.

When to use

  • New PWA - author the offline-fallback test plan before any route ships SW caching.
  • "Site doesn't work offline" bug report - the recipe matrix localizes whether the recipe is wrong or just missing for that route.
  • Migrating from hand-rolled caches.match / event.respondWith to Workbox offlineFallback() - assert that the recipe-named behavior matches the hand-rolled one.
  • Adding a new offline-capable feature (e.g. saved articles, draft form data) - pick a recipe per the matrix and emit the test.

Workflow

Step 1 - Inventory routes and pick a recipe per route

For each URL pattern the SW intercepts, decide which of the eight named recipes per off-cookbook applies. The cookbook recipes verbatim:

Recipe (per off-cookbook)When to use (quoted)
Cache only"Use for static assets you've cached during installation that your site depends on."
Network only"Best for requests without offline equivalents, like analytics or non-GET operations."
Cache, falling back to network"Ideal for building offline-first apps where cached content serves as the default."
Cache and network race"Useful when disk access is slow and network speed might win the competition for small assets."
Network falling back to cache"Good for frequently updating resources, though slow connections create user frustration."
Cache then network"Perfect for content that changes often; displays cached data immediately, updates when fresh data arrives."
Generic fallback"Provides a default response when both cache and network requests fail for secondary content."
Service worker-side templating"Combines cached templates with JSON data to render pages the service worker controls."

Workbox layers a named composite recipe on top per wb-recipes:

  • offlineFallback() - "serve a web page, image, or font if there's a routing error" when users are offline, defaulting to offline.html.

Capture decisions in a YAML manifest:

# tests/offline-recipe-matrix.yaml
routes:
  - pattern: /_next/static/**
    recipe: Cache only
    reason: Hashed filenames, immutable
  - pattern: /
    recipe: Network falling back to cache
    reason: HTML shell - fresh while online, stale offline
  - pattern: /api/feed
    recipe: Cache then network
    reason: List view; show fast, update behind
  - pattern: /api/user/me
    recipe: Network only
    reason: PII; never cache
  - pattern: /img/**
    recipe: Cache, falling back to network
    reason: Bandwidth save; image lifetime ≥ cache TTL
  - pattern: /*
    recipe: Generic fallback
    reason: Catch-all 404; offline.html

Step 2 - Emit the per-recipe Playwright test

For each recipe row in Step 1's matrix, emit the matching test - the assertion shape must distinguish "served from cache" vs "served from network." The two poles (cache-always vs network-always) are inline; the rest live in the reference.

Cache only

Serves from cache even with network reachable AND with network blocked:

test('Cache only: /_next/static/x.css serves from cache regardless of network', async ({ page, context }) => {
  await page.goto('https://localhost:3000/');
  await page.waitForLoadState('networkidle');

  // Block network for the static path
  await context.route('**/_next/static/**', route => route.abort('failed'));

  const res = await page.evaluate(() =>
    fetch('/_next/static/x.css').then(r => r.status).catch(() => 0)
  );
  expect(res).toBe(200);
});

Network only

test('Network only: /api/user/me fails when offline (never cached)', async ({ page, context }) => {
  await page.goto('https://localhost:3000/');
  await context.setOffline(true);
  const res = await page.evaluate(() =>
    fetch('/api/user/me').then(r => r.status).catch(() => 0)
  );
  expect(res).toBe(0); // network error; not served from cache
});

The remaining six recipe templates - Cache falling back to network, Cache and network race, Network falling back to cache, Cache then network, Generic fallback, and SW-side templating - are in references/recipes.md, each with its assertion-shape note.

Step 3 - Test the underlying storage choice

Per off-data, offline data needs the right storage:

StoragePer off-dataTest posture
Cache Storage API"Designed for network resources accessed via URL (HTML, CSS, JavaScript, images, videos, audio)"Assert caches.has(name) + caches.open(name).keys() returns expected URLs
IndexedDB"Use IndexedDB to store structured data" - "data that needs to be searchable or combinable in a NoSQL-like manner"Assert IDB schema + key existence via indexedDB.open()
Storage Managernavigator.storage.estimate() returns { quota, usage }; navigator.storage.persist() requests durable storageAssert persisted() returns true post-request

Test the Storage Manager persist() request:

test('Storage Manager persist() succeeds for installed PWA', async ({ page }) => {
  await page.goto('https://localhost:3000/');
  const result = await page.evaluate(async () => {
    const persisted = await navigator.storage.persist();
    const isPersisted = await navigator.storage.persisted();
    return { persisted, isPersisted };
  });
  expect(result.isPersisted).toBe(true);
});

Per off-data: "Chrome permits origins to use up to 60% of total disk space." Tests that assume small caches mask quota-pressure bugs; assert the application stays well below the estimate:

test('cache usage stays under 50MB', async ({ page }) => {
  await page.goto('https://localhost:3000/');
  await page.waitForLoadState('networkidle');
  const est = await page.evaluate(async () => navigator.storage.estimate());
  expect(est.usage!).toBeLessThan(50 * 1024 * 1024);
});

Step 4 - Test the offline-fallback page itself

Per wb-recipes, offlineFallback() defaults to offline.html. Assert the file is precached + the response shape:

test('offline.html is precached and well-formed', async ({ page, context }) => {
  await page.goto('https://localhost:3000/');
  await page.waitForLoadState('networkidle');

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

  const offlinePagePresent = await sw.evaluate(async () => {
    for (const name of await caches.keys()) {
      const c = await caches.open(name);
      const found = await c.match('/offline.html') || await c.match('/_offline');
      if (found) return found.status;
    }
    return null;
  });

  expect(offlinePagePresent).toBe(200);
});

If the project overrides the default pageFallback option, swap the assertion target.

Step 5 - Test the offline → online recovery

A subtle class of bugs: a route correctly serves from cache offline, but doesn't refresh when network returns. Test the transition:

test('cache then network: recovers fresh data when online returns', async ({ page, context }) => {
  await page.goto('https://localhost:3000/feed');
  await context.setOffline(true);
  await page.reload();
  // Stale cache served

  await context.setOffline(false);
  // Trigger refresh
  await page.evaluate(() => fetch('/api/feed').then(r => r.json()));
  await page.waitForTimeout(500);

  // Assert no "offline" banner lingering
  await expect(page.locator('[data-testid="offline-banner"]')).not.toBeVisible();
});

Step 6 - Emit the coverage matrix and CI gate

Write tests/offline-coverage.yaml mapping each route from Step 1 to its test name and the source cookbook recipe:

matrix:
  - route: /_next/static/**
    recipe: Cache only
    spec: offline-fallback.spec.ts > 'Cache only: /_next/static/*'
    source: off-cookbook "Cache only"
  - route: /
    recipe: Network falling back to cache
    spec: offline-fallback.spec.ts > 'Network falling back to cache'
    source: off-cookbook "Network falling back to cache"
  - route: /api/feed
    recipe: Cache then network
    spec: offline-fallback.spec.ts > 'Cache then network: /api/feed'
    source: off-cookbook "Cache then network"
  - route: /api/user/me
    recipe: Network only
    spec: offline-fallback.spec.ts > 'Network only: /api/user/me'
    source: off-cookbook "Network only"
  - route: /img/**
    recipe: Cache, falling back to network
    spec: offline-fallback.spec.ts > 'Cache, falling back to network: /img/*'
    source: off-cookbook "Cache, falling back to network"
  - route: /*
    recipe: Generic fallback
    spec: offline-fallback.spec.ts > 'Generic fallback'
    source: off-cookbook "Generic fallback"; wb-recipes offlineFallback()
storage:
  - check: cache-usage-under-budget
    source: off-data Storage Manager estimate()
  - check: persisted-storage-granted
    source: off-data Storage Manager persist()

CI gates on every row having a passing spec.

Worked example: a 4-route news PWA

Applying Step 1's matrix to a news PWA - static -> Cache only, / -> Network falling back to cache, /api/articles -> Cache then network, /api/user/me -> Network only, /* -> Generic fallback - emits a 5-cell Playwright spec that runs in ~15 seconds on Chromium and catches the four most common offline regressions:

  1. Static cache miss on stale deploy.
  2. HTML shell stuck stale when network returns.
  3. Article list never refreshes when going online.
  4. PII endpoint accidentally cached.

Anti-patterns

Anti-patternWhy it failsFix
One "site works offline" smoke testPer-route recipe regressions invisibleOne spec per recipe row per off-cookbook (Step 2)
Test offline by killing the dev serverSW caches still serve from network until setOffline(true) is calledAlways context.setOffline(true) (every cell)
Skip the offline → online recovery testStale data lingers after network returns; users see "offline" banner onlineStep 5
Pin caches.match('/offline.html') to a hard URLWorkbox pageFallback option overrides; tests false-fail on custom configsMatch by suffix (Step 4)
Test only ChromiumFirefox cache behavior + quota differRun cross-browser; Firefox storage.persist() UX differs
Hand-build storage assertions instead of navigator.storage.estimate()Misses quota-pressureStep 3 uses the API
Assume cached HTML stays fresh foreverTTL plugin (or its absence) gates this; per wb-recipes imageCache() defaults to 30 daysAssert TTL via imageCache-style entry expiration in workbox-tests
Mix recipes without documenting per routeNew routes inherit wrong defaultThe matrix YAML (Step 1) is the source of truth

Limitations

  • The "Cache and network race" recipe is timing-sensitive; Step 2's < 100ms threshold is a heuristic for "cache won" - slow test machines or instrumented Playwright may report higher values. Tighten / loosen per the project's CI host.
  • SW-side templating is rare in modern PWAs; the Step 2 test for it assumes the project actually uses the pattern. Skip if not.
  • Quota-pressure tests are fragile - a 50 MB ceiling per the Step 3 test is project-specific; per off-data the browser permits "up to 60% of total disk space" which varies by device.
  • navigator.storage.persist() may prompt the user on some browsers; in headless CI the prompt auto-dismisses (or auto-grants) inconsistently. Use the permissions: ['persistent-storage'] Playwright context option to grant upfront.
  • offline.html precache depends on the SW's precacheAndRoute(self.__WB_MANIFEST) including the offline page; workbox-tests Step 3 asserts the manifest contents, which is the upstream gate.
  • No IndexedDB-specific recipe in off-cookbook; this builder documents IDB usage in the storage matrix (Step 3) but does not generate per-store test cells. Pair with a dedicated IDB test skill for full coverage.

References

  • web.dev - Offline cookbook (the eight named recipes; verbatim use-case statements per recipe) - off-cookbook.
  • web.dev - Learn PWA: Offline data (Cache Storage vs IndexedDB vs Storage Manager; quota note "up to 60% of total disk space") - off-data.
  • Chrome - Workbox recipes (offlineFallback() recipe defaults to offline.html) - wb-recipes.
  • Composes: workbox-tests, service-worker-lifecycle-tests, add-to-homescreen-flow-tests.
  • Differentiation: the cache-strategy design reference in workbox-tests (references/cache-strategy-design.md) authors the strategy; this builder generates the verification suite. Both consume the cookbook recipe vocabulary; the authoring side writes the SW code, this side writes the spec that locks it.
  • Sibling builders: add-to-homescreen-flow-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 のスキルをすべて見る

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