@platejs/browser is the first-party proof harness for browser-visible editor behavior: model state, rendered DOM, native selection, focus, screenshots, traces, clipboard, and replayable scenarios. It is test infrastructure, not the product editing API; application code builds editors with Plate and Plite packages. Read Editing Behavior for the runtime pipeline, Selection And DOM for caret proof, Clipboard And Paste for payload proof, and Projection And Overlays for visible highlight and overlay proof.
The package is subpath-only:
@platejs/browser/playwright for Playwright editor harnesses.@platejs/browser/core for pure proof contracts, raw-mobile receipt
validation, capability classifiers, and selection serialization helpers.@platejs/browser/browser for DOM selection snapshots and zero-width
placeholder inspection in browser-capable test environments.Do not import from @platejs/browser directly.
import { expect, test } from "@playwright/test";
import { openExample } from "@platejs/browser/playwright";
test("types through the Browser path", async ({ page }) => {
const editor = await openExample(page, "plaintext", {
ready: { editor: "visible" },
});
await editor.focus();
await editor.type("Hello from @platejs/browser");
await editor.assert.text("Hello from @platejs/browser");
await editor.assert.noDoubleSelectionHighlight();
expect(await editor.get.selectedText()).toBe("");
});import { expect, test } from "@playwright/test";
import { openExample } from "@platejs/browser/playwright";
test("types through the Browser path", async ({ page }) => {
const editor = await openExample(page, "plaintext", {
ready: { editor: "visible" },
});
await editor.focus();
await editor.type("Hello from @platejs/browser");
await editor.assert.text("Hello from @platejs/browser");
await editor.assert.noDoubleSelectionHighlight();
expect(
openExample waits for the mounted editor-ready contract before actions run.
Prefer editor.type(...), semantic selection helpers, clipboard helpers, and
native event traces over raw Playwright DOM shortcuts when the claim is editor
behavior.
Do not use locator.fill() for contenteditable behavior. It can bypass the
editor path you are trying to prove. Use keyboard input, clipboard helpers, or
the Browser harness action that matches the claim.
Browser proof should usually assert more than model state:
| Claim | Useful proof |
|---|---|
| Model state changed correctly | model text, block text, operations, or commit metadata |
| Browser caret is correct | DOM caret, DOM selection, or native selection snapshot |
| Visible selection is sane | displayed selection snapshot and no double-highlight |
| Editor stayed active | focus ownership |
| Native input path is the bug | native event trace for input, paste, selection, or IME |
| Visual evidence matters | screenshot or JSON artifact |
| The editor still works afterward | follow-up typing after navigation, paste, undo, selection, or DOM repair |
Feature contracts group browser behavior families by the owning Plite feature area. Use them to keep example coverage honest without turning one manual route check into a fake global guarantee.
Use low-level helpers when a test needs to locate the editable or inspect a rendered node by path. Editor actions stay on the curated harness so volatile browser-handle method names do not become public API.
import {
getPliteBrowserEditable,
locatePliteBrowserBlock,
locatePliteBrowserText,
} from "@platejs/browser/playwright";import {
getPliteBrowserEditable,
locatePliteBrowserBlock,
locatePliteBrowserText,
} from "@platejs/browser/playwright";| Helper | Use |
|---|---|
getPliteBrowserEditable | Locate the first editor root in a page, frame, or scoped area. |
locatePliteBrowserBlock | Locate a rendered block by Plite path. |
locatePliteBrowserText | Locate a rendered text node by Plite path. |
Prefer the harness methods when they already express the behavior. They keep model, DOM, native-selection, and screenshot proof in one place instead of spreading selectors through tests.
Canonical scenarios contain serializable steps. They can be replayed, reduced, and attached as evidence to repository-owned release proof.
Use scenario.runImperative(...) only when browser work cannot be represented
by the canonical step union. The imperative lane preserves one trace for
interleaved setup and actions, but its result is explicitly non-replayable and
cannot satisfy replay, reduction, or release gates.
A Playwright mobile viewport does not prove raw-device behavior. Raw-device claims require a real device runner and an artifact that records the resolved device, OS, and capability scope. Proxy browser lanes remain useful evidence, but they do not claim native mobile clipboard, human soft-keyboard, glide typing, or voice input.
The Plite app exposes /mobile-lab for LAN-accessible browser capture:
pnpm --filter plite dev:lanpnpm --filter plite dev:lanOpen the printed LAN URL on the target browser, append /mobile-lab, reproduce
the input, take a snapshot, and export the replay JSON. The artifact records
browser events, model and DOM state, model and native selections, commit and
kernel traces, viewport facts, user agent, language, and touch metadata.
The route is an evidence collector. Its JSON explicitly does not certify a raw-device claim. A raw-device or broad release-ready claim needs the real-device runner, device/OS receipts, and evidence bound to the exact source commit.
pnpm --filter plite test:plite-browser
pnpm --filter plite test:plite-browser:chromium
pnpm --filter @platejs/browser testpnpm --filter plite test:plite-browser
pnpm --filter plite test:plite-browser:chromium
pnpm --filter @platejs/browser testUse focused Playwright specs for behavior routes and generated stress replay for portable editing failures.