From ce09fc8356c8967e83f32f092a33952a303d4fef Mon Sep 17 00:00:00 2001 From: "opencode-agent[bot]" Date: Mon, 18 May 2026 22:03:00 +0000 Subject: [PATCH] chore: generate --- .../app/e2e/smoke/session-timeline.fixture.ts | 70 +++++++++++++++---- .../app/e2e/smoke/session-timeline.spec.ts | 35 ++++++++-- packages/app/e2e/utils/mock-server.ts | 11 ++- 3 files changed, 96 insertions(+), 20 deletions(-) diff --git a/packages/app/e2e/smoke/session-timeline.fixture.ts b/packages/app/e2e/smoke/session-timeline.fixture.ts index e9af320ecb..1fc8571db4 100644 --- a/packages/app/e2e/smoke/session-timeline.fixture.ts +++ b/packages/app/e2e/smoke/session-timeline.fixture.ts @@ -111,7 +111,13 @@ function reasoningPart(index: number, partIndex: number, length: number): Messag } } -function toolPart(index: number, partIndex: number, tool: string, input: Record, outputLength = 160): MessagePart { +function toolPart( + index: number, + partIndex: number, + tool: string, + input: Record, + outputLength = 160, +): MessagePart { const metadata = tool === "apply_patch" ? { files: [patchFile(index, "update"), patchFile(index + 1, index % 2 === 0 ? "add" : "delete")] } @@ -168,7 +174,9 @@ function patch(seed: number, length: number) { } function code(seed: number, lines: number) { - return Array.from({ length: lines }, (_, index) => `export const value${index} = "${lorem(seed + index, 32)}"`).join("\n") + return Array.from({ length: lines }, (_, index) => `export const value${index} = "${lorem(seed + index, 32)}"`).join( + "\n", + ) } function turn(index: number): Message[] { @@ -189,12 +197,14 @@ function turn(index: number): Message[] { ...(index % 6 === 0 ? [toolPart(index, 7, "write", { filePath: `src/generated/write-${index}.ts`, content: code(index, 28) }, 560)] : []), - ...(index % 8 === 0 ? [toolPart(index, 8, "apply_patch", { files: [`src/generated/patch-${index}.ts`] }, 620)] : []), - ...(index % 7 === 0 ? [toolPart(index, 4, "bash", { command: "bun typecheck", description: "Verify generated output" }, 620)] : []), - ...(index % 10 === 0 ? [toolPart(index, 9, "webfetch", { url: "https://example.com/docs/sample" }, 120)] : []), - ...(index % 11 === 0 - ? [toolPart(index, 10, "websearch", { query: "sample movement notes" }, 240)] + ...(index % 8 === 0 + ? [toolPart(index, 8, "apply_patch", { files: [`src/generated/patch-${index}.ts`] }, 620)] : []), + ...(index % 7 === 0 + ? [toolPart(index, 4, "bash", { command: "bun typecheck", description: "Verify generated output" }, 620)] + : []), + ...(index % 10 === 0 ? [toolPart(index, 9, "webfetch", { url: "https://example.com/docs/sample" }, 120)] : []), + ...(index % 11 === 0 ? [toolPart(index, 10, "websearch", { query: "sample movement notes" }, 240)] : []), ...(index % 13 === 0 ? [ toolPart( @@ -232,7 +242,14 @@ function orderedParts(message: Message) { export const fixture = { directory, - project: { id: projectID, worktree: directory, vcs: "git", name: "smoke-project", time: { created: 1700000000000, updated: 1700000000000 }, sandboxes: [] }, + project: { + id: projectID, + worktree: directory, + vcs: "git", + name: "smoke-project", + time: { created: 1700000000000, updated: 1700000000000 }, + sandboxes: [], + }, provider: { all: [ { @@ -245,8 +262,24 @@ export const fixture = { default: { providerID: "opencode", modelID: "claude-opus-4-6" }, }, sessions: [ - { id: sourceID, slug: "source", projectID, directory, title: "Uncommitted changes inquiry", version: "dev", time: { created: 1700000000000, updated: 1700000000000 } }, - { id: targetID, slug: "target", projectID, directory, title: "Example Game: sample jump movement & sample physics analysis", version: "dev", time: { created: 1700000001000, updated: 1700000001000 } }, + { + id: sourceID, + slug: "source", + projectID, + directory, + title: "Uncommitted changes inquiry", + version: "dev", + time: { created: 1700000000000, updated: 1700000000000 }, + }, + { + id: targetID, + slug: "target", + projectID, + directory, + title: "Example Game: sample jump movement & sample physics analysis", + version: "dev", + time: { created: 1700000001000, updated: 1700000001000 }, + }, ], sourceID, targetID, @@ -254,14 +287,25 @@ export const fixture = { expected: { sourceTitle: "Uncommitted changes inquiry", targetTitle: "Example Game: sample jump movement & sample physics analysis", - targetMessageIDs: targetMessages.filter((message) => message.info.role === "user").map((message) => message.info.id), - targetPartIDs: targetMessages.flatMap((message) => orderedParts(message).filter(renderable).map((part) => part.id)), + targetMessageIDs: targetMessages + .filter((message) => message.info.role === "user") + .map((message) => message.info.id), + targetPartIDs: targetMessages.flatMap((message) => + orderedParts(message) + .filter(renderable) + .map((part) => part.id), + ), }, } export function pageMessages(sessionID: string, limit: number, before?: string) { const messages = fixture.messages[sessionID as keyof typeof fixture.messages] ?? [] - const end = before ? Math.max(0, messages.findIndex((message) => message.info.id === before)) : messages.length + const end = before + ? Math.max( + 0, + messages.findIndex((message) => message.info.id === before), + ) + : messages.length const start = Math.max(0, end - limit) return { items: messages.slice(start, end), diff --git a/packages/app/e2e/smoke/session-timeline.spec.ts b/packages/app/e2e/smoke/session-timeline.spec.ts index 868f1bcf44..3dda017dde 100644 --- a/packages/app/e2e/smoke/session-timeline.spec.ts +++ b/packages/app/e2e/smoke/session-timeline.spec.ts @@ -172,7 +172,12 @@ async function configureSmokePage(page: Page) { }) } -async function expectCanScrollToStart(page: Page, expectedPartIDs: string[], expectedMessageIDs: string[], errors: string[]) { +async function expectCanScrollToStart( + page: Page, + expectedPartIDs: string[], + expectedMessageIDs: string[], + errors: string[], +) { await pointAtTimeline(page) const seenParts = new Set() const seenMessages = new Set() @@ -189,7 +194,11 @@ async function expectCanScrollToStart(page: Page, expectedPartIDs: string[], exp expectOrderedIDs(expectedMessageIDs, unique(current.messageIds), "mounted message") expectOrderedIDs(expectedMessageIDs, unique(current.visibleMessageIds), "visible message") - if (current.scrollTop <= 1 && seenParts.size === expectedPartIDs.length && seenMessages.size === expectedMessageIDs.length) { + if ( + current.scrollTop <= 1 && + seenParts.size === expectedPartIDs.length && + seenMessages.size === expectedMessageIDs.length + ) { expectCompleteScroll(current, expectedPartIDs, expectedMessageIDs, seenParts, seenMessages, samples) return } @@ -345,7 +354,12 @@ async function waitForTimelineStable(page: Page) { ) } -async function expectSessionTimelineReady(page: Page, expectedPartIDs: string[], expectedMessageIDs: string[], errors: string[]) { +async function expectSessionTimelineReady( + page: Page, + expectedPartIDs: string[], + expectedMessageIDs: string[], + errors: string[], +) { await waitForTimelineStable(page) for (const text of forbiddenText) await expect(page.getByText(text)).toHaveCount(0) const currentState = await timelineState(page) @@ -365,8 +379,14 @@ function expectCompleteScroll( samples: TraversalSample[], ) { expect(state.scrollTop, `timeline should reach the start\n${sampleSummary(samples)}`).toBeLessThanOrEqual(1) - expect(expectedPartIDs.filter((id) => !seenParts.has(id)), `missing visible timeline parts\n${sampleSummary(samples)}`).toEqual([]) - expect(expectedMessageIDs.filter((id) => !seenMessages.has(id)), `missing visible messages\n${sampleSummary(samples)}`).toEqual([]) + expect( + expectedPartIDs.filter((id) => !seenParts.has(id)), + `missing visible timeline parts\n${sampleSummary(samples)}`, + ).toEqual([]) + expect( + expectedMessageIDs.filter((id) => !seenMessages.has(id)), + `missing visible messages\n${sampleSummary(samples)}`, + ).toEqual([]) expect(new Set(expectedPartIDs).size).toBe(expectedPartIDs.length) expect(new Set(expectedMessageIDs).size).toBe(expectedMessageIDs.length) expect(expectedPartIDs.length).toBe(331) @@ -379,7 +399,10 @@ async function openProject(page: Page, projectName: string) { async function navigateToSession(page: Page, sessionId: string, expectedTitle: string) { // Use evaluate to click to avoid strict visibility/animation issues during rapid e2e navigation - await page.locator(`a[href*="${sessionId}"]`).first().evaluate((el) => (el as HTMLElement).click()) + await page + .locator(`a[href*="${sessionId}"]`) + .first() + .evaluate((el) => (el as HTMLElement).click()) await expect(page.getByRole("heading", { name: expectedTitle })).toBeVisible() } diff --git a/packages/app/e2e/utils/mock-server.ts b/packages/app/e2e/utils/mock-server.ts index 2ff1994d2f..b89f46deea 100644 --- a/packages/app/e2e/utils/mock-server.ts +++ b/packages/app/e2e/utils/mock-server.ts @@ -1,6 +1,15 @@ import type { Page, Route } from "@playwright/test" -const emptyList = new Set(["/skill", "/command", "/lsp", "/formatter", "/permission", "/question", "/vcs/status", "/vcs/diff"]) +const emptyList = new Set([ + "/skill", + "/command", + "/lsp", + "/formatter", + "/permission", + "/question", + "/vcs/status", + "/vcs/diff", +]) const emptyObject = new Set(["/global/config", "/config", "/provider/auth", "/mcp", "/session/status"]) export interface MockServerConfig {