Extension Testing
🚀 Upgrade to Premium Get the Premium version at https://app.extensionbooster.net/dashboard/services/extension-ai-kit
Testing Layer Architecture
Unit Tests (Jest) → Test isolated logic, chrome.* API mocks Integration Tests (Jest) → Test service interactions, message passing E2E Tests (Puppeteer) → Test in real Chrome with extension loaded
Critical constraint: Extensions CANNOT run in headless mode. E2E requires headless: false or Chrome's --headless=new (Chrome 112+).
Layer 1: Unit + Integration (Jest)
Install
bashnpm install -D jest @types/jest ts-jest jest-chrome # For React popup: npm install -D @testing-library/react @testing-library/jest-dom jsdom
jest.config.ts
tsexport default { preset: "ts-jest", testEnvironment: "jsdom", setupFilesAfterEnv: ["./jest.setup.ts"], moduleNameMapper: { "^@/(.*)$": "<rootDir>/src/$1", }, };
jest.setup.ts (minimal)
tsimport chrome from "jest-chrome"; Object.assign(global, { chrome });
Full mock setup → chrome-api-mocks.md Unit/integration patterns → unit-integration-testing.md
Layer 2: E2E (Puppeteer)
Install
bashnpm install -D puppeteer
Launch extension in Chrome
tsimport puppeteer from "puppeteer"; import path from "path"; const browser = await puppeteer.launch({ headless: false, // extensions require non-headless args: [ `--disable-extensions-except=${path.resolve("dist")}`, `--load-extension=${path.resolve("dist")}`, ], });
Get extension ID
tsconst targets = await browser.targets(); const extTarget = targets.find((t) => t.type() === "service_worker"); const extUrl = extTarget?.url() ?? ""; const [, , extId] = extUrl.split("/");
Full E2E patterns → e2e-testing-puppeteer.md
Chrome API Mocking Strategy
| API | Approach |
|---|---|
chrome.storage | Mock with in-memory store |
chrome.runtime.sendMessage | jest.fn() + mock response |
chrome.tabs | jest.fn() with createMockTab helper |
chrome.action | jest.fn() stubs |
chrome.alarms | jest.fn() stubs |
All mocks → chrome-api-mocks.md
package.json Scripts
json{ "scripts": { "test:unit": "jest --testPathPattern=unit", "test:integration": "jest --testPathPattern=integration", "test:e2e": "jest --testPathPattern=e2e --runInBand", "test": "npm run test:unit && npm run test:integration" } }
Run E2E separately (
--runInBand) — Puppeteer tests must run serially.
Reference Files
- unit-integration-testing.md — Jest patterns, mocking, component tests
- e2e-testing-puppeteer.md — Puppeteer setup, popup/content/SW tests
- chrome-api-mocks.md — Full chrome.* mock templates


