diff options
| author | Kit Langton <[email protected]> | 2026-04-01 13:15:42 -0400 |
|---|---|---|
| committer | GitHub <[email protected]> | 2026-04-01 13:15:42 -0400 |
| commit | c559af51ced7c47ccb55ece0de1bfee37a74e552 (patch) | |
| tree | db85d2ccf556b9266ac4f1a24c47f3e5e9795f70 /packages/app/e2e/prompt | |
| parent | d1e0a4640c5d10a689f242ad6a811b8c8e7a5fe8 (diff) | |
| download | opencode-c559af51ced7c47ccb55ece0de1bfee37a74e552.tar.gz opencode-c559af51ced7c47ccb55ece0de1bfee37a74e552.zip | |
test(app): migrate more e2e suites to isolated backend (#20505)
Diffstat (limited to 'packages/app/e2e/prompt')
| -rw-r--r-- | packages/app/e2e/prompt/prompt-async.spec.ts | 3 | ||||
| -rw-r--r-- | packages/app/e2e/prompt/prompt-history.spec.ts | 249 | ||||
| -rw-r--r-- | packages/app/e2e/prompt/prompt-shell.spec.ts | 4 | ||||
| -rw-r--r-- | packages/app/e2e/prompt/prompt-slash-share.spec.ts | 64 |
4 files changed, 178 insertions, 142 deletions
diff --git a/packages/app/e2e/prompt/prompt-async.spec.ts b/packages/app/e2e/prompt/prompt-async.spec.ts index 97c15e44c..a9a12cb95 100644 --- a/packages/app/e2e/prompt/prompt-async.spec.ts +++ b/packages/app/e2e/prompt/prompt-async.spec.ts @@ -1,7 +1,7 @@ import { test, expect } from "../fixtures" import { promptSelector } from "../selectors" import { assistantText, sessionIDFromUrl, withSession } from "../actions" -import { openaiModel, promptMatch, withMockOpenAI } from "./mock" +import { openaiModel, promptMatch, titleMatch, withMockOpenAI } from "./mock" const text = (value: string | null) => (value ?? "").replace(/\u200B/g, "").trim() @@ -24,6 +24,7 @@ test("prompt succeeds when sync message endpoint is unreachable", async ({ llmUrl: llm.url, fn: async () => { const token = `E2E_ASYNC_${Date.now()}` + await llm.textMatch(titleMatch, "E2E Title") await llm.textMatch(promptMatch(token), token) await withBackendProject( diff --git a/packages/app/e2e/prompt/prompt-history.spec.ts b/packages/app/e2e/prompt/prompt-history.spec.ts index 1c9c07955..6420534e0 100644 --- a/packages/app/e2e/prompt/prompt-history.spec.ts +++ b/packages/app/e2e/prompt/prompt-history.spec.ts @@ -1,8 +1,9 @@ import type { ToolPart } from "@opencode-ai/sdk/v2/client" import type { Page } from "@playwright/test" import { test, expect } from "../fixtures" -import { withSession } from "../actions" +import { assistantText, sessionIDFromUrl } from "../actions" import { promptSelector } from "../selectors" +import { openaiModel, promptMatch, titleMatch, withMockOpenAI } from "./mock" const text = (value: string | null) => (value ?? "").replace(/\u200B/g, "").trim() @@ -43,20 +44,13 @@ async function wait(page: Page, value: string) { await expect.poll(async () => text(await page.locator(promptSelector).textContent())).toBe(value) } -async function reply(sdk: Parameters<typeof withSession>[0], sessionID: string, token: string) { +async function reply( + sdk: { session: { messages: Parameters<typeof assistantText>[0]["session"] } }, + sessionID: string, + token: string, +) { await expect - .poll( - async () => { - const messages = await sdk.session.messages({ sessionID, limit: 50 }).then((r) => r.data ?? []) - return messages - .filter((item) => item.info.role === "assistant") - .flatMap((item) => item.parts) - .filter((item) => item.type === "text") - .map((item) => item.text) - .join("\n") - }, - { timeout: 90_000 }, - ) + .poll(() => assistantText(sdk as Parameters<typeof assistantText>[0], sessionID), { timeout: 90_000 }) .toContain(token) } @@ -79,106 +73,145 @@ async function shell(sdk: Parameters<typeof withSession>[0], sessionID: string, .toContain(token) } -test("prompt history restores unsent draft with arrow navigation", async ({ page, sdk, gotoSession }) => { +test("prompt history restores unsent draft with arrow navigation", async ({ + page, + llm, + backend, + withBackendProject, +}) => { test.setTimeout(120_000) - await withSession(sdk, `e2e prompt history ${Date.now()}`, async (session) => { - await gotoSession(session.id) - - const prompt = page.locator(promptSelector) - const firstToken = `E2E_HISTORY_ONE_${Date.now()}` - const secondToken = `E2E_HISTORY_TWO_${Date.now()}` - const first = `Reply with exactly: ${firstToken}` - const second = `Reply with exactly: ${secondToken}` - const draft = `draft ${Date.now()}` - - await prompt.click() - await page.keyboard.type(first) - await page.keyboard.press("Enter") - await wait(page, "") - await reply(sdk, session.id, firstToken) - - await prompt.click() - await page.keyboard.type(second) - await page.keyboard.press("Enter") - await wait(page, "") - await reply(sdk, session.id, secondToken) - - await prompt.click() - await page.keyboard.type(draft) - await wait(page, draft) - - // Clear the draft before navigating history (ArrowUp only works when prompt is empty) - await prompt.fill("") - await wait(page, "") - - await page.keyboard.press("ArrowUp") - await wait(page, second) - - await page.keyboard.press("ArrowUp") - await wait(page, first) - - await page.keyboard.press("ArrowDown") - await wait(page, second) - - await page.keyboard.press("ArrowDown") - await wait(page, "") + await withMockOpenAI({ + serverUrl: backend.url, + llmUrl: llm.url, + fn: async () => { + const firstToken = `E2E_HISTORY_ONE_${Date.now()}` + const secondToken = `E2E_HISTORY_TWO_${Date.now()}` + const first = `Reply with exactly: ${firstToken}` + const second = `Reply with exactly: ${secondToken}` + const draft = `draft ${Date.now()}` + + await llm.textMatch(titleMatch, "E2E Title") + await llm.textMatch(promptMatch(firstToken), firstToken) + await llm.textMatch(promptMatch(secondToken), secondToken) + + await withBackendProject( + async (project) => { + const prompt = page.locator(promptSelector) + + await prompt.click() + await page.keyboard.type(first) + await page.keyboard.press("Enter") + await wait(page, "") + + await expect(page).toHaveURL(/\/session\/[^/?#]+/, { timeout: 30_000 }) + const sessionID = sessionIDFromUrl(page.url())! + project.trackSession(sessionID) + await reply(project.sdk, sessionID, firstToken) + + await prompt.click() + await page.keyboard.type(second) + await page.keyboard.press("Enter") + await wait(page, "") + await reply(project.sdk, sessionID, secondToken) + + await prompt.click() + await page.keyboard.type(draft) + await wait(page, draft) + + await prompt.fill("") + await wait(page, "") + + await page.keyboard.press("ArrowUp") + await wait(page, second) + + await page.keyboard.press("ArrowUp") + await wait(page, first) + + await page.keyboard.press("ArrowDown") + await wait(page, second) + + await page.keyboard.press("ArrowDown") + await wait(page, "") + }, + { + model: openaiModel, + }, + ) + }, }) }) -test("shell history stays separate from normal prompt history", async ({ page, sdk, gotoSession }) => { +test("shell history stays separate from normal prompt history", async ({ page, llm, backend, withBackendProject }) => { test.setTimeout(120_000) - await withSession(sdk, `e2e shell history ${Date.now()}`, async (session) => { - await gotoSession(session.id) - - const prompt = page.locator(promptSelector) - const firstToken = `E2E_SHELL_ONE_${Date.now()}` - const secondToken = `E2E_SHELL_TWO_${Date.now()}` - const normalToken = `E2E_NORMAL_${Date.now()}` - const first = `echo ${firstToken}` - const second = `echo ${secondToken}` - const normal = `Reply with exactly: ${normalToken}` - - await prompt.click() - await page.keyboard.type("!") - await page.keyboard.type(first) - await page.keyboard.press("Enter") - await wait(page, "") - await shell(sdk, session.id, first, firstToken) - - await prompt.click() - await page.keyboard.type("!") - await page.keyboard.type(second) - await page.keyboard.press("Enter") - await wait(page, "") - await shell(sdk, session.id, second, secondToken) - - await prompt.click() - await page.keyboard.type("!") - await page.keyboard.press("ArrowUp") - await wait(page, second) - - await page.keyboard.press("ArrowUp") - await wait(page, first) - - await page.keyboard.press("ArrowDown") - await wait(page, second) - - await page.keyboard.press("ArrowDown") - await wait(page, "") - - await page.keyboard.press("Escape") - await wait(page, "") - - await prompt.click() - await page.keyboard.type(normal) - await page.keyboard.press("Enter") - await wait(page, "") - await reply(sdk, session.id, normalToken) - - await prompt.click() - await page.keyboard.press("ArrowUp") - await wait(page, normal) + await withMockOpenAI({ + serverUrl: backend.url, + llmUrl: llm.url, + fn: async () => { + const firstToken = `E2E_SHELL_ONE_${Date.now()}` + const secondToken = `E2E_SHELL_TWO_${Date.now()}` + const normalToken = `E2E_NORMAL_${Date.now()}` + const first = `echo ${firstToken}` + const second = `echo ${secondToken}` + const normal = `Reply with exactly: ${normalToken}` + + await llm.textMatch(titleMatch, "E2E Title") + await llm.textMatch(promptMatch(normalToken), normalToken) + + await withBackendProject( + async (project) => { + const prompt = page.locator(promptSelector) + + await prompt.click() + await page.keyboard.type("!") + await page.keyboard.type(first) + await page.keyboard.press("Enter") + await wait(page, "") + + await expect(page).toHaveURL(/\/session\/[^/?#]+/, { timeout: 30_000 }) + const sessionID = sessionIDFromUrl(page.url())! + project.trackSession(sessionID) + await shell(project.sdk, sessionID, first, firstToken) + + await prompt.click() + await page.keyboard.type("!") + await page.keyboard.type(second) + await page.keyboard.press("Enter") + await wait(page, "") + await shell(project.sdk, sessionID, second, secondToken) + + await prompt.click() + await page.keyboard.type("!") + await page.keyboard.press("ArrowUp") + await wait(page, second) + + await page.keyboard.press("ArrowUp") + await wait(page, first) + + await page.keyboard.press("ArrowDown") + await wait(page, second) + + await page.keyboard.press("ArrowDown") + await wait(page, "") + + await page.keyboard.press("Escape") + await wait(page, "") + + await prompt.click() + await page.keyboard.type(normal) + await page.keyboard.press("Enter") + await wait(page, "") + await reply(project.sdk, sessionID, normalToken) + + await prompt.click() + await page.keyboard.press("ArrowUp") + await wait(page, normal) + }, + { + model: openaiModel, + }, + ) + }, }) }) diff --git a/packages/app/e2e/prompt/prompt-shell.spec.ts b/packages/app/e2e/prompt/prompt-shell.spec.ts index 019219bf5..7c39a2db3 100644 --- a/packages/app/e2e/prompt/prompt-shell.spec.ts +++ b/packages/app/e2e/prompt/prompt-shell.spec.ts @@ -10,10 +10,10 @@ const isBash = (part: unknown): part is ToolPart => { return "state" in part } -test("shell mode runs a command in the project directory", async ({ page, withProject }) => { +test("shell mode runs a command in the project directory", async ({ page, withBackendProject }) => { test.setTimeout(120_000) - await withProject(async ({ directory, gotoSession, trackSession, sdk }) => { + await withBackendProject(async ({ directory, gotoSession, trackSession, sdk }) => { const prompt = page.locator(promptSelector) const cmd = process.platform === "win32" ? "dir" : "command ls" diff --git a/packages/app/e2e/prompt/prompt-slash-share.spec.ts b/packages/app/e2e/prompt/prompt-slash-share.spec.ts index 817b353a7..efb0272b5 100644 --- a/packages/app/e2e/prompt/prompt-slash-share.spec.ts +++ b/packages/app/e2e/prompt/prompt-slash-share.spec.ts @@ -22,43 +22,45 @@ async function seed(sdk: Parameters<typeof withSession>[0], sessionID: string) { .toBeGreaterThan(0) } -test("/share and /unshare update session share state", async ({ page, sdk, gotoSession }) => { +test("/share and /unshare update session share state", async ({ page, withBackendProject }) => { test.skip(shareDisabled, "Share is disabled in this environment (OPENCODE_DISABLE_SHARE).") - await withSession(sdk, `e2e slash share ${Date.now()}`, async (session) => { - const prompt = page.locator(promptSelector) + await withBackendProject(async (project) => { + await withSession(project.sdk, `e2e slash share ${Date.now()}`, async (session) => { + const prompt = page.locator(promptSelector) - await seed(sdk, session.id) - await gotoSession(session.id) + await seed(project.sdk, session.id) + await project.gotoSession(session.id) - await prompt.click() - await page.keyboard.type("/share") - await expect(page.locator('[data-slash-id="session.share"]').first()).toBeVisible() - await page.keyboard.press("Enter") + await prompt.click() + await page.keyboard.type("/share") + await expect(page.locator('[data-slash-id="session.share"]').first()).toBeVisible() + await page.keyboard.press("Enter") - await expect - .poll( - async () => { - const data = await sdk.session.get({ sessionID: session.id }).then((r) => r.data) - return data?.share?.url || undefined - }, - { timeout: 30_000 }, - ) - .not.toBeUndefined() + await expect + .poll( + async () => { + const data = await project.sdk.session.get({ sessionID: session.id }).then((r) => r.data) + return data?.share?.url || undefined + }, + { timeout: 30_000 }, + ) + .not.toBeUndefined() - await prompt.click() - await page.keyboard.type("/unshare") - await expect(page.locator('[data-slash-id="session.unshare"]').first()).toBeVisible() - await page.keyboard.press("Enter") + await prompt.click() + await page.keyboard.type("/unshare") + await expect(page.locator('[data-slash-id="session.unshare"]').first()).toBeVisible() + await page.keyboard.press("Enter") - await expect - .poll( - async () => { - const data = await sdk.session.get({ sessionID: session.id }).then((r) => r.data) - return data?.share?.url || undefined - }, - { timeout: 30_000 }, - ) - .toBeUndefined() + await expect + .poll( + async () => { + const data = await project.sdk.session.get({ sessionID: session.id }).then((r) => r.data) + return data?.share?.url || undefined + }, + { timeout: 30_000 }, + ) + .toBeUndefined() + }) }) }) |
