跳到正文
cccskills
免费在 GitHub 公开

playwright-extension-fixtures

Author the Playwright fixture layer every Chromium extension test depends on - `chromium.launchPersistentContext` with `--disable-extensions-except=$DIR` + `--load-extension=$DIR`, the `channel: 'chromium'` selection that unlocks headless extension support, the `context.serviceWorkers()` + `waitForEvent('serviceworker')` race-handling pattern, and the `extensionId = serviceWorker.url().split('/')[2]` extraction recipe - plus, in references/, the extension load / reload matrix (which edit re-evaluates which surface), the `chrome.storage` test suite (area selection, quota-exceeded, `storage.onChanged`, managed read-only), and the per-surface assertion recipes (popup, content script, background messaging, MV3 auto-suspend survival). Use when authoring or debugging the fixture a Chromium extension test imports, when an edit appears to have no effect and you need the reload matrix, or when authoring popup / content-script / storage tests.

中文简介正在准备中。当前显示原文说明。

testland/qa星标 13(GitHub 13 + cccskills 0)
集成工具Playwright

实测评分 未实测

查看安装方法

包含的文件(5)

  • SKILL.md12.0 KB
  • .tessl-plugin/plugin.json1.1 KB
  • references/extension-surface-tests.md5.1 KB
  • references/reload-matrix.md1.7 KB
  • references/storage-tests.md6.2 KB

SKILL.md(原文)

安装前可以查看交给 AI 代理的指令内容。

playwright-extension-fixtures

Overview

Every Playwright-driven Chromium-extension test starts the same way: a persistent context launched with two flags, a service-worker race, and an extension-ID extraction. Per the Playwright Chrome extensions docs, this fixture is the contract the assertion-level skills depend on - it's the foundation, not the test logic.

This SKILL.md is the "how to launch" layer - the Playwright fixture pattern (launchPersistentContext + --disable-extensions-except + --load-extension) shared by all extension tests. The "what to assert" playbook per surface - popup, content script, background messaging, storage, MV3 auto-suspend - lives in references/extension-surface-tests.md and builds on this fixture.

Composes with:

  • references/extension-surface-tests.md - per-surface assertion recipes (popup, content script, messaging, storage persistence, auto-suspend) run from this fixture.
  • references/reload-matrix.md - which edits require which reload; the manual chrome://extensions gesture this fixture automates.
  • references/storage-tests.md - the chrome.storage area / quota / event test suite run from this fixture's SW context.
  • manifest-v3-test-surface-reference - the manifest fields the fixture is testing against.

When to use

  • Authoring a Playwright test against any unpacked Chromium extension (popup, content script, service worker, options page, side panel, devtools page).
  • Headless CI runs of an extension - the channel: 'chromium' selection is what unlocks headless extension support per pw-ext.
  • Diagnosing a "tests pass locally headed, fail headless" bug - the channel / flag matrix in this skill is the first checkpoint.
  • Sharing the fixture across multiple spec files in the same repository (a fixtures.ts that every spec imports).

Authoring

The verbatim fixture file

Per pw-ext, fixtures.ts:

import { test as base, chromium, type BrowserContext } from '@playwright/test';
import path from 'path';

export const test = base.extend<{
  context: BrowserContext;
  extensionId: string;
}>({
  context: async ({ }, use) => {
    const pathToExtension = path.join(__dirname, 'my-extension');
    const context = await chromium.launchPersistentContext('', {
      channel: 'chromium',
      args: [
        `--disable-extensions-except=${pathToExtension}`,
        `--load-extension=${pathToExtension}`,
      ],
    });
    await use(context);
    await context.close();
  },
  extensionId: async ({ context }, use) => {
    // for manifest v3:
    let [serviceWorker] = context.serviceWorkers();
    if (!serviceWorker)
      serviceWorker = await context.waitForEvent('serviceworker');
    const extensionId = serviceWorker.url().split('/')[2];
    await use(extensionId);
  },
});

export const expect = test.expect;

Field-by-field rationale

ElementWhy it matters per pw-ext
chromium.launchPersistentContext('')"Extensions require a persistent context in Chromium" - launch() is non-persistent and extensions never load
'' (userDataDir)Empty string = ephemeral temp dir (Playwright cleans up); replace with a fixed path to persist auth state across runs
channel: 'chromium'The bundled Chromium channel; unlocks headless extension support per pw-ext
--disable-extensions-except=$DIRPrevents any pre-installed extension from also loading and confusing assertions
--load-extension=$DIRLoads the unpacked extension at $DIR (where manifest.json lives)
context.serviceWorkers()Synchronous accessor - may be empty if the SW hasn't registered yet
context.waitForEvent('serviceworker')Race-safe fallback when SW isn't yet up
serviceWorker.url().split('/')[2]Service-worker URL is chrome-extension://<id>/<path>; index 2 is the ID

Usage in spec files

Per pw-ext:

import { test, expect } from './fixtures';

test('example test', async ({ page }) => {
  await page.goto('https://example.com');
  await expect(page.locator('body')).toHaveText('Changed by my-extension');
});

test('popup page', async ({ page, extensionId }) => {
  await page.goto(`chrome-extension://${extensionId}/popup.html`);
  await expect(page.locator('body')).toHaveText('my-extension popup');
});

