extension-test
Extension Testing
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
npm 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
export default {
preset: 'ts-jest',
testEnvironment: 'jsdom',
setupFilesAfterEnv: ['./jest.setup.ts'],
moduleNameMapper: {
'^@/(.*)$': '<rootDir>/src/$1',
},
};
jest.setup.ts (minimal)
import 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
npm install -D puppeteer
Launch extension in Chrome
import 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
const 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
{
"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
More from quangpl/browser-extension-skills
extension-ui
Build polished Chrome extension UIs (popup/sidepanel/options). Analyze existing UI, suggest improvements, set up design systems, enforce a11y and UX best practices.
21extension-analyze
Audit Chrome extensions for security issues, best practice violations, performance problems, and CWS compliance. Scans manifest, code, CSP, message handlers, storage, and dependencies.
20extension-create
Auto-scaffold Chrome extensions with WXT or Plasmo. Ask user for name/features, scaffold, configure entrypoints. Use when: create extension, scaffold, new extension.
19extension-manifest
Generate and validate manifest.json with optimal permissions for Chrome MV3 extensions. Analyzes code to determine minimum permissions. Use when: manifest, permissions, manifest.json.
18extension-dev
Detect Chrome extension framework/stack, find proper docs, implement features, and debug across service worker, content script, and popup contexts.
17extension-assets
Generate and manage all Chrome extension assets: icons (16–128px), CWS listing images, promotional tiles, and public/ folder setup. Supports ImageMagick, Gemini API, and manual prompt templates.
16