@addon-core/browser/testing is a framework-agnostic test kit for code that uses @addon-core/browser. It provides
deterministic fixtures, configurable methods and events, and a stateful in-memory browser harness. Importing the
testing subpath does not install globals or otherwise change the process.
import assert from "node:assert/strict";
import {getManifest, queryTabs} from "@addon-core/browser";
import {
createBrowserHarness,
createManifestFixture,
createTabFixture,
installBrowserGlobals,
} from "@addon-core/browser/testing";
const harness = createBrowserHarness({
manifest: createManifestFixture({name: "Consumer extension"}),
tabs: [createTabFixture({active: true, id: 7})],
});
const restore = installBrowserGlobals(harness, {profile: "chrome"});
try {
assert.equal(getManifest().name, "Consumer extension");
assert.deepEqual(
(await queryTabs({active: true})).map(tab => tab.id),
[7]
);
} finally {
restore();
}The production functions are imported from @addon-core/browser and execute unchanged. Only the browser globals
they read are supplied by the test.
- Fixtures create fresh, deterministic Chrome-typed data.
- Primitives provide runner-independent methods and browser events.
- Harness and globals provide browser profiles, state, capabilities, reset, and exact global restoration.
- URL patterns and host permissions cover wildcard tab queries and granted-origin checks.
- Contexts and documents provide a shared registry, context-local subscriptions and cleanup.
- Storage provides isolated areas, managed policy fixtures, change events and real
@addon-core/storageexamples. - Offscreen shares context state with runtime queries and supports controlled creation, closure and cleanup.
- Context messaging routes messages between registered extension contexts and content-script frames.
- Scripting targets and executors selects registered documents and uses an explicitly supplied executor; no code runs implicitly.
- Node execution provides an opt-in
testing/nodeexecutor with fresh VM realms and data-only globals. - Persistent Node runtime adds classic-script bootstrap, document/world state and optional document lifecycle binding.
- Guest virtual clocks explicitly controls VM timers and time sources without patching host globals.
- Jest usage shows how to combine the kit with Jest without making the kit depend on Jest.
- Limitations describes intentional differences from real browsers.
A fixture is only data. A configurable stub records calls and returns or throws exactly what the test configured. A stateful fake models a small documented part of browser behavior, such as tabs and permissions. A real-browser integration test runs the extension in Chrome, Firefox, Safari, or another browser.
Use this kit for deterministic unit and integration tests around your application code. Keep real-browser tests for browser compatibility, manifest behavior, security boundaries, lifecycle timing, and vendor-specific behavior.
The package's own production-wrapper tests run in actual extensions under tests/browser/.
After building, run npm run test:browser -- --browser chromium --api search --binary /path/to/browser or select firefox.
This shared infrastructure groups scenarios by API and uses a fresh extension/profile for each permission set.
Search methods are configurable through harness.configurable.active.search: query models callback/Promise calls,
while Firefox's get and search use Promise-only calls. Configure an engine list with get.setResult(engines) and
operation completion with query.setResult(undefined) or search.setResult(undefined). These stubs do not navigate
tabs or simulate a search provider. The configurable surface is a cross-browser superset; use
harness.capabilities.set("search.get", false) and harness.capabilities.set("search.search", false) to model Chromium's
missing engine APIs, or install globals without the namespace to model Safari.
Top Sites is configurable through harness.configurable.active.topSites.get.setResult(sites).
The Chromium facade models callback calls and the Firefox facade supports callback/Promise calls,
including the options overload. This stub returns configured data; it does not rank browsing history
or implement Firefox's filtering. Configure native failures with failNext(error) and remove the
capability to model an unavailable API. See Top Sites browser coverage
for the real-browser scenarios.
Every harness owns independent state. Call harness.reset() between tests when reusing one harness, or create a fresh
harness per test. Always call the restore function returned by installBrowserGlobals() in finally; it is safe to
call more than once and restores the original property descriptors.
Restore nested installations in reverse order. Use environment: "preserve" in jsdom or when the test already owns
window, document, location, or navigator; see preserving an existing environment.