prompt-async.spec.ts 2.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354
  1. import { test, expect } from "../fixtures"
  2. import { promptSelector } from "../selectors"
  3. import { assistantText, withSession } from "../actions"
  4. const text = (value: string | null) => (value ?? "").replace(/\u200B/g, "").trim()
  5. // Regression test for Issue #12453: the synchronous POST /message endpoint holds
  6. // the connection open while the agent works, causing "Failed to fetch" over
  7. // VPN/Tailscale. The fix switches to POST /prompt_async which returns immediately.
  8. test("prompt succeeds when sync message endpoint is unreachable", async ({ page, project, assistant }) => {
  9. test.setTimeout(120_000)
  10. // Simulate Tailscale/VPN killing the long-lived sync connection
  11. await page.route("**/session/*/message", (route) => route.abort("connectionfailed"))
  12. const token = `E2E_ASYNC_${Date.now()}`
  13. await project.open()
  14. await assistant.reply(token)
  15. const sessionID = await project.prompt(`Reply with exactly: ${token}`)
  16. await expect.poll(() => assistant.calls()).toBeGreaterThanOrEqual(1)
  17. await expect.poll(() => assistantText(project.sdk, sessionID), { timeout: 90_000 }).toContain(token)
  18. })
  19. test("failed prompt send restores the composer input", async ({ page, sdk, gotoSession }) => {
  20. await withSession(sdk, `e2e prompt failure ${Date.now()}`, async (session) => {
  21. const prompt = page.locator(promptSelector)
  22. const value = `restore ${Date.now()}`
  23. await page.route(`**/session/${session.id}/prompt_async`, (route) =>
  24. route.fulfill({
  25. status: 500,
  26. contentType: "application/json",
  27. body: JSON.stringify({ message: "e2e prompt failure" }),
  28. }),
  29. )
  30. await gotoSession(session.id)
  31. await prompt.click()
  32. await page.keyboard.type(value)
  33. await page.keyboard.press("Enter")
  34. await expect.poll(async () => text(await prompt.textContent())).toBe(value)
  35. await expect
  36. .poll(
  37. async () => {
  38. const messages = await sdk.session.messages({ sessionID: session.id, limit: 50 }).then((r) => r.data ?? [])
  39. return messages.length
  40. },
  41. { timeout: 15_000 },
  42. )
  43. .toBe(0)
  44. })
  45. })