Skip to content

Commit c7760b4

Browse files
authored
fix(app): more startup perf (anomalyco#19288)
1 parent 2e6ac8f commit c7760b4

28 files changed

Lines changed: 1012 additions & 568 deletions

packages/app/e2e/actions.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -465,10 +465,13 @@ export async function waitSession(page: Page, input: { directory: string; sessio
465465
if (!slug) return false
466466
const resolved = await resolveSlug(slug).catch(() => undefined)
467467
if (!resolved || resolved.directory !== target) return false
468-
if (input.sessionID && sessionIDFromUrl(page.url()) !== input.sessionID) return false
468+
const current = sessionIDFromUrl(page.url())
469+
if (input.sessionID && current !== input.sessionID) return false
470+
if (!input.sessionID && current) return false
469471

470472
const state = await probeSession(page)
471473
if (input.sessionID && (!state || state.sessionID !== input.sessionID)) return false
474+
if (!input.sessionID && state?.sessionID) return false
472475
if (state?.dir) {
473476
const dir = await resolveDirectory(state.dir).catch(() => state.dir ?? "")
474477
if (dir !== target) return false

packages/app/e2e/session/session-composer-dock.spec.ts

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ async function todoDock(page: any, sessionID: string) {
9393

9494
const write = async (driver: ComposerDriverState | undefined) => {
9595
await page.evaluate(
96-
(input) => {
96+
(input: { event: string; sessionID: string; driver: ComposerDriverState | undefined }) => {
9797
const win = window as ComposerWindow
9898
const composer = win.__opencode_e2e?.composer
9999
if (!composer?.enabled) throw new Error("Composer e2e driver is not enabled")
@@ -118,7 +118,7 @@ async function todoDock(page: any, sessionID: string) {
118118
}
119119

120120
const read = () =>
121-
page.evaluate((sessionID) => {
121+
page.evaluate((sessionID: string) => {
122122
const win = window as ComposerWindow
123123
return win.__opencode_e2e?.composer?.sessions?.[sessionID]?.probe ?? null
124124
}, sessionID) as Promise<ComposerProbeState | null>
@@ -186,6 +186,8 @@ async function withMockPermission<T>(
186186
opts: { child?: any } | undefined,
187187
fn: (state: { resolved: () => Promise<void> }) => Promise<T>,
188188
) {
189+
const listUrl = /\/permission(?:\?.*)?$/
190+
const replyUrls = [/\/session\/[^/]+\/permissions\/[^/?]+(?:\?.*)?$/, /\/permission\/[^/]+\/reply(?:\?.*)?$/]
189191
let pending = [
190192
{
191193
...request,
@@ -204,7 +206,8 @@ async function withMockPermission<T>(
204206

205207
const reply = async (route: any) => {
206208
const url = new URL(route.request().url())
207-
const id = url.pathname.split("/").pop()
209+
const parts = url.pathname.split("/").filter(Boolean)
210+
const id = parts.at(-1) === "reply" ? parts.at(-2) : parts.at(-1)
208211
pending = pending.filter((item) => item.id !== id)
209212
await route.fulfill({
210213
status: 200,
@@ -213,8 +216,10 @@ async function withMockPermission<T>(
213216
})
214217
}
215218

216-
await page.route("**/permission", list)
217-
await page.route("**/session/*/permissions/*", reply)
219+
await page.route(listUrl, list)
220+
for (const item of replyUrls) {
221+
await page.route(item, reply)
222+
}
218223

219224
const sessionList = opts?.child
220225
? async (route: any) => {
@@ -242,8 +247,10 @@ async function withMockPermission<T>(
242247
try {
243248
return await fn(state)
244249
} finally {
245-
await page.unroute("**/permission", list)
246-
await page.unroute("**/session/*/permissions/*", reply)
250+
await page.unroute(listUrl, list)
251+
for (const item of replyUrls) {
252+
await page.unroute(item, reply)
253+
}
247254
if (sessionList) await page.unroute("**/session?*", sessionList)
248255
}
249256
}

packages/app/e2e/session/session-model-persistence.spec.ts

Lines changed: 119 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,17 @@ type Footer = {
2828
type Probe = {
2929
dir?: string
3030
sessionID?: string
31-
model?: { providerID: string; modelID: string }
31+
agent?: string
32+
model?: { providerID: string; modelID: string; name?: string }
33+
variant?: string | null
34+
pick?: {
35+
agent?: string
36+
model?: { providerID: string; modelID: string }
37+
variant?: string | null
38+
}
39+
variants?: string[]
40+
models?: Array<{ providerID: string; modelID: string; name: string }>
41+
agents?: Array<{ name: string }>
3242
}
3343

3444
const escape = (value: string) => value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")
@@ -50,6 +60,86 @@ async function probe(page: Page): Promise<Probe | null> {
5060
})
5161
}
5262

63+
async function currentModel(page: Page) {
64+
await expect.poll(() => probe(page).then(modelKey), { timeout: 30_000 }).not.toBe(null)
65+
const value = await probe(page).then(modelKey)
66+
if (!value) throw new Error("Failed to resolve current model key")
67+
return value
68+
}
69+
70+
async function waitControl(page: Page, key: "setAgent" | "setModel" | "setVariant") {
71+
await expect
72+
.poll(
73+
() =>
74+
page.evaluate((key) => {
75+
const win = window as Window & {
76+
__opencode_e2e?: {
77+
model?: {
78+
controls?: Record<string, unknown>
79+
}
80+
}
81+
}
82+
return !!win.__opencode_e2e?.model?.controls?.[key]
83+
}, key),
84+
{ timeout: 30_000 },
85+
)
86+
.toBe(true)
87+
}
88+
89+
async function pickAgent(page: Page, value: string) {
90+
await waitControl(page, "setAgent")
91+
await page.evaluate((value) => {
92+
const win = window as Window & {
93+
__opencode_e2e?: {
94+
model?: {
95+
controls?: {
96+
setAgent?: (value: string | undefined) => void
97+
}
98+
}
99+
}
100+
}
101+
const fn = win.__opencode_e2e?.model?.controls?.setAgent
102+
if (!fn) throw new Error("Model e2e agent control is not enabled")
103+
fn(value)
104+
}, value)
105+
}
106+
107+
async function pickModel(page: Page, value: { providerID: string; modelID: string }) {
108+
await waitControl(page, "setModel")
109+
await page.evaluate((value) => {
110+
const win = window as Window & {
111+
__opencode_e2e?: {
112+
model?: {
113+
controls?: {
114+
setModel?: (value: { providerID: string; modelID: string } | undefined) => void
115+
}
116+
}
117+
}
118+
}
119+
const fn = win.__opencode_e2e?.model?.controls?.setModel
120+
if (!fn) throw new Error("Model e2e model control is not enabled")
121+
fn(value)
122+
}, value)
123+
}
124+
125+
async function pickVariant(page: Page, value: string) {
126+
await waitControl(page, "setVariant")
127+
await page.evaluate((value) => {
128+
const win = window as Window & {
129+
__opencode_e2e?: {
130+
model?: {
131+
controls?: {
132+
setVariant?: (value: string | undefined) => void
133+
}
134+
}
135+
}
136+
}
137+
const fn = win.__opencode_e2e?.model?.controls?.setVariant
138+
if (!fn) throw new Error("Model e2e variant control is not enabled")
139+
fn(value)
140+
}, value)
141+
}
142+
53143
async function read(page: Page): Promise<Footer> {
54144
return {
55145
agent: await text(page.locator(`${promptAgentSelector} [data-slot="select-select-trigger-value"]`).first()),
@@ -82,31 +172,15 @@ async function waitModel(page: Page, value: string) {
82172
async function choose(page: Page, root: string, value: string) {
83173
const select = page.locator(root)
84174
await expect(select).toBeVisible()
85-
await select.locator('[data-action], [data-slot="select-select-trigger"]').first().click()
86-
const item = page
87-
.locator('[data-slot="select-select-item"]')
88-
.filter({ hasText: new RegExp(`^\\s*${escape(value)}\\s*$`) })
89-
.first()
90-
await expect(item).toBeVisible()
91-
await item.click()
175+
await pickAgent(page, value)
92176
}
93177

94178
async function variantCount(page: Page) {
95-
const select = page.locator(promptVariantSelector)
96-
await expect(select).toBeVisible()
97-
await select.locator('[data-slot="select-select-trigger"]').click()
98-
const count = await page.locator('[data-slot="select-select-item"]').count()
99-
await page.keyboard.press("Escape")
100-
return count
179+
return (await probe(page))?.variants?.length ?? 0
101180
}
102181

103182
async function agents(page: Page) {
104-
const select = page.locator(promptAgentSelector)
105-
await expect(select).toBeVisible()
106-
await select.locator('[data-action], [data-slot="select-select-trigger"]').first().click()
107-
const labels = await page.locator('[data-slot="select-select-item-label"]').allTextContents()
108-
await page.keyboard.press("Escape")
109-
return labels.map((item) => item.trim()).filter(Boolean)
183+
return ((await probe(page))?.agents ?? []).map((item) => item.name).filter(Boolean)
110184
}
111185

112186
async function ensureVariant(page: Page, directory: string): Promise<Footer> {
@@ -132,48 +206,23 @@ async function ensureVariant(page: Page, directory: string): Promise<Footer> {
132206

133207
async function chooseDifferentVariant(page: Page): Promise<Footer> {
134208
const current = await read(page)
135-
const select = page.locator(promptVariantSelector)
136-
await expect(select).toBeVisible()
137-
await select.locator('[data-slot="select-select-trigger"]').click()
138-
139-
const items = page.locator('[data-slot="select-select-item"]')
140-
const count = await items.count()
141-
if (count < 2) throw new Error("Current model has no alternate variant to select")
142-
143-
for (let i = 0; i < count; i++) {
144-
const item = items.nth(i)
145-
const next = await text(item.locator('[data-slot="select-select-item-label"]').first())
146-
if (!next || next === current.variant) continue
147-
await item.click()
148-
return waitFooter(page, { agent: current.agent, model: current.model, variant: next })
149-
}
209+
const next = (await probe(page))?.variants?.find((item) => item !== current.variant)
210+
if (!next) throw new Error("Current model has no alternate variant to select")
150211

151-
throw new Error("Failed to choose a different variant")
212+
await pickVariant(page, next)
213+
return waitFooter(page, { agent: current.agent, model: current.model, variant: next })
152214
}
153215

154-
async function chooseOtherModel(page: Page): Promise<Footer> {
155-
const current = await read(page)
156-
const button = page.locator(`${promptModelSelector} [data-action="prompt-model"]`)
157-
await expect(button).toBeVisible()
158-
await button.click()
159-
160-
const dialog = page.getByRole("dialog")
161-
await expect(dialog).toBeVisible()
162-
const items = dialog.locator('[data-slot="list-item"]')
163-
const count = await items.count()
164-
expect(count).toBeGreaterThan(1)
165-
166-
for (let i = 0; i < count; i++) {
167-
const item = items.nth(i)
168-
const selected = (await item.getAttribute("data-selected")) === "true"
169-
if (selected) continue
170-
await item.click()
171-
await expect(dialog).toHaveCount(0)
172-
await expect.poll(async () => (await read(page)).model !== current.model, { timeout: 30_000 }).toBe(true)
173-
return read(page)
174-
}
175-
176-
throw new Error("Failed to choose a different model")
216+
async function chooseOtherModel(page: Page, skip: string[] = []): Promise<Footer> {
217+
const current = await currentModel(page)
218+
const next = (await probe(page))?.models?.find((item) => {
219+
const key = `${item.providerID}:${item.modelID}`
220+
return key !== current && !skip.includes(key)
221+
})
222+
if (!next) throw new Error("Failed to choose a different model")
223+
await pickModel(page, { providerID: next.providerID, modelID: next.modelID })
224+
await expect.poll(async () => (await read(page)).model, { timeout: 30_000 }).toBe(next.name)
225+
return read(page)
177226
}
178227

179228
async function goto(page: Page, directory: string, sessionID?: string) {
@@ -249,17 +298,14 @@ async function newWorkspaceSession(page: Page, slug: string) {
249298
return waitSession(page, { directory: next.directory }).then((item) => item.directory)
250299
}
251300

252-
test("session model and variant restore per session without leaking into new sessions", async ({
253-
page,
254-
withProject,
255-
}) => {
301+
test("session model restore per session without leaking into new sessions", async ({ page, withProject }) => {
256302
await page.setViewportSize({ width: 1440, height: 900 })
257303

258304
await withProject(async ({ directory, gotoSession, trackSession }) => {
259305
await gotoSession()
260306

261-
await ensureVariant(page, directory)
262-
const firstState = await chooseDifferentVariant(page)
307+
const firstState = await chooseOtherModel(page)
308+
const firstKey = await currentModel(page)
263309
const first = await submit(page, `session variant ${Date.now()}`)
264310
trackSession(first)
265311
await waitUser(directory, first)
@@ -269,10 +315,10 @@ test("session model and variant restore per session without leaking into new ses
269315
await waitFooter(page, firstState)
270316

271317
await gotoSession()
272-
const fresh = await ensureVariant(page, directory)
273-
expect(fresh.variant).not.toBe(firstState.variant)
318+
const fresh = await read(page)
319+
expect(fresh.model).not.toBe(firstState.model)
274320

275-
const secondState = await chooseOtherModel(page)
321+
const secondState = await chooseOtherModel(page, [firstKey])
276322
const second = await submit(page, `session model ${Date.now()}`)
277323
trackSession(second)
278324
await waitUser(directory, second)
@@ -294,8 +340,8 @@ test("session model restore across workspaces", async ({ page, withProject }) =>
294340
await withProject(async ({ directory: root, slug, gotoSession, trackDirectory, trackSession }) => {
295341
await gotoSession()
296342

297-
await ensureVariant(page, root)
298-
const firstState = await chooseDifferentVariant(page)
343+
const firstState = await chooseOtherModel(page)
344+
const firstKey = await currentModel(page)
299345
const first = await submit(page, `root session ${Date.now()}`)
300346
trackSession(first, root)
301347
await waitUser(root, first)
@@ -307,7 +353,8 @@ test("session model restore across workspaces", async ({ page, withProject }) =>
307353
const oneDir = await newWorkspaceSession(page, one.slug)
308354
trackDirectory(oneDir)
309355

310-
const secondState = await chooseOtherModel(page)
356+
const secondState = await chooseOtherModel(page, [firstKey])
357+
const secondKey = await currentModel(page)
311358
const second = await submit(page, `workspace one ${Date.now()}`)
312359
trackSession(second, oneDir)
313360
await waitUser(oneDir, second)
@@ -316,8 +363,7 @@ test("session model restore across workspaces", async ({ page, withProject }) =>
316363
const twoDir = await newWorkspaceSession(page, two.slug)
317364
trackDirectory(twoDir)
318365

319-
await ensureVariant(page, twoDir)
320-
const thirdState = await chooseDifferentVariant(page)
366+
const thirdState = await chooseOtherModel(page, [firstKey, secondKey])
321367
const third = await submit(page, `workspace two ${Date.now()}`)
322368
trackSession(third, twoDir)
323369
await waitUser(twoDir, third)

0 commit comments

Comments
 (0)