mirror of
https://fastgit.cc/https://github.com/anomalyco/opencode
synced 2026-04-20 21:00:29 +08:00
147 lines
4.5 KiB
TypeScript
147 lines
4.5 KiB
TypeScript
import type { ToolPart } from "@opencode-ai/sdk/v2/client"
|
|
import type { Page } from "@playwright/test"
|
|
import { test, expect } from "../fixtures"
|
|
import { assistantText } from "../actions"
|
|
import { promptSelector } from "../selectors"
|
|
import { createSdk } from "../utils"
|
|
|
|
const text = (value: string | null) => (value ?? "").replace(/\u200B/g, "").trim()
|
|
type Sdk = ReturnType<typeof createSdk>
|
|
|
|
const isBash = (part: unknown): part is ToolPart => {
|
|
if (!part || typeof part !== "object") return false
|
|
if (!("type" in part) || part.type !== "tool") return false
|
|
if (!("tool" in part) || part.tool !== "bash") return false
|
|
return "state" in part
|
|
}
|
|
|
|
async function wait(page: Page, value: string) {
|
|
await expect.poll(async () => text(await page.locator(promptSelector).textContent())).toBe(value)
|
|
}
|
|
|
|
async function reply(sdk: Sdk, sessionID: string, token: string) {
|
|
await expect.poll(() => assistantText(sdk, sessionID), { timeout: 90_000 }).toContain(token)
|
|
}
|
|
|
|
async function shell(sdk: Sdk, sessionID: string, cmd: string, token: string) {
|
|
await expect
|
|
.poll(
|
|
async () => {
|
|
const messages = await sdk.session.messages({ sessionID, limit: 50 }).then((r) => r.data ?? [])
|
|
const part = messages
|
|
.filter((item) => item.info.role === "assistant")
|
|
.flatMap((item) => item.parts)
|
|
.filter(isBash)
|
|
.find((item) => item.state.input?.command === cmd && item.state.status === "completed")
|
|
|
|
if (!part || part.state.status !== "completed") return
|
|
return typeof part.state.metadata?.output === "string" ? part.state.metadata.output : part.state.output
|
|
},
|
|
{ timeout: 90_000 },
|
|
)
|
|
.toContain(token)
|
|
}
|
|
|
|
test("prompt history restores unsent draft with arrow navigation", async ({ page, project, assistant }) => {
|
|
test.setTimeout(120_000)
|
|
|
|
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 project.open()
|
|
await assistant.reply(firstToken)
|
|
const sessionID = await project.prompt(first)
|
|
await wait(page, "")
|
|
await reply(project.sdk, sessionID, firstToken)
|
|
|
|
await assistant.reply(secondToken)
|
|
await project.prompt(second)
|
|
await wait(page, "")
|
|
await reply(project.sdk, sessionID, secondToken)
|
|
|
|
const prompt = page.locator(promptSelector)
|
|
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, "")
|
|
})
|
|
|
|
test.fixme("shell history stays separate from normal prompt history", async ({ page, sdk, gotoSession }) => {
|
|
test.setTimeout(120_000)
|
|
|
|
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 gotoSession()
|
|
|
|
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())!
|
|
await shell(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(sdk, sessionID, second, secondToken)
|
|
|
|
await page.keyboard.press("Escape")
|
|
await wait(page, "")
|
|
|
|
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, sessionID, normalToken)
|
|
|
|
await prompt.click()
|
|
await page.keyboard.press("ArrowUp")
|
|
await wait(page, normal)
|
|
})
|