# Testing WXT provides several utils for writing tests. ## Unit tests If you're using auto-imports (enabled by default), [Vitest](https://vitest.dev/) is the only testing framework that supports them. If you want to use a different testing library/framework (like Jest, mocha, node:test, etc), you can keep using it, but you have two options: 1. Switch to Vitest (recommended) 2. Configure the testing library manually - Disable auto-imports by setting `imports: false` in your `wxt.config.ts` file - Manually add globals normally provided by WXT (like `__BROWSER__`) that you consume to the global scope before accessing them (`globalThis.__BROWSER__ = "chrome"`) ### Vitest Setup Install vitest and add the `WxtVitest` plugin to your `vitest.config.ts` file. ```sh pnpm i -D vitest ``` ```ts // /vitest.config.ts import { defineConfig } from 'vitest/config'; import { WxtVitest } from 'wxt/testing'; export default defineConfig({ plugins: [WxtVitest()], test: { server: { deps: { // Add any dependencies that import webextension-polyfill here, otherwise tests will attempt to import the real polyfill, breaking the // TODO: Auto-detect these dependencies inside `WxtVitest` so maintaining this list manually isn't necessary inline: [...], }, }, }, }); ``` And that's it. You're ready to start writing tests. ### Writing Tests Here's a very basic test, written with a few different testing libraries, with a few different approaches for mocking the `browser` global. :::code-group ```ts [Vitest] import { describe, it, expect, vi } from 'vitest'; function logRuntimeId() { // Vitest automatically mocks "browser" with "fakeBrowser" console.log(browser.runtime.id); } describe('logRuntimeId', () => { it("should log the extension's runtime ID", () => { // Set a known ID on fakeBrowser for the test const id = 'some-runtime-id'; fakeBrowser.runtime.id = id; const logSpy = vi.spyOn(console, 'log'); logRuntimeId(); expect(logSpy).toBeCalledWith(id); }); }); ``` ```ts [Jest - Manual Mock] import { fakeBrowser } from 'wxt/testing'; import { browser } from 'wxt/browser'; function logRuntimeId() { console.log(browser.runtime.id); } // Manually mock jest.mock('wxt/browser', () => { const { fakeBrowser } = require('wxt/testing'); return { browser: fakeBrowser }; }); describe('logRuntimeId', () => { it("should log the extension's runtime ID", () => { // Set a known ID on fakeBrowser for the test const id = 'some-runtime-id'; fakeBrowser.runtime.id = id; const logSpy = jest.spyOn(console, 'log'); logRuntimeId(); expect(logSpy).toBeCalledWith(id); }); }); ``` ```ts [node:test - Parameterized] import { describe, it, mock } from 'node:test'; import { assert } from 'node:assert'; import { fakeBrowser } from 'wxt/testing'; import { browser } from 'wxt/browser'; // Add browser as a parameter so fakeBrowser can be passed instead of browser function logRuntimeId(browser = browser) { console.log(browser.runtime.id); } describe('logRuntimeId', () => { it("should log the extension's runtime ID", () => { // Set a known ID on fakeBrowser for the test const id = 'some-runtime-id'; fakeBrowser.runtime.id = id; console.log = mock.fn(); // pass in fakeBrowser during tests logRuntimeId(fakeBrowser); assert.deepStrictEqual(console.log.mock.calls[0].arguments, [id]); }); }); ``` ::: :::warning Without mocking the `browser` variable, you'll see errors like this: ``` This script should only be loaded in a browser extension. ``` ::: WXT provides an in-memory, partial implementation of `browser`, [`fakeBrowser`](/api/wxt/testing/variables/fakeBrowser), from the [`@webext-core/fake-browser`](https://webext-core.aklinker1.io/guide/fake-browser/) package. `fakeBrowser` works with all testing frameworks/libraries. See their docs for a list of [implemented APIs](https://webext-core.aklinker1.io/guide/fake-browser/implemented-apis.html) and more example tests. ## E2E Tests WXT does not provide any utils for running E2E tests. There are two libraries you can use to run E2E tests for any chrome extension. - [`playwright`](https://playwright.dev/docs/chrome-extensions) (recommended) - "A high-level API to automate web browsers" - [`puppeteer`](https://pptr.dev/guides/chrome-extensions) - "A high-level API to control headless Chrome over the DevTools Protocol" :::info Note that both only support running tests on Chrome. ::: Before running tests with either of these tools, you must build the extension with `wxt build` and then load the extension from the output directory in a new tab. To test an extension's UI, like the popup or options page, you'll need to know the extension's ID to open the URL directly. > _chrome-extension://`browser.runtime.id`/popup.html_ - Playwright provides an API to get your extension ID after it has been installed. [See their docs](https://playwright.dev/docs/chrome-extensions#testing). - Puppeteer requires you know the ID before installing the extension, so you can hard code it into the URLs you open. Follow [Chrome's guide](https://developer.chrome.com/docs/extensions/mv3/manifest/key/) to setup a consistent runtime id. :::info You cannot test popups in their normal popup window, you have to open them in a tab. :::