pikabo · open source · MIT

Automated testing for Chrome extensions

pikabo loads your unpacked extension into a real Chromium and drives all of it — popup, options page, content scripts, and the MV3 service worker — then writes a report with a screenshot of every step.

$ npm install -D pikabo
pikabo explore --ext .
$ pikabo explore --ext ./my-extension

my-extension v2.1.0 · MV3
popup: popup.html · options: options.html · service worker · 5 permissions

Generated 5 smoke test(s) → tests/smoke.generated.yaml
my-extension smoke tests
   service worker starts without errors      12ms
   popup renders                            431ms
   options page renders                     318ms
   content script injects on github.com     772ms

  4 passed · 3.8s
  html      pikabo-results/report.html

An extension lives outside the page

Ordinary browser automation can’t reach it. The popup is browser chrome, the MV3 background is a service worker with no tab, and stable Chrome 137+ refuses --load-extension outright.

So you test it by hand — and repeat after every change.

  • Reload the unpacked build in chrome://extensions every time you touch the code.
  • Click through the popup by hand just to confirm it still renders.
  • Watch separate DevTools windows for silent service-worker errors.
  • No CI signal — nothing fails until a user hits it.

What pikabo does

Every surface, addressable

Drive the popup, options page, content scripts, MV3 service worker, side panel, devtools, and offscreen documents — each by name.

Smoke tests from the manifest

pikabo explore reads manifest.json, generates a smoke suite, and runs it — no hand-written test required.

Record a session

Click through your extension in a real browser and pikabo writes a runnable YAML suite — a first draft you finish with assertions.

Errors you’d never see by hand

Service-worker startup crashes, silent popup failures, and selector drift are caught and reported with file and line.

Permission audit

Every run compares the chrome.* namespaces your code actually reaches against what manifest.json declares.

Validate what it exports

assertPdf and assertFile check the PDFs and images your extension downloads — page count, geometry, byte size and all.

One vocabulary for the whole extension

A test step is one action; in: chooses which part of the extension it acts on. This is most of the point of the tool.

page

the web page under test, where content scripts run

popup

the extension popup, as a real document

options

the options page

worker

the MV3 service worker — storage, runtime, alarms, messaging

sidepanel

the manifest-declared side-panel document

devtools

the DevTools extension page

background

an MV2 persistent background page

offscreen

an MV3 offscreen document

Invisible failures

The console you can never keep open

An MV3 service worker’s console is invisible unless its inspector happens to be open the moment it starts. pikabo attaches over the DevTools protocol before assertions run and picks up the buffered output.

  • Service-worker startup errors reported with file and line, even when they throw on the first line.
  • Silent popup failures — a popup whose script throws still renders its HTML; pikabo checks it rendered real content.
  • Selector drift tells you what is on the page, not just that it timed out.
assertNoConsoleErrors
✗ service worker starts without errors
  assertNoConsoleErrors: 2 console error(s):
    [worker] ReferenceError: initialise is not defined
      at chrome-extension://abc…/background.js:4:1
    [worker] Error: storage quota exceeded
      at chrome-extension://abc…/background.js:31:16

Expected "#save-pdf" to be visible in popup.
  Elements present: #save, #cancel, #status.

Tests are just YAML

A step is one action. Already have a Playwright suite? Extend your own test once and keep every locator, assertion and reporter you have.

name: PageSaver

tests:
  - name: popup saves a PDF
    steps:
      - navigate: https://example.com
      - openPopup
      - click: "#save-pdf"
      - waitForDownload: "*.pdf"
      - assertStorage:
          key: lastSave
          exists: true
      - assertNoConsoleErrors

  - name: options page persists the theme
    steps:
      - openOptions
      - select: { selector: "#theme", value: dark }
      - click: "#save"
      - assertStorage: { key: theme, equals: dark }
// fixtures.ts
import { test as base, expect } from '@playwright/test';
import { createExtensionTest } from 'pikabo/playwright';

export const test = createExtensionTest(base);
test.use({ extensionPath: 'dist/extension' });

// popup.spec.ts
test('popup changes the current page', async ({ extensionPage, extensionPopup }) => {
  await extensionPage.goto('https://example.com/');
  await extensionPopup.getByRole('button', { name: 'Apply' }).click();
  await expect(extensionPage.locator('[data-extension-applied]')).toBeVisible();
});

Made for your coding agent

The bundled skill teaches an agent the workflow and the non-obvious parts, and installs into whichever convention your agent already reads. pikabo mcp serves the same primitives over MCP, so an agent can drive the browser and then freeze what worked into a YAML suite.

# install the skill everywhere
pikabo skill install --global

# or just this repo
pikabo skill install --dir .

# serve the runner over MCP
pikabo mcp

Stop testing extensions by hand

Point pikabo at your unpacked build and let it drive every surface, catch the invisible errors, and drop straight into CI.

Runs on Chromium, Chrome for Testing, and Edge — not stable Google Chrome, which no longer loads unpacked extensions from the command line. Needs Node 22+. MIT licensed.