/**
* Shared browser utilities for Playwright automation
*/
import { chromium, Browser, Page, BrowserContext } from 'playwright';
export interface BrowserOptions {
headless?: boolean;
timeout?: number;
userAgent?: string;
}
export class BrowserManager {
private browser: Browser | null = null;
private context: BrowserContext | null = null;
async launch(options: BrowserOptions = {}): Promise<void> {
this.browser = await chromium.launch({
headless: options.headless !== false,
args: ['--no-sandbox', '--disable-setuid-sandbox'],
});
this.context = await this.browser.newContext({
userAgent: options.userAgent,
});
}
async newPage(): Promise<Page> {
if (!this.context) {
throw new Error('Browser not launched. Call launch() first.');
}
return await this.context.newPage();
}
async close(): Promise<void> {
if (this.context) {
await this.context.close();
}
if (this.browser) {
await this.browser.close();
}
this.browser = null;
this.context = null;
}
isLaunched(): boolean {
return this.browser !== null;
}
}
// Singleton instance for reuse across tests
let globalBrowserManager: BrowserManager | null = null;
/**
* Get or create global browser manager instance
* Browser is launched on first call and reused for subsequent calls
*/
export async function getBrowserManager(): Promise<BrowserManager> {
if (!globalBrowserManager || !globalBrowserManager.isLaunched()) {
globalBrowserManager = new BrowserManager();
await globalBrowserManager.launch();
}
return globalBrowserManager;
}
/**
* Close global browser manager
* Call this on MCP server shutdown
*/
export async function closeBrowserManager(): Promise<void> {
if (globalBrowserManager) {
await globalBrowserManager.close();
globalBrowserManager = null;
}
}
/**
* Create an isolated browser instance (not singleton)
* Use this when you need a fresh browser context
*/
export async function createBrowserManager(options: BrowserOptions = {}): Promise<BrowserManager> {
const manager = new BrowserManager();
await manager.launch(options);
return manager;
}