playwright-crx-open
v0.15.29
Published
Playwright Chrome Extension with exposed internals for advanced state management and precise control. A fork of playwright-crx with all internal state accessible for library consumers who need full control over the lifecycle.
Maintainers
Readme
Playwright CRX Open
A fork of playwright-crx with exposed internals for advanced state management and precise control.
This package contains the Chrome Extensions flavor of the Playwright library, with all internal state accessible for library consumers who need full control over the lifecycle.
Why This Fork?
The original playwright-crx heavily encapsulates its internal state, making it difficult to:
- Clean up stale state when used as a library
- Recover from errors without reloading the extension
- Reattach to tabs without creating new ones
- Precisely control the debugger lifecycle
This fork exposes all internal state and provides utility methods for:
- State inspection: Access internal maps, caches, and singleton references
- State manipulation: Clear cached state, reset connections, force reattachment
- Lifecycle control: Precise control over when resources are created and destroyed
Installation
npm install playwright-crx-openKey Differences from playwright-crx
Exposed Internal State
All internal state is now accessible via public properties:
import { crx } from 'playwright-crx-open';
const crxApp = await crx.start();
// Access internal state directly
console.log(crx._transport); // CrxTransport instance
console.log(crx._browserPromise); // Browser promise
console.log(crx._crxApplicationPromise); // Regular mode app promise
// Access transport internals
const transport = crxApp.getTransport();
console.log(transport._tabToTarget); // Map<tabId, targetInfo>
console.log(transport._targetToTab); // Map<targetId, tabId>
console.log(transport._sessions); // Map<sessionId, tabId>New Utility Methods
On Crx class:
// Check if started without throwing
if (crx.isStarted()) {
// ...
}
// Clear cached promise without closing (use with caution)
crx.clearApplicationPromise();
// Full reset - closes everything and clears all state
await crx.reset();On CrxApplication class:
// Force attach - clears cached state first
const page = await crxApp.forceAttach(tabId);
// Check if closed
if (crxApp.isClosed()) {
// ...
}
// Reset closed state (use with caution)
crxApp.resetClosedState();
// Clear recorder app reference
crxApp.clearRecorderApp();
// Get parent instances
const crx = crxApp.getCrx();
const transport = crxApp.getTransport();On CrxTransport class:
const transport = crxApp.getTransport();
// Clear all cached mappings
transport.clearMappings();
// Clear specific tab
transport.clearTab(tabId);
// Get all attached tabs
const tabIds = transport.getAttachedTabIds();
// Get all targets
const targets = transport.getAllTargets();
// Check if attached
if (transport.isAttached(tabId)) {
// ...
}
// Force detach with cache clearing
await transport.forceDetach(tabId);
await transport.forceDetachAll();Common Use Cases
Reattaching to a Tab
When you need to reattach to a tab that was previously attached (e.g., after navigation or error):
// Instead of this (may return stale cached data):
const page = await crxApp.attach(tabId);
// Use this (clears cache first):
const page = await crxApp.forceAttach(tabId);Recovering from Errors
try {
await crxApp.recorder.run(code);
} catch (error) {
// Clear transport state
crxApp.getTransport().clearMappings();
// Or do a full reset
await crx.reset();
// Start fresh
const newApp = await crx.start();
}Cleanup on Extension Reload
// In your background script startup:
const cleanupOrphanedDebuggers = async () => {
const targets = await chrome.debugger.getTargets();
const attached = targets.filter(t => t.attached && t.tabId);
for (const target of attached) {
await chrome.debugger.detach({ tabId: target.tabId }).catch(() => {});
}
};
await cleanupOrphanedDebuggers();Original API
This fork maintains full compatibility with the original playwright-crx API:
import { crx, expect } from 'playwright-crx-open/test';
chrome.action.onClicked.addListener(async ({ id: tabId }) => {
const crxApp = await crx.start({ slowMo: 500 });
try {
const page = await crxApp.attach(tabId!).catch(() => crxApp.newPage());
await page.goto('https://demo.playwright.dev/todomvc/#/');
await page.getByPlaceholder('What needs to be done?').click();
await page.getByPlaceholder('What needs to be done?').fill('Hello World!');
await page.getByPlaceholder('What needs to be done?').press('Enter');
await expect(page.getByTestId('todo-title')).toHaveText('Hello World!');
} finally {
await crxApp.detach(page);
await crxApp.close();
}
});Tracing
Playwright CRX also supports tracing, compatible with Playwright Trace Viewer.
await page.context().tracing.start({ screenshots: true, snapshots: true });
await page.goto('https://demo.playwright.dev/todomvc');
// ... perform actions ...
await page.context().tracing.stop({ path: '/tmp/trace.zip' });
const data = crx.fs.readFileSync('/tmp/trace.zip');Build
To build playwright-crx-open:
npm ci
npm run buildCredits
This is a fork of playwright-crx by Rui Figueira.
License
Apache-2.0
