summaryrefslogtreecommitdiffhomepage
path: root/packages/app/e2e/prompt
diff options
context:
space:
mode:
authorKit Langton <[email protected]>2026-04-01 13:15:42 -0400
committerGitHub <[email protected]>2026-04-01 13:15:42 -0400
commitc559af51ced7c47ccb55ece0de1bfee37a74e552 (patch)
treedb85d2ccf556b9266ac4f1a24c47f3e5e9795f70 /packages/app/e2e/prompt
parentd1e0a4640c5d10a689f242ad6a811b8c8e7a5fe8 (diff)
downloadopencode-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.ts3
-rw-r--r--packages/app/e2e/prompt/prompt-history.spec.ts249
-rw-r--r--packages/app/e2e/prompt/prompt-shell.spec.ts4
-rw-r--r--packages/app/e2e/prompt/prompt-slash-share.spec.ts64
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()
+ })
})
})