Driver FixRecommendedSound, Wi-Fi or graphics acting up? Check drivers firstFind missing or outdated drivers fast.Check DriversOctober DealsAmazon USOctober deal check: compare before you payAmazon US: current deals, useful picks and tech finds.Check DealsPC HealthRecommendedCrashes, freezes, slowdowns? Check your PC nowSpot repairable issues before they interrupt work.Check PC×
Skip to content
World desk7 min

How to Automate Chrome Extensions with Puppeteer

A practical Puppeteer workflow for loading unpacked Chrome extensions and testing their background contexts, action popups, and content scripts.
Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

To test a Chrome extension with Puppeteer, launch Chrome with the built, unpacked extension enabled, then interact with the right extension context: a service worker for Manifest V3 (MV3), a background page for Manifest V2 (MV2), a popup target for its action UI, or an extension realm for content scripts. The examples below use Puppeteer’s documented extension APIs and are written for repeatable tests.

Set up Puppeteer and build the extension

Use the extension’s built output directory—the unpacked folder containing its manifest—not a source directory that still needs compiling. Build it before launching the test. Puppeteer’s Chrome Extensions guide, displayed as version 25.12.0, documents loading unpacked extensions with enableExtensions: Puppeteer Chrome Extensions guide.

As an Amazon Associate I earn from qualifying purchases.

Install Puppeteer in your project if it is not already installed:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
npm install puppeteer

The following examples use ECMAScript modules. In a Node.js project, set "type": "module" in package.json, or save the test as an .mjs file. Replace my-extension with the path to your built extension.

#1 Best Overall
Samsung 14" Galaxy Chromebook Go Laptop PC Computer, Intel Celeron N4500 Processor, 4GB RAM, 64GB Storage, ChromeOS, XE340XDA-KA2US, Student Laptop, Silver
  • SLIM. LIGHTWEIGHT. READY TO GO: The all-new slim design is perfect for busy lives on the go.
  • SKILLFULLY DESIGNED. MILITARY TOUGH: Built with premium craftsmanship to withstand the occasional drop or ding.
  • ALL-DAY, ALL-IN-ONE CHARGING: Power through your school day – and beyond – with a long-lasting 12-hour battery.¹
  • 3X FASTER THAN THE PREVIOUS GENERATION OF WIFI: Crush your schoolwork in record time with Wi-Fi that’s three times faster than the previous generation of Wi-Fi.
  • YOUR PHONE AND CHROMEBOOK WORK BETTER TOGETHER: Easily transfer files between devices, and control your phone right from your Chromebook.

Load an unpacked extension when launching Chrome

Pass the extension directory to enableExtensions. This is the simplest choice when the test always uses the same extension:

import puppeteer from 'puppeteer';
import path from 'node:path';

const extensionPath = path.join(process.cwd(), 'my-extension');
const browser = await puppeteer.launch({
  enableExtensions: [extensionPath],
});

try {
  console.log(await browser.extensions());
  // Run extension checks here.
} finally {
  await browser.close();
}

The path must point to an unpacked, built extension. The enableExtensions launch option accepts either a boolean or an array of extension paths; setting it avoids Puppeteer’s default arguments that prevent extensions from being enabled. See the LaunchOptions interface.

Install the extension at runtime instead

If the test needs the extension ID returned directly, enable extensions at launch and install the path afterward:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
import puppeteer from 'puppeteer';
import path from 'node:path';

const extensionPath = path.join(process.cwd(), 'my-extension');
const browser = await puppeteer.launch({ enableExtensions: true });

try {
  const extensionId = await browser.installExtension(extensionPath);
  console.log('Installed extension:', extensionId);
  console.log(await browser.extensions());
  // Run extension checks here.
} finally {
  await browser.close();
}

Puppeteer also provides browser.uninstallExtension(extensionId) to remove an installed extension. The browser extension APIs are documented in the Chrome Extensions guide.

Rank #2
HP Chromebook 14 Laptop, Intel Celeron N4120, 4 GB RAM, 64 GB eMMC, 14" HD Display, Chrome OS, Thin Design, 4K Graphics, Long Battery Life, Ash Gray Keyboard (14a-na0226nr, 2022, Mineral Silver)
  • FOR HOME, WORK, & SCHOOL – With an Intel processor, 14-inch display, custom-tuned stereo speakers, and long battery life, this Chromebook laptop lets you knock out any assignment or binge-watch your favorite shows..Voltage:5.0 volts
  • HD DISPLAY, PORTABLE DESIGN – See every bit of detail on this micro-edge, anti-glare, 14-inch HD (1366 x 768) display (1); easily take this thin and lightweight laptop PC from room to room, on trips, or in a backpack.
  • ALL-DAY PERFORMANCE – Reliably tackle all your assignments at once with the quad-core, Intel Celeron N4120—the perfect processor for performance, power consumption, and value (2).
  • 4K READY – Smoothly stream 4K content and play your favorite next-gen games with Intel UHD Graphics 600 (3) (4).
  • MEMORY AND STORAGE – Enjoy a boost to your system’s performance with 4 GB of RAM while saving more of your favorite memories with 64 GB of reliable flash-based eMMC storage (5).