The fixture-injected page automatically belongs to the persistent context - any content scripts the extension declares for the navigated URL will already be attached.

Running

Local headed

npx playwright test

Defaults to headed for extensions (per pw-ext, non-chromium channels require headed mode).

Local + CI headless

npx playwright test --headed=false

Requires channel: 'chromium' in the fixture per pw-ext:

"Headless mode for extensions is supported only when using the chromium channel."

Edge channel and Chrome channel will fail extension load in headless because, quoting pw-ext:

"Google Chrome and Microsoft Edge removed the command-line flags needed to side-load extensions."

MV2 fallback for extensionId

Per pw-ext, the SW fixture is MV3-specific. For an MV2 extension where the background context is a background page (not a service worker), extract the ID from a background-page event instead:

extensionId: async ({ context }, use) => {
  // for manifest v2:
  let [background] = context.backgroundPages();
  if (!background) background = await context.waitForEvent('backgroundpage');
  const extensionId = background.url().split('/')[2];
  await use(extensionId);
},

backgroundPages() is the MV2 analogue of serviceWorkers().

Pinning the temp profile

Replace '' with a fixed userDataDir to persist auth across runs:

const userDataDir = path.join(__dirname, '.pw-profile');
const context = await chromium.launchPersistentContext(userDataDir, { ... });

Trade-off: stateful runs become non-deterministic; do this only for debugging an auth flow, not for CI.

Parsing results

Service-worker restart errors

Per pw-ext, MV3 service workers auto-suspend after ~30s idle. Playwright keeps "the same Worker object alive" across the restart, but "an in-flight evaluate() at the moment of suspension will throw" with the message:

"Service worker restarted"

Handle in tests by retrying the evaluate:

async function swEvaluate<T>(sw: any, fn: () => T): Promise<T> {
  try { return await sw.evaluate(fn); }
  catch (e: any) {
    if (e.message.includes('Service worker restarted')) {
      return await sw.evaluate(fn); // re-run once
    }
    throw e;
  }
}

Extension-load failures

If serviceWorker never fires and waitForEvent times out, typical causes:

SymptomLikely causeFix
waitForEvent('serviceworker') times outManifest invalid or background.service_worker field missingLoad unpacked via chrome://extensions manually first and read the red Errors card
Extension loads in headed but not headlessChannel set to chrome / msedge instead of chromiumUse channel: 'chromium' per pw-ext
extensionId extracts an empty stringURL not chrome-extension://... shape (e.g., about:blank listener fired)Filter on serviceWorker.url().startsWith('chrome-extension://') before split

CI integration

GitHub Actions example:

name: extension-e2e
on: [pull_request]
jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v5
      - uses: actions/setup-node@v5
        with: { node-version: 'lts/*' }
      - run: npm ci
      - run: npx playwright install --with-deps chromium
      - name: Build extension
        run: npm run build:extension
      - name: E2E
        run: npx playwright test
      - if: failure()
        uses: actions/upload-artifact@v4
        with:
          name: playwright-report
          path: playwright-report/

Key choices:

  • npx playwright install --with-deps chromium - installs the bundled Chromium that the channel: 'chromium' fixture uses.
  • No xvfb needed - headless Chromium extensions work per pw-ext.

Anti-patterns

Anti-patternWhy it failsFix
chromium.launch() instead of launchPersistentContextExtension never loads per pw-extAlways use the persistent variant
channel: 'chrome' or channel: 'msedge'Side-load flags removed per pw-ext; headless will failUse channel: 'chromium'
Hardcoding extensionId from a local installID changes per build / per machineExtract from SW URL via the fixture
Skipping the waitForEvent('serviceworker') fallbackRace: SW not yet registered → [] from serviceWorkers() → undefined IDAlways include the if-empty branch per pw-ext
Persistent userDataDir in CIAuth state leaks between runs; flakyUse '' for CI determinism
Catching "Service worker restarted" as a permanent failurePer pw-ext this is recoverable; the SW resumesRetry the evaluate once
Using extra --args without verifying they don't conflictPer pw-ext: "some of them may break Playwright functionality"Add browser args defensively, one at a time

Limitations

  • Chromium-only. Per pw-ext, the fixture covers Chromium-family extensions only. Firefox WebExtensions test differently - see the web-ext reference in manifest-v3-test-surface-reference (references/web-ext-firefox.md) for the Mozilla-side runner.
  • MV3 SW lifecycle is asynchronous. The 30s auto-suspend is non-deterministic; tests timing-sensitive to it should use chrome.alarms (see manifest-v3-test-surface-reference).
  • Custom args at own risk. Per pw-ext: "Use custom browser args at your own risk, as some of them may break Playwright functionality."
  • --disable-extensions-except requires absolute path. Relative paths silently fail to load - always pass path.resolve(...).
  • devtools_page / side-panel test surfaces aren't covered here. The base fixture works; the assertion patterns for those surfaces are extension-specific.

References

评价

还没有评价。欢迎分享你的使用感受。

评论

无需登录即可发表。欢迎分享使用感受、提问,或说明在哪些环境下无法运行。

还没有评论。来写第一条吧。

提交后会立即公开。请勿填写邮箱、电话等个人信息。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 的全部技能

报告此技能的问题