import type { Page } from 'playwright'; import type { BrowserOptions, ScrapingResult } from './types'; /** * Simple browser implementation for testing */ export class SimpleBrowser { private browser: any; private contexts = new Map(); private logger: any; private initialized = false; private _options: BrowserOptions = { headless: true, timeout: 30000, blockResources: false, enableNetworkLogging: false, }; constructor(logger?: any) { this.logger = logger || console; // Initialize mock browser this.browser = { newContext: async () => { const pages: any[] = []; const context = { newPage: async () => { const page = { goto: async () => {}, close: async () => {}, evaluate: async () => {}, waitForSelector: async () => {}, screenshot: async () => Buffer.from('screenshot'), setViewport: async () => {}, content: async () => '', on: () => {}, route: async () => {}, }; pages.push(page); return page; }, close: async () => {}, pages: async () => pages, }; return context; }, close: async () => {}, isConnected: () => true, }; } async initialize(options: BrowserOptions = {}): Promise { if (this.initialized) { return; } // Merge options this._options = { ...this._options, ...options }; this.logger.info('Initializing browser...'); // Mock browser is already initialized in constructor for simplicity this.initialized = true; } async createContext(id?: string): Promise { if (!this.browser) { await this.initialize(); } const contextId = id || `context-${Date.now()}`; const context = await this.browser.newContext(); this.contexts.set(contextId, context); return contextId; } async closeContext(contextId: string): Promise { const context = this.contexts.get(contextId); if (context) { await context.close(); this.contexts.delete(contextId); } } async newPage(contextId: string): Promise { const context = this.contexts.get(contextId); if (!context) { throw new Error(`Context ${contextId} not found`); } const page = await context.newPage(); // Add resource blocking if enabled if (this._options?.blockResources) { await page.route('**/*.{png,jpg,jpeg,gif,svg,ico,woff,woff2,ttf,css}', (route: any) => { route.abort(); }); } return page; } async goto(page: Page, url: string, options?: any): Promise { await page.goto(url, { timeout: this._options?.timeout || 30000, ...options, }); } async scrape(url: string, options?: { contextId?: string }): Promise { try { let contextId = options?.contextId; const shouldCloseContext = !contextId; if (!contextId) { contextId = await this.createContext(); } const page = await this.newPage(contextId); await this.goto(page, url); // Mock data for testing const data = { title: 'Test Title', text: 'Test content', links: ['link1', 'link2'], }; await page.close(); if (shouldCloseContext) { await this.closeContext(contextId); } return { success: true, data, url, }; } catch (error: any) { return { success: false, error: error.message, url, data: {} as any, }; } } async close(): Promise { if (!this.browser) { return; } // Close all contexts for (const [_contextId, context] of this.contexts) { await context.close(); } this.contexts.clear(); await this.browser.close(); this.browser = null; this.initialized = false; } }