diff options
| author | Adam <[email protected]> | 2026-03-26 13:41:22 -0500 |
|---|---|---|
| committer | GitHub <[email protected]> | 2026-03-26 13:41:22 -0500 |
| commit | c7760b433b1bdbcaed7e7cd55d53b5b331f0f0fa (patch) | |
| tree | 4d1a865b8890dc30767b66293923c15e2b3f6d24 /packages/app/e2e | |
| parent | 2e6ac8ff49eabcb1b62c1bd504338e7449f80c6e (diff) | |
| download | opencode-c7760b433b1bdbcaed7e7cd55d53b5b331f0f0fa.tar.gz opencode-c7760b433b1bdbcaed7e7cd55d53b5b331f0f0fa.zip | |
fix(app): more startup perf (#19288)
Diffstat (limited to 'packages/app/e2e')
| -rw-r--r-- | packages/app/e2e/actions.ts | 5 | ||||
| -rw-r--r-- | packages/app/e2e/session/session-composer-dock.spec.ts | 21 | ||||
| -rw-r--r-- | packages/app/e2e/session/session-model-persistence.spec.ts | 192 |
3 files changed, 137 insertions, 81 deletions
diff --git a/packages/app/e2e/actions.ts b/packages/app/e2e/actions.ts index 90af177ed..efd370d39 100644 --- a/packages/app/e2e/actions.ts +++ b/packages/app/e2e/actions.ts @@ -465,10 +465,13 @@ export async function waitSession(page: Page, input: { directory: string; sessio if (!slug) return false const resolved = await resolveSlug(slug).catch(() => undefined) if (!resolved || resolved.directory !== target) return false - if (input.sessionID && sessionIDFromUrl(page.url()) !== input.sessionID) return false + const current = sessionIDFromUrl(page.url()) + if (input.sessionID && current !== input.sessionID) return false + if (!input.sessionID && current) return false const state = await probeSession(page) if (input.sessionID && (!state || state.sessionID !== input.sessionID)) return false + if (!input.sessionID && state?.sessionID) return false if (state?.dir) { const dir = await resolveDirectory(state.dir).catch(() => state.dir ?? "") if (dir !== target) return false diff --git a/packages/app/e2e/session/session-composer-dock.spec.ts b/packages/app/e2e/session/session-composer-dock.spec.ts index 5b2e8a8c6..f083bf359 100644 --- a/packages/app/e2e/session/session-composer-dock.spec.ts +++ b/packages/app/e2e/session/session-composer-dock.spec.ts @@ -93,7 +93,7 @@ async function todoDock(page: any, sessionID: string) { const write = async (driver: ComposerDriverState | undefined) => { await page.evaluate( - (input) => { + (input: { event: string; sessionID: string; driver: ComposerDriverState | undefined }) => { const win = window as ComposerWindow const composer = win.__opencode_e2e?.composer if (!composer?.enabled) throw new Error("Composer e2e driver is not enabled") @@ -118,7 +118,7 @@ async function todoDock(page: any, sessionID: string) { } const read = () => - page.evaluate((sessionID) => { + page.evaluate((sessionID: string) => { const win = window as ComposerWindow return win.__opencode_e2e?.composer?.sessions?.[sessionID]?.probe ?? null }, sessionID) as Promise<ComposerProbeState | null> @@ -186,6 +186,8 @@ async function withMockPermission<T>( opts: { child?: any } | undefined, fn: (state: { resolved: () => Promise<void> }) => Promise<T>, ) { + const listUrl = /\/permission(?:\?.*)?$/ + const replyUrls = [/\/session\/[^/]+\/permissions\/[^/?]+(?:\?.*)?$/, /\/permission\/[^/]+\/reply(?:\?.*)?$/] let pending = [ { ...request, @@ -204,7 +206,8 @@ async function withMockPermission<T>( const reply = async (route: any) => { const url = new URL(route.request().url()) - const id = url.pathname.split("/").pop() + const parts = url.pathname.split("/").filter(Boolean) + const id = parts.at(-1) === "reply" ? parts.at(-2) : parts.at(-1) pending = pending.filter((item) => item.id !== id) await route.fulfill({ status: 200, @@ -213,8 +216,10 @@ async function withMockPermission<T>( }) } - await page.route("**/permission", list) - await page.route("**/session/*/permissions/*", reply) + await page.route(listUrl, list) + for (const item of replyUrls) { + await page.route(item, reply) + } const sessionList = opts?.child ? async (route: any) => { @@ -242,8 +247,10 @@ async function withMockPermission<T>( try { return await fn(state) } finally { - await page.unroute("**/permission", list) - await page.unroute("**/session/*/permissions/*", reply) + await page.unroute(listUrl, list) + for (const item of replyUrls) { + await page.unroute(item, reply) + } if (sessionList) await page.unroute("**/session?*", sessionList) } } diff --git a/packages/app/e2e/session/session-model-persistence.spec.ts b/packages/app/e2e/session/session-model-persistence.spec.ts index b758a3b3d..36cbb0fbf 100644 --- a/packages/app/e2e/session/session-model-persistence.spec.ts +++ b/packages/app/e2e/session/session-model-persistence.spec.ts @@ -28,7 +28,17 @@ type Footer = { type Probe = { dir?: string sessionID?: string - model?: { providerID: string; modelID: string } + agent?: string + model?: { providerID: string; modelID: string; name?: string } + variant?: string | null + pick?: { + agent?: string + model?: { providerID: string; modelID: string } + variant?: string | null + } + variants?: string[] + models?: Array<{ providerID: string; modelID: string; name: string }> + agents?: Array<{ name: string }> } const escape = (value: string) => value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&") @@ -50,6 +60,86 @@ async function probe(page: Page): Promise<Probe | null> { }) } +async function currentModel(page: Page) { + await expect.poll(() => probe(page).then(modelKey), { timeout: 30_000 }).not.toBe(null) + const value = await probe(page).then(modelKey) + if (!value) throw new Error("Failed to resolve current model key") + return value +} + +async function waitControl(page: Page, key: "setAgent" | "setModel" | "setVariant") { + await expect + .poll( + () => + page.evaluate((key) => { + const win = window as Window & { + __opencode_e2e?: { + model?: { + controls?: Record<string, unknown> + } + } + } + return !!win.__opencode_e2e?.model?.controls?.[key] + }, key), + { timeout: 30_000 }, + ) + .toBe(true) +} + +async function pickAgent(page: Page, value: string) { + await waitControl(page, "setAgent") + await page.evaluate((value) => { + const win = window as Window & { + __opencode_e2e?: { + model?: { + controls?: { + setAgent?: (value: string | undefined) => void + } + } + } + } + const fn = win.__opencode_e2e?.model?.controls?.setAgent + if (!fn) throw new Error("Model e2e agent control is not enabled") + fn(value) + }, value) +} + +async function pickModel(page: Page, value: { providerID: string; modelID: string }) { + await waitControl(page, "setModel") + await page.evaluate((value) => { + const win = window as Window & { + __opencode_e2e?: { + model?: { + controls?: { + setModel?: (value: { providerID: string; modelID: string } | undefined) => void + } + } + } + } + const fn = win.__opencode_e2e?.model?.controls?.setModel + if (!fn) throw new Error("Model e2e model control is not enabled") + fn(value) + }, value) +} + +async function pickVariant(page: Page, value: string) { + await waitControl(page, "setVariant") + await page.evaluate((value) => { + const win = window as Window & { + __opencode_e2e?: { + model?: { + controls?: { + setVariant?: (value: string | undefined) => void + } + } + } + } + const fn = win.__opencode_e2e?.model?.controls?.setVariant + if (!fn) throw new Error("Model e2e variant control is not enabled") + fn(value) + }, value) +} + async function read(page: Page): Promise<Footer> { return { agent: await text(page.locator(`${promptAgentSelector} [data-slot="select-select-trigger-value"]`).first()), @@ -82,31 +172,15 @@ async function waitModel(page: Page, value: string) { async function choose(page: Page, root: string, value: string) { const select = page.locator(root) await expect(select).toBeVisible() - await select.locator('[data-action], [data-slot="select-select-trigger"]').first().click() - const item = page - .locator('[data-slot="select-select-item"]') - .filter({ hasText: new RegExp(`^\\s*${escape(value)}\\s*$`) }) - .first() - await expect(item).toBeVisible() - await item.click() + await pickAgent(page, value) } async function variantCount(page: Page) { - const select = page.locator(promptVariantSelector) - await expect(select).toBeVisible() - await select.locator('[data-slot="select-select-trigger"]').click() - const count = await page.locator('[data-slot="select-select-item"]').count() - await page.keyboard.press("Escape") - return count + return (await probe(page))?.variants?.length ?? 0 } async function agents(page: Page) { - const select = page.locator(promptAgentSelector) - await expect(select).toBeVisible() - await select.locator('[data-action], [data-slot="select-select-trigger"]').first().click() - const labels = await page.locator('[data-slot="select-select-item-label"]').allTextContents() - await page.keyboard.press("Escape") - return labels.map((item) => item.trim()).filter(Boolean) + return ((await probe(page))?.agents ?? []).map((item) => item.name).filter(Boolean) } async function ensureVariant(page: Page, directory: string): Promise<Footer> { @@ -132,48 +206,23 @@ async function ensureVariant(page: Page, directory: string): Promise<Footer> { async function chooseDifferentVariant(page: Page): Promise<Footer> { const current = await read(page) - const select = page.locator(promptVariantSelector) - await expect(select).toBeVisible() - await select.locator('[data-slot="select-select-trigger"]').click() - - const items = page.locator('[data-slot="select-select-item"]') - const count = await items.count() - if (count < 2) throw new Error("Current model has no alternate variant to select") - - for (let i = 0; i < count; i++) { - const item = items.nth(i) - const next = await text(item.locator('[data-slot="select-select-item-label"]').first()) - if (!next || next === current.variant) continue - await item.click() - return waitFooter(page, { agent: current.agent, model: current.model, variant: next }) - } + const next = (await probe(page))?.variants?.find((item) => item !== current.variant) + if (!next) throw new Error("Current model has no alternate variant to select") - throw new Error("Failed to choose a different variant") + await pickVariant(page, next) + return waitFooter(page, { agent: current.agent, model: current.model, variant: next }) } -async function chooseOtherModel(page: Page): Promise<Footer> { - const current = await read(page) - const button = page.locator(`${promptModelSelector} [data-action="prompt-model"]`) - await expect(button).toBeVisible() - await button.click() - - const dialog = page.getByRole("dialog") - await expect(dialog).toBeVisible() - const items = dialog.locator('[data-slot="list-item"]') - const count = await items.count() - expect(count).toBeGreaterThan(1) - - for (let i = 0; i < count; i++) { - const item = items.nth(i) - const selected = (await item.getAttribute("data-selected")) === "true" - if (selected) continue - await item.click() - await expect(dialog).toHaveCount(0) - await expect.poll(async () => (await read(page)).model !== current.model, { timeout: 30_000 }).toBe(true) - return read(page) - } - - throw new Error("Failed to choose a different model") +async function chooseOtherModel(page: Page, skip: string[] = []): Promise<Footer> { + const current = await currentModel(page) + const next = (await probe(page))?.models?.find((item) => { + const key = `${item.providerID}:${item.modelID}` + return key !== current && !skip.includes(key) + }) + if (!next) throw new Error("Failed to choose a different model") + await pickModel(page, { providerID: next.providerID, modelID: next.modelID }) + await expect.poll(async () => (await read(page)).model, { timeout: 30_000 }).toBe(next.name) + return read(page) } async function goto(page: Page, directory: string, sessionID?: string) { @@ -249,17 +298,14 @@ async function newWorkspaceSession(page: Page, slug: string) { return waitSession(page, { directory: next.directory }).then((item) => item.directory) } -test("session model and variant restore per session without leaking into new sessions", async ({ - page, - withProject, -}) => { +test("session model restore per session without leaking into new sessions", async ({ page, withProject }) => { await page.setViewportSize({ width: 1440, height: 900 }) await withProject(async ({ directory, gotoSession, trackSession }) => { await gotoSession() - await ensureVariant(page, directory) - const firstState = await chooseDifferentVariant(page) + const firstState = await chooseOtherModel(page) + const firstKey = await currentModel(page) const first = await submit(page, `session variant ${Date.now()}`) trackSession(first) await waitUser(directory, first) @@ -269,10 +315,10 @@ test("session model and variant restore per session without leaking into new ses await waitFooter(page, firstState) await gotoSession() - const fresh = await ensureVariant(page, directory) - expect(fresh.variant).not.toBe(firstState.variant) + const fresh = await read(page) + expect(fresh.model).not.toBe(firstState.model) - const secondState = await chooseOtherModel(page) + const secondState = await chooseOtherModel(page, [firstKey]) const second = await submit(page, `session model ${Date.now()}`) trackSession(second) await waitUser(directory, second) @@ -294,8 +340,8 @@ test("session model restore across workspaces", async ({ page, withProject }) => await withProject(async ({ directory: root, slug, gotoSession, trackDirectory, trackSession }) => { await gotoSession() - await ensureVariant(page, root) - const firstState = await chooseDifferentVariant(page) + const firstState = await chooseOtherModel(page) + const firstKey = await currentModel(page) const first = await submit(page, `root session ${Date.now()}`) trackSession(first, root) await waitUser(root, first) @@ -307,7 +353,8 @@ test("session model restore across workspaces", async ({ page, withProject }) => const oneDir = await newWorkspaceSession(page, one.slug) trackDirectory(oneDir) - const secondState = await chooseOtherModel(page) + const secondState = await chooseOtherModel(page, [firstKey]) + const secondKey = await currentModel(page) const second = await submit(page, `workspace one ${Date.now()}`) trackSession(second, oneDir) await waitUser(oneDir, second) @@ -316,8 +363,7 @@ test("session model restore across workspaces", async ({ page, withProject }) => const twoDir = await newWorkspaceSession(page, two.slug) trackDirectory(twoDir) - await ensureVariant(page, twoDir) - const thirdState = await chooseDifferentVariant(page) + const thirdState = await chooseOtherModel(page, [firstKey, secondKey]) const third = await submit(page, `workspace two ${Date.now()}`) trackSession(third, twoDir) await waitUser(twoDir, third) |
