Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion .env.example
Original file line number Diff line number Diff line change
@@ -1 +1,8 @@
HEADLESS=false
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=<encoded_caps>
# Example LambdaTest: wss://cdp.lambdatest.com/playwright?capabilities=<encoded_caps>
BROWSER_WS_ENDPOINT=
27 changes: 27 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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=<encoded_capabilities>

# LambdaTest example
BROWSER_WS_ENDPOINT=wss://cdp.lambdatest.com/playwright?capabilities=<encoded_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.

Expand Down
26 changes: 25 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<Browser> => {
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();
Expand Down Expand Up @@ -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
Expand Down
43 changes: 43 additions & 0 deletions tests/browser-config.spec.ts
Original file line number Diff line number Diff line change
@@ -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;
});
});
});