packages/app/perf/composer.bench.ts
Rama dev · réplica de lectura del repositorio original.
// 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()
},
},
},
})