Skip to main content

Popup

Extensions can show a popup when users click their toolbar icon, but Playwright cannot interact with the native toolbar popup as a Page.

playwright-webext provides extension.openPopup() to open the document declared by action.default_popup in a regular browser tab. This lets you test its UI with Playwright locators while using the extension's APIs and storage. See Limitations for differences from the native popup.

test('popup', async ({ extension }) => {
const popupPage = await extension.openPopup();
// ...check popup page
});

Example​

The following example assumes your popup has a “Save” button that stores saved: true, and your manifest includes the storage permission:

test/popup.spec.ts
import { test } from 'playwright-webext';

test('saves preferences from the popup', async ({ extension }) => {
const popupPage = await extension.openPopup();

await popupPage.getByRole('button', { name: 'Save' }).click();
await extension.storage.local.expect('saved').toEqual(true);
});

Use Playwright's locators and retrying assertions to interact with the document.

If your popup has a button that closes its tab, use extension.pollForPageClosed() after clicking it:

await popupPage.getByRole('button', { name: 'Close' }).click();
await extension.pollForPageClosed(popupPage);

Wrap as POM​

If your popup has many elements to interact with, wrap it in a Page Object Model (POM) to keep locators and actions in one place.

test/pages/popup.ts
import type { Page } from '@playwright/test';
import type { Extension } from 'playwright-webext';

export class Popup {
#page?: Page;

constructor(private extension: Extension) {}

get page() {
if (!this.#page || this.#page.isClosed()) {
throw new Error('Call Popup.open() before interacting with the popup.');
}
return this.#page;
}

async open() {
this.#page = await this.extension.openPopup();
return this;
}

async close() {
await this.page.close();
}

saveButton() {
return this.page.getByRole('button', { name: 'Save' });
}
}

Use Popup in your test to check the same behavior through the page object:

test/popup.spec.ts
import { test } from 'playwright-webext';
import { Popup } from './pages/popup';

test('saves preferences from the popup', async ({ extension }) => {
const popup = await new Popup(extension).open();

await popup.saveButton().click();
await extension.storage.local.expect('saved').toEqual(true);
});

Limitations​

openPopup() opens the popup document in a regular browser tab. Its extension APIs and storage are available, but some native toolbar popup behaviors cannot be tested this way:

  • The document uses a normal tab viewport and does not close when focus moves elsewhere. Native popup sizing and dismissal are not reproduced.
  • The new tab becomes active. Code querying the active tab sees the popup document's tab, not the previously active website.
  • Opening the document does not grant the toolbar action's temporary activeTab permission or reproduce its user gesture.
  • Runtime messages from this document include sender.tab; messages from a native toolbar popup normally do not.

Chromium can open the native toolbar popup through chrome.action.openPopup() or the CDP command Extensions.triggerAction. However, Playwright does not expose that popup as a Page through its supported API, so opening it does not let you interact with it using Playwright locators and assertions. extension.openPopup() opens the popup document in a regular tab to provide that access. See Chrome's extension testing guidance.