Files
Sawyer Hood 16dec89cfe feat!: ship doobie as dev-browser 1.0 (#133)
* doobie: skeleton — client, daemon, vm runner, page helpers, build

Co-Authored-By: Claude <noreply@anthropic.com>

* doobie: snapshot engine, waitForLoad, docs, tests, bench, Bun-native fast client

Co-Authored-By: Claude <noreply@anthropic.com>

* doobie: fix round 1 from adversarial review (transport backpressure, profiles per mode, page mutex, bringToFront, zombie runs, shot DPR, dialogs, snapshot names/frames/boxes, docs)

Co-Authored-By: Claude <noreply@anthropic.com>

* docs: handoff notes

Co-Authored-By: Claude <noreply@anthropic.com>

* launch: mark clean exit to skip session restore, cache --no-sandbox; untrack build artifact

Co-Authored-By: Claude <noreply@anthropic.com>

* snapshot: drop [cursor=pointer] on inherently interactive roles, no row content names (HN interactive 29k -> 21k chars)

Co-Authored-By: Claude <noreply@anthropic.com>

* ci: bench tolerance on shared runners; release: npm publish only with NPM_TOKEN

Co-Authored-By: Claude <noreply@anthropic.com>

* mcp: stdio MCP server over the daemon frames (doobie mcp); ci: explicit test timeout

Co-Authored-By: Claude <noreply@anthropic.com>

* docs: bb integration note (socket source contract)

Co-Authored-By: Claude <noreply@anthropic.com>

* fix round 2: front lock (no stale cache), run gate covers handles/frames/popups, --connect extends only touched tabs, doobie chrome verifies launch, self-healing shim + devDependencies, snapshot name fallbacks/pointer inheritance/shadow refs, docs

Co-Authored-By: Claude <noreply@anthropic.com>

* daemon: drain active requests before exit; tests: realpath-safe and node-optional packaging tests; chrome: share sandbox helpers

Co-Authored-By: Claude <noreply@anthropic.com>

* relative file paths resolve against the caller's cwd; readFile("downloads/<name>"); TimeoutError for ref waits

Co-Authored-By: Claude <noreply@anthropic.com>

* runtime: share host Error constructors with the script realm

Co-Authored-By: Claude <noreply@anthropic.com>

* shim: sh/JS polyglot so bun-only machines run it; v0.1.1

Co-Authored-By: Claude <noreply@anthropic.com>

* launch: automation profile prefs (leak-detection dialog off, no password/autofill UI) — fixes dead input after logins in new headless

Co-Authored-By: Claude <noreply@anthropic.com>

* v0.1.2

Co-Authored-By: Claude <noreply@anthropic.com>

* client: retry when racing a shutting-down daemon (flaky stop/status on CI)

Co-Authored-By: Claude <noreply@anthropic.com>

* feat!: make doobie the dev-browser 1.0 runtime

* ci: use Node 24 GitHub actions

* fix: isolate authenticated CDP sessions

* fix: close run gate escape paths

* fix: preserve UTF-8 across protocol chunks

* test: stabilize browser context gate coverage

* test: isolate browser context gate coverage

---------

Co-authored-by: Sawyer Hood <kirbyhood@gmail.com>
Co-authored-by: Claude <noreply@anthropic.com>
2026-09-03 17:21:40 -07:00

35 lines
1.5 KiB
TypeScript

import { test, expect, afterAll } from "bun:test";
import { withPage, closeBrowser } from "./helpers/browser.ts";
import { smallHtml } from "../bench/fixtures/small.ts";
import { serpHtml } from "../bench/fixtures/serp.ts";
afterAll(closeBrowser);
const stats = `(() => { const w = document.createTreeWalker(document, NodeFilter.SHOW_ALL); let n = 0; while (w.nextNode()) n++;
return { nodes: n, elements: document.querySelectorAll('*').length, links: document.querySelectorAll('a[href]').length, buttons: document.querySelectorAll('button').length }; })()`;
test("small fixture is ~3 KB with ~60 elements", async () => {
const html = smallHtml();
expect(html.length).toBeGreaterThan(2000);
expect(html.length).toBeLessThan(4500);
await withPage(async (page) => {
await page.setContent(html);
const s = (await page.evaluate(stats)) as { elements: number };
expect(s.elements).toBeGreaterThanOrEqual(50);
expect(s.elements).toBeLessThanOrEqual(80);
});
});
test("serp fixture has ~1500 nodes, 300 links, 100 buttons", async () => {
await withPage(async (page) => {
await page.setContent(serpHtml());
const s = (await page.evaluate(stats)) as { nodes: number; links: number; buttons: number };
expect(s.nodes).toBeGreaterThanOrEqual(1400);
expect(s.nodes).toBeLessThanOrEqual(2200);
expect(s.links).toBeGreaterThanOrEqual(300);
expect(s.links).toBeLessThanOrEqual(340);
expect(s.buttons).toBeGreaterThanOrEqual(100);
expect(s.buttons).toBeLessThanOrEqual(110);
});
});