Find the background context for the manifest version

Do not assume all extensions have the same background target. MV3 uses a service worker; MV2 uses a background page. Match both the target type and a URL condition that identifies your extension. Puppeteer’s guide shows a worker URL ending in background.js as an example, but that filename is not universal.

Manifest V3: service worker

const workerTarget = await browser.waitForTarget(target =>
  target.type() === 'service_worker' &&
  target.url().endsWith('background.js')
);

if (!workerTarget) {
  throw new Error('Extension service worker was not found');
}

const worker = await workerTarget.worker();
if (!worker) {
  throw new Error('Could not get the extension service worker');
}

const result = await worker.evaluate(() => {
  // Replace with a check appropriate to your background code.
  return typeof chrome !== 'undefined' && Boolean(chrome.runtime?.id);
});

if (!result) {
  throw new Error('Extension runtime is unavailable in the service worker');
}

For a suite with multiple extensions or workers, make the predicate more specific: check the expected extension ID in the target URL and the worker script path used by your build. A broad predicate can attach the test to another extension’s worker.

Manifest V2: background page

const backgroundTarget = await browser.waitForTarget(target =>
  target.type() === 'background_page' &&
  target.url().includes(expectedExtensionId)
);

if (!backgroundTarget) {
  throw new Error('Extension background page was not found');
}

const backgroundPage = await backgroundTarget.page();
if (!backgroundPage) {
  throw new Error('Could not get the extension background page');
}

const result = await backgroundPage.evaluate(() =>
  typeof chrome !== 'undefined' && Boolean(chrome.runtime?.id)
);

Define expectedExtensionId from the installed extension or your known test configuration. The MV2 background-page flow and MV3 worker flow are distinct in Puppeteer’s extension documentation.

What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Test the toolbar action and popup

Puppeteer provides page.triggerExtensionAction(extension) and extension.triggerAction(page) to trigger an extension’s default action for a page. If the action opens a popup, wait for the popup target and convert it to a page before asserting on its contents.

const page = await browser.newPage();
await page.goto('https://example.com');

const [extension] = await browser.extensions();
if (!extension) {
  throw new Error('No extension is installed');
}

await page.triggerExtensionAction(extension);

const popupTarget = await browser.waitForTarget(target =>
  target.type() === 'page' &&
  target.url().startsWith(`chrome-extension://${extension.id}/`) &&
  target.url().endsWith('/popup.html')
);

if (!popupTarget) {
  throw new Error('Extension popup did not open');
}

const popup = await popupTarget.asPage();
const heading = await popup.locator('h1').textContent();
if (heading !== 'Expected popup heading') {
  throw new Error(`Unexpected popup heading: ${heading}`);
}

await browser.close();

Change the extension selection, popup path, and assertion to match your project. If a test suite can open more than one extension popup, include the specific extension ID and expected popup path in the target predicate; the official guide’s simpler popup.html suffix example assumes a single match. The guide also demonstrates opening a popup with chrome.action.openPopup() through the MV3 service worker.

Test content-script behavior on a normal web page

Navigate to a page where the extension’s content-script match rules apply. Then inspect the page’s extension realms, find the realm associated with your extension ID, and evaluate the assertion there. A content script runs in an extension realm, so evaluating only in the ordinary page context may not test the extension code.

const page = await browser.newPage();
await page.goto('https://example.com');

const [extension] = await browser.extensions();
if (!extension) {
  throw new Error('No extension is installed');
}

const realms = await page.extensionRealms();
const extensionRealm = realms.find(realm =>
  realm.extension?.id === extension.id
);

if (!extensionRealm) {
  throw new Error(`No content-script realm found for ${extension.id}`);
}

const result = await extensionRealm.evaluate(() => {
  // Replace this with an observable effect of your content script.
  return document.documentElement.hasAttribute('data-my-extension-ready');
});

if (!result) {
  throw new Error('Content-script behavior was not observed');
}

Use a URL that satisfies the extension’s host permissions and content-script match patterns, and make the assertion specific to the behavior under test. Failing clearly when the realm is missing prevents an accidental pass from evaluating in the page’s regular JavaScript context. See the Puppeteer extension guide for page.extensionRealms().

Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Choose a browser mode that matches the assertion

Puppeteer launches headless by default. Use headless: false when the test needs a visible Chrome window or depends on extension UI behavior that must be checked in regular Chrome:

Rank #4
HP 14" HD Chromebook Laptop for Students, Intel Quad-Core N4120(> N4020), 4GB RAM, 64GB eMMC, WiFi, Webcam, HDMI, USB-A&C, 14 Hours Battery Life, Zoom, Chrome OS, CUE Accessories
  • Intel Celeron N4120: 4 Cores & Threads, 1.1GHz Base Clock, Up to 2.6GHz Boost Clock, 4MB Cache, Intel UHD Graphics 600. The perfect combination of performance, power consumption, and value helps your device handle multitasking smoothly and reliably with four processing cores to divide up the work.
const browser = await puppeteer.launch({
  enableExtensions: [extensionPath],
  headless: false,
});

Puppeteer distinguishes regular headless Chrome from headless: 'shell', which selects the separate chrome-headless-shell binary. The shell can be faster when its reduced feature set is sufficient, but it does not completely match regular Chrome. Validate the exact mode used in CI when popup or toolbar behavior matters. Details are in Puppeteer’s Headless mode guide.

For reproducible runs, prefer the Chrome for Testing build Puppeteer downloads by default. Puppeteer says it works best with that version and does not guarantee operation with another Chrome version; if your environment requires an independently installed browser, validate that pairing. See PuppeteerNode.launch().

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Troubleshoot common failures

  • The extension is missing. Confirm that enableExtensions is set and that the path points to the built, unpacked extension directory. Puppeteer’s defaults can disable extensions; the launch option avoids the default arguments that prevent this. On Windows, Puppeteer’s troubleshooting guide also identifies Chrome policies as a possible launch issue and describes enableExtensions: true as a workaround. See LaunchOptions and Troubleshooting.
  • The background target never appears. Check the manifest version and wait for the matching target type: service_worker for MV3 or background_page for MV2. Tighten the URL predicate so it matches the extension and script or page actually used by your build.
  • The popup wait times out. Confirm that the action is configured to open a popup, trigger the action on the intended page, and match the actual popup path as well as extension ID. A suffix-only predicate may be ambiguous in a larger suite.
  • No content-script realm is found. Check that the navigated URL matches the content script’s configured patterns and that the extension has been installed. Do not fall back silently to evaluating in the page’s ordinary context.
  • Extension behavior differs in headless CI. Try regular headful Chrome with headless: false, especially for UI assertions; the separate headless shell is not fully equivalent to regular Chrome. See Headless mode.
  • Chrome fails to launch on Linux. Check for missing system dependencies using Puppeteer’s troubleshooting guide. It strongly discourages running Chrome without its sandbox, so do not treat --no-sandbox as a routine fix.
  • A separately installed Chrome behaves differently. Reproduce with Puppeteer’s downloaded Chrome for Testing first, then validate the separately managed browser and Puppeteer pairing in the same environment.

Or skip the browser setup

For capturing a web page as an image or PDF—not for testing extension internals—ScreenshotNeo provides a website screenshot API and MCP server. One GET request returns a PNG, JPEG, WebP, or PDF. Here is a cURL example; see the API documentation for request options:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
  • Cookie and consent banners, newsletter popups, and chat widgets are handled before capture; each step can be turned off.
  • Bot checks and CAPTCHAs, blank pages, timeouts, failed loads, and cache hits cost nothing; response headers indicate the page verdict and billing status.
  • An MCP server provides take_screenshot, get_page_info, and capture_pdf tools for AI agents and MCP clients.
  • The free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000 screenshots.

Sign up free for 1,000 screenshots a month—no card required.

Frequently asked questions

Can Puppeteer test a Chrome extension without opening Chrome visibly?

Yes. Puppeteer is headless by default, but extension tests that rely on visible UI should be validated in the exact browser mode used for the test, including headful mode where needed.

Can Puppeteer test extension background logic and page-injected behavior in the same run?

Yes. Use the background target appropriate to the manifest version for background checks, then navigate a page and evaluate in the extension realm for content-script checks.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Leave a Reply

Your email address will not be published. Required fields are marked *

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

More from the Wire

  1. World desk4 min
    How to Spot an AI Voice Scam Before Sending MoneyDon’t rely on how a caller sounds. Pause, call back through a known number, and verify the emergency with another trusted person before sending money.
  2. Mountain View desk4 min
    Google’s SynthID Detector: How to Check AI-Generated Images, Video and AudioGoogle’s SynthID Detector looks for an embedded watermark in supported images, video and audio. Here is what its results do—and do not—show.
  3. Redmond desk20 min
    How to create a link to File or Folder in Windows 11Windows 11 gives you several ways to point to a file or folder without moving or duplicating it. You can create a desktop shortcut,…
Recommended PC Tool
Recommended PC Tool
PC Slower Than It Used to Be?Free scan - under a minute
Outdated Drivers Are Slowing You DownFree scan - exact matches

Two free Windows tools

One Free Minute Could Fix That PC

Before you go - each of these free tools takes about a minute and tackles what quietly slows a Windows PC down.

Special offer. View Outbyte info, uninstall instructions, EULA, and Privacy Policy.