// The composer with a lot in it: a 44,000-character saved draft, and a long prompt history with screenshots. At // 1440×900. import { bench, web } from "@hona/strobe" import { fixture, mockStressTimeline } from "../e2e/utils/session-fixture" import { countDraftEncodes, draftEnd, mockLargeDraft, recalled, seedHistory } from "./fixtures/composer" import { installProjects } from "./fixtures/home" import { cached } from "./fixtures/mock" const composer = '[data-component="composer-editor"]' const newSession = '[data-component="new-session"] [data-component="composer-editor"][contenteditable="true"]' const typed = " Keep the existing error handling and add coverage." export default bench({ // What a launch sets up: the large draft, or the prompt history. Cases with the same one share a launch. setupKey: (ctx) => (ctx.case.includes("history") ? "history" : "draft"), targets: { web: web({ expect: { title: "OpenCode" }, viewport: { width: 1440, height: 900 }, async prepare(ctx) { const { setupKey, url } = ctx const server = new URL(url).origin const page = cached(ctx) if (setupKey === "draft") { await countDraftEncodes(page) return mockLargeDraft(page, server) } await mockStressTimeline(page, { server }) await installProjects(page, [fixture.directory]) // A static file first: setup seeds the history before the app opens its store. return { path: "/favicon.svg" } }, url: ({ prepared }) => prepared.path, }), }, async setup({ setupKey, page }) { if (setupKey === "history") { await page.eval(seedHistory) await page.goto("/", { until: '[data-action="home-new-session"]', timeout: 60_000 }) await page.idle() return } // The caret at the end of the large draft. await page.waitFor(composer, { text: draftEnd, timeout: 60_000 }) await page.click(composer) await page.press("ControlOrMeta+End") await page.idle() }, cases: { // Each keystroke changes the draft and saves it; `count.draft.encode` is how many times the draft was encoded. "type in a large draft": { primary: "main.work", metrics: ["interaction.p95", "count.draft.encode"], async run({ page }) { await page.type(composer, typed, { delay: 50 }) await page.idle() }, async reset({ page }) { for (let step = 0; step < typed.length; step++) await page.press("Backspace") await page.idle() }, }, // Twenty caret moves: each saves the caret position, not the text. "move the cursor in a large draft": { primary: "main.work", metrics: ["interaction.p95", "count.draft.encode"], async run({ page }) { for (let step = 0; step < 20; step++) await page.press("ArrowLeft", { until: "frame" }) await page.idle() }, async reset({ page }) { await page.press("ControlOrMeta+End") await page.idle() }, }, // A new session's composer loads the history (counted: the run waits until the page settles), then ArrowUp // recalls the newest prompt and its screenshot. "recall a prompt from history": { primary: "main.work", metrics: ["run.duration"], async run({ page }) { await page.click('[data-action="home-new-session"]', { until: newSession }) await page.idle() await page.press("ArrowUp") await page.waitFor(newSession, { text: recalled }) await page.idle() }, async reset({ page }) { await page.goto("/", { until: '[data-action="home-new-session"]', timeout: 60_000 }) await page.idle() }, }, }, })