Procházet zdrojové kódy

refactor(app): remove dead e2e fixtures, helpers, and types

Delete unused code from the e2e test infrastructure:

- fixtures: withProject, withBackendProject, withMockProject, runProject,
  AssistantFixture type, assistant fixture
- actions: clickPopoverButton, seedSessionPermission, seedSessionTodos
- Update AGENTS.md to reflect the project fixture pattern
Kit Langton před 2 týdny
rodič
revize
bcddb59c9c

+ 7 - 8
packages/app/e2e/AGENTS.md

@@ -59,8 +59,10 @@ test("test description", async ({ page, sdk, gotoSession }) => {
 ### Using Fixtures
 
 - `page` - Playwright page
-- `sdk` - OpenCode SDK client for API calls
-- `gotoSession(sessionID?)` - Navigate to session
+- `llm` - Mock LLM server for queuing responses (`text`, `tool`, `toolMatch`, `textMatch`, etc.)
+- `project` - Golden-path project fixture (call `project.open()` first, then use `project.sdk`, `project.prompt(...)`, `project.gotoSession(...)`, `project.trackSession(...)`)
+- `sdk` - OpenCode SDK client for API calls (worker-scoped, shared directory)
+- `gotoSession(sessionID?)` - Navigate to session (worker-scoped, shared directory)
 
 ### Helper Functions
 
@@ -73,12 +75,9 @@ test("test description", async ({ page, sdk, gotoSession }) => {
 - `waitTerminalReady(page, { term? })` - Wait for a mounted terminal to connect and finish rendering output
 - `runTerminal(page, { cmd, token, term?, timeout? })` - Type into the terminal via the browser and wait for rendered output
 - `withSession(sdk, title, callback)` - Create temp session
-- `withProject(...)` - Create temp project/workspace
 - `sessionIDFromUrl(url)` - Read session ID from URL
 - `slugFromUrl(url)` - Read workspace slug from URL
 - `waitSlug(page, skip?)` - Wait for resolved workspace slug
-- `trackSession(sessionID, directory?)` - Register session for fixture cleanup
-- `trackDirectory(directory)` - Register directory for fixture cleanup
 - `clickListItem(container, filter)` - Click list item by key/text
 
 **Selectors** (`selectors.ts`):
@@ -128,9 +127,9 @@ test("test with cleanup", async ({ page, sdk, gotoSession }) => {
 })
 ```
 
-- Prefer `withSession(...)` for temp sessions
-- In `withProject(...)` tests that create sessions or extra workspaces, call `trackSession(sessionID, directory?)` and `trackDirectory(directory)`
-- This lets fixture teardown abort, wait for idle, and clean up safely under CI concurrency
+- Prefer the `project` fixture for tests that need a dedicated project with LLM mocking — call `project.open()` then use `project.prompt(...)`, `project.trackSession(...)`, etc.
+- Use `withSession(sdk, title, callback)` for lightweight temp sessions on the shared worker directory
+- Call `project.trackSession(sessionID, directory?)` and `project.trackDirectory(directory)` for any resources created outside the fixture so teardown can clean them up
 - Avoid calling `sdk.session.delete(...)` directly
 
 ### Timeouts

+ 0 - 70
packages/app/e2e/actions.ts

@@ -637,12 +637,6 @@ export async function openSharePopover(page: Page) {
   return { rightSection: scroller, popoverBody }
 }
 
-export async function clickPopoverButton(page: Page, buttonName: string | RegExp) {
-  const button = page.getByRole("button").filter({ hasText: buttonName }).first()
-  await expect(button).toBeVisible()
-  await button.click()
-}
-
 export async function clickListItem(
   container: Locator | Page,
   filter: string | RegExp | { key?: string; text?: string | RegExp; keyStartsWith?: string },
@@ -808,40 +802,6 @@ export async function seedSessionQuestion(
   return { id: result.id }
 }
 
-export async function seedSessionPermission(
-  sdk: ReturnType<typeof createSdk>,
-  input: {
-    sessionID: string
-    permission: string
-    patterns: string[]
-    description?: string
-  },
-) {
-  const text = [
-    "Your only valid response is one bash tool call.",
-    `Use this JSON input: ${JSON.stringify({
-      command: input.patterns[0] ? `ls ${JSON.stringify(input.patterns[0])}` : "pwd",
-      workdir: "/",
-      description: input.description ?? `seed ${input.permission} permission request`,
-    })}`,
-    "Do not output plain text.",
-  ].join("\n")
-
-  const result = await seed({
-    sdk,
-    sessionID: input.sessionID,
-    prompt: text,
-    timeout: 30_000,
-    probe: async () => {
-      const list = await sdk.permission.list().then((x) => x.data ?? [])
-      return list.find((item) => item.sessionID === input.sessionID)
-    },
-  })
-
-  if (!result) throw new Error("Timed out seeding permission request")
-  return { id: result.id }
-}
-
 export async function seedSessionTask(
   sdk: ReturnType<typeof createSdk>,
   input: {
@@ -900,36 +860,6 @@ export async function seedSessionTask(
   return result
 }
 
-export async function seedSessionTodos(
-  sdk: ReturnType<typeof createSdk>,
-  input: {
-    sessionID: string
-    todos: Array<{ content: string; status: string; priority: string }>
-  },
-) {
-  const text = [
-    "Your only valid response is one todowrite tool call.",
-    `Use this JSON input: ${JSON.stringify({ todos: input.todos })}`,
-    "Do not output plain text.",
-  ].join("\n")
-  const target = JSON.stringify(input.todos)
-
-  const result = await seed({
-    sdk,
-    sessionID: input.sessionID,
-    prompt: text,
-    timeout: 30_000,
-    probe: async () => {
-      const todos = await sdk.session.todo({ sessionID: input.sessionID }).then((x) => x.data ?? [])
-      if (JSON.stringify(todos) !== target) return
-      return true
-    },
-  })
-
-  if (!result) throw new Error("Timed out seeding todos")
-  return true
-}
-
 export async function clearSessionDockSeed(sdk: ReturnType<typeof createSdk>, sessionID: string) {
   const [questions, permissions] = await Promise.all([
     sdk.question.list().then((x) => x.data ?? []),

+ 0 - 112
packages/app/e2e/fixtures.ts

@@ -18,7 +18,6 @@ import {
   waitSlug,
   withNoReplyPrompt,
 } from "./actions"
-import { openaiModel, withMockOpenAI } from "./prompt/mock"
 import { promptSelector } from "./selectors"
 import { createSdk, dirSlug, getWorktree, sessionPath } from "./utils"
 
@@ -59,19 +58,6 @@ type LLMWorker = LLMFixture & {
   reset: () => Promise<void>
 }
 
-type AssistantFixture = {
-  reply: (value: string, opts?: { usage?: Usage }) => Promise<void>
-  tool: (name: string, input: unknown) => Promise<void>
-  toolHang: (name: string, input: unknown) => Promise<void>
-  reason: (value: string, opts?: { text?: string; usage?: Usage }) => Promise<void>
-  fail: (message?: unknown) => Promise<void>
-  error: (status: number, body: unknown) => Promise<void>
-  hang: () => Promise<void>
-  hold: (value: string, wait: PromiseLike<unknown>) => Promise<void>
-  calls: () => Promise<number>
-  pending: () => Promise<number>
-}
-
 export const settingsKey = "settings.v3"
 
 const seedModel = (() => {
@@ -143,13 +129,9 @@ type ProjectFixture = ProjectHandle & {
 
 type TestFixtures = {
   llm: LLMFixture
-  assistant: AssistantFixture
   project: ProjectFixture
   sdk: ReturnType<typeof createSdk>
   gotoSession: (sessionID?: string) => Promise<void>
-  withProject: <T>(callback: (project: ProjectHandle) => Promise<T>, options?: ProjectOptions) => Promise<T>
-  withBackendProject: <T>(callback: (project: ProjectHandle) => Promise<T>, options?: ProjectOptions) => Promise<T>
-  withMockProject: <T>(callback: (project: ProjectHandle) => Promise<T>, options?: ProjectOptions) => Promise<T>
 }
 
 type WorkerFixtures = {
@@ -238,20 +220,6 @@ export const test = base.extend<TestFixtures, WorkerFixtures>({
       throw new Error(`TestLLMServer still has ${pending} queued response(s) after the test finished`)
     }
   },
-  assistant: async ({ llm }, use) => {
-    await use({
-      reply: llm.text,
-      tool: llm.tool,
-      toolHang: llm.toolHang,
-      reason: llm.reason,
-      fail: llm.fail,
-      error: llm.error,
-      hang: llm.hang,
-      hold: llm.hold,
-      calls: llm.calls,
-      pending: llm.pending,
-    })
-  },
   page: async ({ page }, use) => {
     let boundary: string | undefined
     setHealthPhase(page, "test")
@@ -312,29 +280,6 @@ export const test = base.extend<TestFixtures, WorkerFixtures>({
       await item.cleanup()
     }
   },
-  withProject: async ({ page }, use) => {
-    await use((callback, options) => runProject(page, callback, options))
-  },
-  withBackendProject: async ({ page, backend }, use) => {
-    await use((callback, options) =>
-      runProject(page, callback, { ...options, serverUrl: backend.url, sdk: backend.sdk }),
-    )
-  },
-  withMockProject: async ({ page, llm, backend }, use) => {
-    await use((callback, options) =>
-      withMockOpenAI({
-        serverUrl: backend.url,
-        llmUrl: llm.url,
-        fn: () =>
-          runProject(page, callback, {
-            ...options,
-            model: options?.model ?? openaiModel,
-            serverUrl: backend.url,
-            sdk: backend.sdk,
-          }),
-      }),
-    )
-  },
 })
 
 function makeProject(
@@ -560,63 +505,6 @@ function makeProject(
   }
 }
 
-async function runProject<T>(
-  page: Page,
-  callback: (project: ProjectHandle) => Promise<T>,
-  options?: ProjectOptions & {
-    serverUrl?: string
-    sdk?: (directory?: string) => ReturnType<typeof createSdk>
-  },
-) {
-  const url = options?.serverUrl
-  const root = await createTestProject(url ? { serverUrl: url } : undefined)
-  const sdk = options?.sdk?.(root) ?? createSdk(root, url)
-  const sessions = new Map<string, string>()
-  const dirs = new Set<string>()
-  await options?.setup?.(root)
-  await seedStorage(page, {
-    directory: root,
-    extra: options?.extra,
-    model: options?.model,
-    serverUrl: url,
-  })
-
-  const gotoSession = async (sessionID?: string) => {
-    await visit(page, sessionPath(root, sessionID))
-    await waitSession(page, {
-      directory: root,
-      sessionID,
-      serverUrl: url,
-      allowAnySession: !sessionID,
-    })
-    const current = sessionIDFromUrl(page.url())
-    if (current) trackSession(current)
-  }
-
-  const trackSession = (sessionID: string, directory?: string) => {
-    sessions.set(sessionID, directory ?? root)
-  }
-
-  const trackDirectory = (directory: string) => {
-    if (directory !== root) dirs.add(directory)
-  }
-
-  try {
-    await options?.beforeGoto?.({ directory: root, sdk })
-    await gotoSession()
-    const slug = await waitSlug(page)
-    return await callback({ directory: root, slug, gotoSession, trackSession, trackDirectory, sdk })
-  } finally {
-    setHealthPhase(page, "cleanup")
-    await Promise.allSettled(
-      Array.from(sessions, ([sessionID, directory]) => cleanupSession({ sessionID, directory, serverUrl: url })),
-    )
-    await Promise.allSettled(Array.from(dirs, (directory) => cleanupTestProject(directory)))
-    await cleanupTestProject(root)
-    setHealthPhase(page, "test")
-  }
-}
-
 async function seedStorage(
   page: Page,
   input: {