From 179a1241d834c5d18d427b37d475ab6bee1d72e6 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 6 Dec 2025 02:41:33 +0000 Subject: [PATCH 1/2] Initial plan From fa66775cca1a35fa978424c453054e7180db3e3d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 6 Dec 2025 02:47:59 +0000 Subject: [PATCH 2/2] Add support for remote browser via CDP endpoint (BrowserStack/LambdaTest) Co-authored-by: npv2k1 <73846954+npv2k1@users.noreply.github.com> --- .env.example | 9 +++++++- README.md | 27 ++++++++++++++++++++++ src/index.ts | 26 +++++++++++++++++++++- tests/browser-config.spec.ts | 43 ++++++++++++++++++++++++++++++++++++ 4 files changed, 103 insertions(+), 2 deletions(-) create mode 100644 tests/browser-config.spec.ts diff --git a/.env.example b/.env.example index 55aeeb2..a63d786 100644 --- a/.env.example +++ b/.env.example @@ -1 +1,8 @@ -HEADLESS=false \ No newline at end of file +HEADLESS=false + +# Remote browser configuration (BrowserStack, LambdaTest, etc.) +# Set BROWSER_WS_ENDPOINT to connect to a remote browser via CDP +# Leave empty or unset to use a local browser +# Example BrowserStack: wss://cdp.browserstack.com/playwright?caps= +# Example LambdaTest: wss://cdp.lambdatest.com/playwright?capabilities= +BROWSER_WS_ENDPOINT= \ No newline at end of file diff --git a/README.md b/README.md index 432217b..5434695 100644 --- a/README.md +++ b/README.md @@ -23,6 +23,33 @@ dev - Starts the app in development mode with live reloading build - Compiles TypeScript to JavaScript start - Runs the compiled app test - Runs unit tests + +## Remote Browser Support + +This application supports connecting to remote browser services like BrowserStack or LambdaTest via CDP (Chrome DevTools Protocol). + +### Configuration + +Set the `BROWSER_WS_ENDPOINT` environment variable to connect to a remote browser: + +```bash +# BrowserStack example +BROWSER_WS_ENDPOINT=wss://cdp.browserstack.com/playwright?caps= + +# LambdaTest example +BROWSER_WS_ENDPOINT=wss://cdp.lambdatest.com/playwright?capabilities= +``` + +When `BROWSER_WS_ENDPOINT` is not set, the application uses a local browser. + +### Environment Variables + +| Variable | Description | Default | +|----------|-------------|---------| +| `BROWSER_WS_ENDPOINT` | WebSocket endpoint for remote browser connection | (empty - uses local browser) | +| `HEADLESS` | Run browser in headless mode (only applies to local browser) | `true` | +| `PORT` | Server port | `5510` | + Deployment Notes on how to deploy the app to production. diff --git a/src/index.ts b/src/index.ts index 155003a..f91770e 100644 --- a/src/index.ts +++ b/src/index.ts @@ -10,6 +10,29 @@ export type CaptureOptions = { fullPage?: boolean; }; +export type BrowserConfig = { + wsEndpoint?: string; + headless?: boolean; +}; + +// Function to get browser configuration from environment variables +export const getBrowserConfig = (): BrowserConfig => { + return { + wsEndpoint: process.env.BROWSER_WS_ENDPOINT, + headless: process.env.HEADLESS !== 'false', + }; +}; + +// Function to initialize browser (local or remote) +export const initBrowser = async (config: BrowserConfig): Promise => { + if (config.wsEndpoint) { + console.log(`Connecting to remote browser at: ${config.wsEndpoint}`); + return chromium.connectOverCDP(config.wsEndpoint); + } + console.log('Launching local browser'); + return chromium.launch({ headless: config.headless }); +}; + // Function to capture a screenshot using a shared browser instance const capturePage = async (browser: Browser, url, options?: CaptureOptions) => { const context = await browser.newContext(); @@ -52,7 +75,8 @@ const port = process.env.PORT || 5510; // Launch browser once when the server starts (async () => { - const browser = await chromium.launch({ headless: process.env.HEADLESS !== 'false' }); + const browserConfig = getBrowserConfig(); + const browser = await initBrowser(browserConfig); app.locals.browser = browser; // Health check endpoint diff --git a/tests/browser-config.spec.ts b/tests/browser-config.spec.ts new file mode 100644 index 0000000..f0c0b00 --- /dev/null +++ b/tests/browser-config.spec.ts @@ -0,0 +1,43 @@ +describe('Browser Configuration Types', () => { + describe('BrowserConfig environment variable handling', () => { + it('should handle BROWSER_WS_ENDPOINT environment variable', () => { + // Test the logic for determining browser config + const wsEndpoint = process.env.BROWSER_WS_ENDPOINT; + const isRemote = wsEndpoint && wsEndpoint.length > 0; + + // When wsEndpoint is not set, should use local browser + expect(isRemote).toBeFalsy(); + }); + + it('should handle HEADLESS environment variable', () => { + // Test the logic for determining headless mode + const headless = process.env.HEADLESS !== 'false'; + + // When HEADLESS is not set to 'false', should be true (default) + expect(typeof headless).toBe('boolean'); + }); + + it('should correctly parse headless as false when HEADLESS=false', () => { + const originalHeadless = process.env.HEADLESS; + process.env.HEADLESS = 'false'; + + const headless = process.env.HEADLESS !== 'false'; + expect(headless).toBe(false); + + // Restore + process.env.HEADLESS = originalHeadless; + }); + + it('should correctly use wsEndpoint for remote browser connection', () => { + const originalWsEndpoint = process.env.BROWSER_WS_ENDPOINT; + process.env.BROWSER_WS_ENDPOINT = 'wss://cdp.browserstack.com/playwright'; + + const wsEndpoint = process.env.BROWSER_WS_ENDPOINT; + const isRemote = wsEndpoint && wsEndpoint.length > 0; + expect(isRemote).toBe(true); + + // Restore + process.env.BROWSER_WS_ENDPOINT = originalWsEndpoint; + }); + }); +});