extension-test

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Extension Testing

Chrome扩展测试

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+).

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
关键限制:扩展无法在无头模式下运行。E2E测试需要设置
headless: false
或使用Chrome的
--headless=new
参数(Chrome 112及以上版本)。

Layer 1: Unit + Integration (Jest)

第一层:单元测试 + 集成测试(Jest)

Install

安装

bash
npm install -D jest @types/jest ts-jest jest-chrome
bash
npm install -D jest @types/jest ts-jest jest-chrome

For React popup:

For React popup:

npm install -D @testing-library/react @testing-library/jest-dom jsdom
undefined
npm install -D @testing-library/react @testing-library/jest-dom jsdom
undefined

jest.config.ts

jest.config.ts

ts
export default {
  preset: 'ts-jest',
  testEnvironment: 'jsdom',
  setupFilesAfterEnv: ['./jest.setup.ts'],
  moduleNameMapper: {
    '^@/(.*)$': '<rootDir>/src/$1',
  },
};
ts
export default {
  preset: 'ts-jest',
  testEnvironment: 'jsdom',
  setupFilesAfterEnv: ['./jest.setup.ts'],
  moduleNameMapper: {
    '^@/(.*)$': '<rootDir>/src/$1',
  },
};

jest.setup.ts (minimal)

jest.setup.ts(最简配置)

ts
import chrome from 'jest-chrome';
Object.assign(global, { chrome });
Full mock setup → chrome-api-mocks.md Unit/integration patterns → unit-integration-testing.md

ts
import chrome from 'jest-chrome';
Object.assign(global, { chrome });
完整模拟配置 → chrome-api-mocks.md 单元/集成测试模式 → unit-integration-testing.md

Layer 2: E2E (Puppeteer)

第二层:E2E测试(Puppeteer)

Install

安装

bash
npm install -D puppeteer
bash
npm install -D puppeteer

Launch extension in Chrome

在Chrome中启动扩展

ts
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')}`,
  ],
});
ts
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

获取扩展ID

ts
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

ts
const targets = await browser.targets();
const extTarget = targets.find(t => t.type() === 'service_worker');
const extUrl = extTarget?.url() ?? '';
const [, , extId] = extUrl.split('/');
完整E2E测试模式 → e2e-testing-puppeteer.md

Chrome API Mocking Strategy

Chrome API模拟策略

APIApproach
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

API实现方式
chrome.storage
使用内存存储模拟
chrome.runtime.sendMessage
jest.fn() + 模拟响应
chrome.tabs
jest.fn() + createMockTab辅助函数
chrome.action
jest.fn()存根
chrome.alarms
jest.fn()存根
所有模拟示例 → chrome-api-mocks.md

package.json Scripts

package.json脚本

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.

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"
  }
}
单独运行E2E测试(
--runInBand
)—— Puppeteer测试必须串行执行。

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
  • unit-integration-testing.md — Jest测试模式、模拟、组件测试
  • e2e-testing-puppeteer.md — Puppeteer配置、弹窗/内容脚本/服务工作者测试
  • chrome-api-mocks.md — 完整的chrome.* API模拟模板