npm package: @ricsam/quickjs-playwright
Description: Playwright browser automation bridge for QuickJS
Author: Richard Samuelsson
Latest version: 0.2.20

# @ricsam/quickjs-playwright

Playwright browser automation bridge for QuickJS. Provides page object, locators, and expect matchers for browser testing from within the sandbox.

> **Note**: This is a low-level package. For most use cases, use [`@ricsam/quickjs-runtime`](../runtime) with `createRuntime({ playwright: { page } })` instead.

## Installation

```bash
bun add @ricsam/quickjs-playwright
```

## Setup

```typescript
import { chromium } from "playwright";
import { setupPlaywright } from "@ricsam/quickjs-playwright";

const browser = await chromium.launch();
const page = await browser.newPage();

const handle = setupPlaywright(context, {
  page,
  baseUrl: "https://example.com",
  timeout: 30000,
  console: true, // Route browser console logs through console handler
  onEvent: (event) => {
    if (event.type === "browserConsoleLog") {
      console.log(`[browser:${event.level}]`, ...event.args);
    }
  },
});

// Collected data
const data = handle.getBrowserConsoleLogs();
const requests = handle.getNetworkRequests();
const responses = handle.getNetworkResponses();

handle.clearCollected();
handle.dispose();
await browser.close();
```

## Injected Globals

- `page` - Page object with navigation and locator methods
- `Locator` - Element locator class with actions

## Usage in QuickJS

```javascript
// Navigation
await page.goto("/dashboard");
await page.reload();
const url = await page.url();
const title = await page.title();

// Waiting
await page.waitForSelector(".loaded");
await page.waitForTimeout(1000);
await page.waitForLoadState("networkidle");

// Locators
const button = page.locator("button.submit");
const heading = page.getByRole("heading", { name: "Welcome" });
const input = page.getByLabel("Email");
const link = page.getByText("Sign in");
const field = page.getByPlaceholder("Enter email");
const item = page.getByTestId("user-card");

// Locator actions
await button.click();
await input.fill("user@example.com");
await input.clear();
await button.hover();
await input.focus();

// Locator queries
const text = await heading.textContent();
const value = await input.inputValue();
const visible = await button.isVisible();
const enabled = await button.isEnabled();
const count = await page.locator("li").count();

// Chaining
await page.locator("ul").locator("li").nth(2).click();

// Request API
const response = await page.request.get("/api/users");
const data = await response.json();

await page.request.post("/api/users", {
  data: { name: "John" },
  headers: { "Content-Type": "application/json" },
});
```

## Playwright + Test Environment

When both test environment and playwright are set up, `expect()` is extended with locator matchers:

```javascript
describe("Homepage", () => {
  it("displays welcome message", async () => {
    await page.goto("/");
    await expect(page.getByRole("heading")).toBeVisible();
    await expect(page.getByRole("heading")).toContainText("Welcome");
  });

  it("login form works", async () => {
    await page.goto("/login");
    await page.getByLabel("Email").fill("user@example.com");
    await expect(page.getByLabel("Email")).toHaveValue("user@example.com");
    await expect(page.getByRole("button", { name: "Submit" })).toBeEnabled();
  });
});
```

## Locator Matchers

| Matcher | Description |
|---------|-------------|
| `toBeVisible()` | Assert element is visible |
| `toContainText(text)` | Assert element contains text |
| `toHaveValue(value)` | Assert input has value |
| `toBeEnabled()` | Assert element is enabled |
| `toBeChecked()` | Assert checkbox is checked |

All matchers support `.not` for negation: `expect(locator).not.toBeVisible()`

## PlaywrightEvent Types

| Event Type | Description |
|------------|-------------|
| `browserConsoleLog` | Browser console message (level, args, timestamp) |
| `networkRequest` | Network request made (url, method, headers, timestamp) |
| `networkResponse` | Network response received (url, status, headers, timestamp) |