Rozszerzenia ChromeManifest V3PlaywrightCI/CD

Chrome Web Store przyjął build. MV3 service worker był martwy.

Opublikowano · 8 min czytania · Zespół Optymized

Zmiana bundlingu przeniosła kod do background service workera w Manifest V3. Ten kod dotykał document. Produkcyjny build przechodził. Paczka była poprawna. Ale w chrome://extensions service worker był nieaktywny.

To jest mały check CI, który dodaliśmy po tej awarii: zbuduj dokładnie ten artefakt, który idzie do release, załaduj go jako unpacked extension w Chromium, poczekaj na MV3 service workera i wyślij ping do prawdziwego workera.

1. Co się zepsuło

To nie był dramatyczny rewrite. To była zmiana bundlingu. Zmieniło się nazewnictwo chunków, zmienił się graf chunków, a kod, który wcześniej nie trafiał do tła, został zaimportowany przez service workera.

Ten kod dotykał document. Na stronie, w popupie albo w content scripcie to normalne. W MV3 service workerze nie. Dokumentacja Chrome o service workerach rozszerzeń dobrze ustawia model mentalny: background context to event-driven worker code, a nie strona z DOM. Zacznij od przewodnika Chrome po extension service workers, jeśli ta granica nie jest jeszcze oczywista.

Linia, która psuje workera

const title = document.title

Objaw końcowy był nudny i groźny: paczka rozszerzenia była akceptowalna, ale service worker nie startował poprawnie w produkcji. W Chrome extension page worker był widoczny jako nieaktywny.

2. Dlaczego build i lint tego nie złapały

Bundler może udowodnić, że JavaScript jest składniowo poprawny. Nie udowodni, że każdy zaimportowany moduł jest bezpieczny w każdym kontekście rozszerzenia. Linter może złapać oczywiste użycie globala, jeśli masz osobne środowiska dla service workerów, content scriptów, extension pages i kodu aplikacji. Większość projektów rozszerzeń nie idzie tak daleko.

Kluczowe rozróżnienie: paczka była build-valid, ale runtime-invalid. Manifest istniał. Plik service workera istniał. Zip dało się wysłać. Brakowało tylko przeglądarki wykonującej ten sam krok, który zrobi po dostarczeniu paczki przez Chrome Web Store.

Dodatkowa ochrona statyczna: zablokuj DOM globale w plikach workera

Browser-level smoke test nadal jest ostatnią siatką bezpieczeństwa, ale linting może złapać oczywistą pomyłkę wcześniej. Jeśli Twoja konfiguracja TypeScript już udostępnia chrome przez @types/chrome, zostaw Chrome API dostępne i jawnie zbanuj DOM-only globale w entrypointach service workera przez regułę ESLint no-restricted-globals albo kompatybilną regułę Oxlint.

export default [
  {
    files: ["src/background/**/*.{js,ts}", "src/**/*service-worker*.{js,ts}"],
    rules: {
      "no-restricted-globals": [
        "error",
        { name: "document", message: "MV3 service workers do not have a DOM." },
        { name: "window", message: "Use self/globalThis in workers, not window." },
        { name: "localStorage", message: "Use chrome.storage in extension workers." },
      ],
    },
  },
]

Zasada, której używamy teraz

Nie testuj tylko źródeł. Testuj finalny artefakt, który trafia do Chrome Web Store.

3. Smoke test, który powinien istnieć

Ten smoke test jest celowo mały. Nie loguje się do produktu. Nie potrzebuje sandboxa. Nie potrzebuje ciastek. Odpowiada tylko na jedno pytanie: czy Chromium potrafi załadować finalny build rozszerzenia i wykonać MV3 service workera?

Dokumentacja Chrome pokazuje ten sam browser-level kierunek w Puppeteer: załaduj unpacked extension i czekaj na target service_worker. W samplu używamy Playwrighta, ale granica jest ta sama. Zobacz przewodnik Chrome po testowaniu rozszerzeń.

Minimalny kształt testu Playwright

const context = await chromium.launchPersistentContext(userDataDir, {
  headless: false,
  args: [
    '--disable-extensions-except=' + extensionDir,
    '--load-extension=' + extensionDir,
  ],
})

const serviceWorker = await context.waitForEvent('serviceworker', {
  predicate: (worker) => worker.url().startsWith('chrome-extension://'),
})

const response = await extensionPage.evaluate(() => {
  return chrome.runtime.sendMessage({ type: 'service-worker-smoke' })
})

expect(response.ok).toBe(true)

Pełny działający przykład opublikowaliśmy jako Toumash/mv3-service-worker-smoke. Najważniejszy fragment to spec Playwright, który otwiera stronę rozszerzenia i wysyła ping do workera.

4. Minimalny workflow GitHub Actions

Trzymaj ten test osobno od pełnego E2E. Wartość polega na tym, że jest wystarczająco tani, żeby odpalać go na pull requestach dotykających kodu rozszerzenia, bundlingu, konfiguracji Vite, konfiguracji CRXJS albo pakowania releasu.

name: Service Worker Smoke Test

on:
  pull_request:
  push:
    branches:
      - main

jobs:
  service-worker-smoke:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 24
          cache: npm
      - run: npm ci
      - run: npm run build
      - run: npx playwright install --with-deps chromium
      - run: xvfb-run --auto-servernum npm run test:service-worker

Test powinien oblać build, jeśli top-level runtime error blokuje rejestrację listenerów w workerze. W naszym repo demonstracyjnym odkomentowanie const title = document.title sprawia, że build przechodzi, a smoke test pada.

5. Checklista release

  • 1.Zbuduj dokładnie ten output produkcyjny, który trafia do release zipa.
  • 2.Załaduj ten output przez --load-extension, nie pliki źródłowe.
  • 3.Poczekaj na prawdziwy chrome-extension:// service worker.
  • 4.Wyślij ping do handlera zarejestrowanego przez właściwy kod workera.
  • 5.Nie uzależniaj smoke testu od API produktu, sesji użytkownika ani danych klienta.
  • 6.Ustaw go jako wymagany check dla PR-ów zmieniających build albo release rozszerzenia.

To nie udowodni, że całe rozszerzenie działa. Udowodni, że background worker nie jest martwy od startu. Dla rozszerzeń MV3 to zasługuje na osobny check.

Powiązane artykuły

Potrzebujesz utwardzić release pipeline rozszerzenia Chrome?

Budujemy i utrzymujemy produkcyjne rozszerzenia przeglądarkowe, w tym checki CI dla uprawnień, service workerów, pakowania i release do Chrome Web Store.

Kto za tym stoi

Tomasz Dłuski

Tomasz Dłuski

Założyciel & CEO

Senior Software Engineer z 10+ letnim doświadczeniem. W poprzedniej firmie był częścią firmy, która wyskalowała się z 5 do 50+ inżynierów. Teraz buduje Optymized — firmę, która łączy doświadczenie w dostarczaniu projektów enterprise z własnymi produktami SaaS. Maintainer CRXJS (3.9k stars na GitHubie), jednego z najpopularniejszych narzędzi do budowy rozszerzeń przeglądarek.

Porozmawiajmy o Twoim projekcie

Potrzebujesz rozszerzenia do przeglądarki, dedykowanego zespołu, czy konsultacji technicznej? Znajdźmy najlepsze podejście razem.

lub napisz do nas