The Tool Desk
Outbyte Driver Updater FREEFix the driver behind crashes, sound loss and screen glitchesFind Drivers →Outbyte PC Repair FREERepair Windows errors before they cause bigger problemsFix Now →Load the extension when you launch the automated Chrome session: use Puppeteer’s enableExtensions option, or ChromeDriver’s load-extension argument for an unpacked extension and addExtensions for a packaged .crx. For unattended testing, use Chrome’s new headless mode, --headless=new; Chrome’s end-to-end testing guidance says the old headless mode does not support extensions. Then wait for the extension to start before testing its behavior.
Choose the loading method that matches your test
An unpacked extension is its built directory, including manifest.json. A packaged extension is a .crx file. Use the API documented for your automation tool; ChromeDriver options are not automatically portable to other tools. Chrome lists Puppeteer/Playwright, Selenium, and WebDriverIO as testing-library choices in its end-to-end testing guide.
| Tool and artifact | Loading approach | Best fit |
|---|---|---|
| Puppeteer, extension directory | enableExtensions: [EXTENSION_PATH] |
Tests that need to inspect extension contexts and interact with them from Puppeteer. |
| Selenium/ChromeDriver, unpacked directory | load-extension=/absolute/path/to/extension |
Development builds produced as directories. |
| Selenium/ChromeDriver, packaged extension | ChromeOptions.addExtensions(new File("/path/to/extension.crx")) |
Tests that specifically need to install the packaged artifact. |
Chrome’s guidance treats unpacked extensions as trusted development code, not as a distribution mechanism. For distribution, Chrome documents the Chrome Web Store and self-hosting in managed environments, subject to policy constraints. See Distribute your extension.
Load an extension with Puppeteer
Use a path to the extension directory, not to its manifest.json. This runnable example launches Chrome with the extension, waits for its Manifest V3 service worker, and prints the worker URL. Replace the directory path with the location of your built extension.
const puppeteer = require('puppeteer');
const path = require('node:path');
(async () => {
const extensionPath = path.resolve('./dist/extension');
const browser = await puppeteer.launch({
headless: false,
pipe: true,
enableExtensions: [extensionPath],
});
try {
const workerTarget = await browser.waitForTarget(
target => target.type() === 'service_worker' &&
target.url().startsWith('chrome-extension://'),
{ timeout: 10000 }
);
console.log('Extension worker:', workerTarget.url());
// Add assertions for the behavior under test here.
} finally {
await browser.close();
}
})().catch(error => {
console.error(error);
process.exitCode = 1;
});
Chrome’s Puppeteer tutorial uses this launch pattern and waits for a target of type service_worker whose URL identifies the extension. Its example lists puppeteer: ^24.8.1 as a dependency; that is the tutorial’s illustrative dependency range, not a statement of the latest version. Puppeteer APIs can change, so check the documentation for the version installed in your project: Test Chrome Extensions with Puppeteer.
Use the new headless mode in CI
When running headlessly, use Chrome’s new mode. Puppeteer’s tutorial shows headless: 'new' as an option outside local development. For other launch configurations, Chrome documents --headless=new. Check whether your automation library already adds the flag before adding it yourself. The old headless mode does not support loading extensions, according to Chrome’s end-to-end testing guide.
Rank #2
Open and test the popup
Prefer assertions about what a user can see and do. When a test needs the extension page directly, navigate to chrome-extension://<id>/<page>. To test a popup, Chrome documents using action.openPopup() where the automation library supports it; otherwise, open the popup URL in another tab. The extension ID is part of that URL, so obtain it from the loaded extension or use a fixed ID if your test requires a stable origin.
Load an extension with Selenium and ChromeDriver
Unpacked extension directory
Pass the directory as a Chrome argument. Use an absolute path that exists in the test environment, especially in CI.
Free tools Windows power users keep installed
One-click scans. No signup required.
Rank #3
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.chrome.ChromeOptions;
ChromeOptions options = new ChromeOptions();
options.addArguments("load-extension=/absolute/path/to/extension");
options.addArguments("--headless=new");
ChromeDriver driver = new ChromeDriver(options);
try {
driver.get("chrome://extensions/");
// Add assertions for the extension's user-visible behavior.
} finally {
driver.quit();
}
Packaged .crx file
Use addExtensions for a packaged extension instead of the unpacked-directory argument.
import java.io.File;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.chrome.ChromeOptions;
ChromeOptions options = new ChromeOptions();
options.addExtensions(new File("/absolute/path/to/extension.crx"));
options.addArguments("--headless=new");
ChromeDriver driver = new ChromeDriver(options);
try {
driver.get("https://example.com");
// Add assertions for the extension's user-visible behavior.
} finally {
driver.quit();
}
ChromeDriver’s documented loading forms are covered in Chrome Extensions — ChromeDriver. ChromeDriver ordinarily creates a temporary profile for a session. If a test intentionally needs a configured profile, ChromeDriver supports a user-data-dir argument; see Capabilities and ChromeOptions.
Make startup, state, and assertions reliable
- Wait for readiness. Do not assume the extension is active as soon as the browser process starts. For Manifest V3, wait for its service-worker target with a bounded timeout and produce a useful test failure if it does not appear.
- Isolate tests. Use a fresh browser session or profile when tests should not share extension storage, cookies, or other browser state. Chrome’s Puppeteer tutorial warns that browser reuse can allow one test to affect another.
- Test observable behavior first. Verify the page changes or user interaction the extension is supposed to provide. Access internal extension pages or worker contexts when a specific test requires them.
- Account for service-worker lifecycle differences. Chrome notes that Selenium relies on ChromeDriver, which attaches a debugger to service workers and can prevent them from stopping as they normally would. If the test specifically depends on normal worker termination, use a strategy that does not keep that debugger attachment in place.
- Stabilize the extension origin only if needed. A fixed extension ID is useful when tests allow-list an origin or need to navigate to extension pages. Follow Chrome’s separate consistent-ID procedure linked from its end-to-end testing guide rather than assuming an ID from one local build will remain fixed.
Or skip the browser setup
If your goal is to capture a website screenshot rather than exercise the extension itself, ScreenshotNeo offers a one-request screenshot API. This does not preload or test a Chrome extension. For an API screenshot, use:
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
See the ScreenshotNeo API documentation for request options. Before capture, it accepts cookie/consent banners and removes more than 60 known consent platforms, newsletter popups, and chat widgets; each step can be disabled. Bot checks, blank pages, timeouts, failed loads, and cache hits are not billed, and response headers report the page verdict and billing status. Its MCP server provides screenshot tools for AI agents, including Claude, Cursor, and other MCP clients. The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000 shots.
Sign up free for 1,000 screenshots a month, with no card required.
Quick Recap
Best Value
Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
| Extension does not appear in the session | The path points to the wrong location, the directory is not a built extension, or the loading form does not match the artifact. | For an unpacked extension, pass the directory containing manifest.json. For ChromeDriver and a packaged build, use addExtensions with the .crx file. Confirm the path exists inside the CI worker. |
| Extension fails only in headless CI | The browser is using old headless mode or the automation tool is not launching the intended Chrome configuration. | Set --headless=new (or Puppeteer’s documented new-headless option), and check whether the library already supplies a headless flag. |
| Test looks for the worker too early | The extension service worker has not started yet. | Wait for the worker target with a finite timeout before interacting; include the target URL and session details in the timeout failure. |
| Extension page URL changes between runs | The test assumes a stable extension ID without configuring one. | Use Chrome’s consistent-ID instructions if the test needs a fixed ID, such as for an allow-listed origin or a direct extension-page URL. |
| Worker never terminates during a Selenium test | ChromeDriver’s debugger attachment can keep a service worker alive. | Do not treat termination timing under Selenium as normal user-session behavior. Choose a different approach for tests whose outcome depends on the worker stopping. |
| Tests pass alone but interfere in a suite | Sessions share browser profile or extension storage state. | Launch a fresh session/profile per test when isolation is required; reserve a configured user-data-dir for tests that intentionally need persistent state. |
Which approach should you use?
- Use Puppeteer’s extension-loading option when your suite is already built around Puppeteer and needs to wait on or interact with extension contexts.
- Use ChromeDriver’s directory argument for a local unpacked development build, and
addExtensionswhen the test must load the packaged.crx. - Use a fresh profile for isolation; use a fixed ID or persistent profile only when the test has a concrete need for them.
- Do not choose a tool based only on loading syntax if your assertion depends on popup behavior or service-worker termination; those contexts have different access and lifecycle caveats.
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.




