|
1 | 1 | import { base64Encode } from "@opencode-ai/core/util/encode" |
2 | 2 | import { expect, test, type Page } from "@playwright/test" |
3 | 3 | import { mockOpenCodeServer } from "../utils/mock-server" |
| 4 | +import { installSseTransport } from "../utils/sse-transport" |
4 | 5 | import { expectSessionTitle } from "../utils/waits" |
5 | 6 |
|
6 | 7 | const directory = "C:/OpenCode/RequestDocks" |
@@ -100,6 +101,67 @@ test("shows a pending permission dock", async ({ page }) => { |
100 | 101 | expect(request.postDataJSON()).toEqual({ response: "once" }) |
101 | 102 | }) |
102 | 103 |
|
| 104 | +test("restores the draft caret before typing after a request dock closes", async ({ page }) => { |
| 105 | + const transport = await installSseTransport(page, { |
| 106 | + server: `http://${process.env.PLAYWRIGHT_SERVER_HOST ?? "127.0.0.1"}:${process.env.PLAYWRIGHT_SERVER_PORT ?? "4096"}`, |
| 107 | + retry: 20, |
| 108 | + }) |
| 109 | + await mockServer(page, { questions: [] }) |
| 110 | + await page.goto(`/${base64Encode(directory)}/session/${sessionID}`) |
| 111 | + await transport.waitForConnection() |
| 112 | + await expectSessionTitle(page, title) |
| 113 | + |
| 114 | + const editor = page.locator('[data-component="prompt-input"][contenteditable="true"]') |
| 115 | + const draft = "keep the caret at the end" |
| 116 | + await editor.fill(draft) |
| 117 | + await page.evaluate(() => new Promise<void>((resolve) => requestAnimationFrame(() => resolve()))) |
| 118 | + for (let index = 0; index < 4; index++) await page.keyboard.press("ArrowLeft") |
| 119 | + const cursor = draft.length - 4 |
| 120 | + await expect |
| 121 | + .poll(() => |
| 122 | + editor.evaluate((element) => { |
| 123 | + const selection = window.getSelection() |
| 124 | + if (!selection?.rangeCount || !element.contains(selection.anchorNode)) return -1 |
| 125 | + const range = selection.getRangeAt(0).cloneRange() |
| 126 | + range.selectNodeContents(element) |
| 127 | + range.setEnd(selection.anchorNode!, selection.anchorOffset) |
| 128 | + return range.toString().length |
| 129 | + }), |
| 130 | + ) |
| 131 | + .toBe(cursor) |
| 132 | + await transport.send({ |
| 133 | + directory, |
| 134 | + payload: { |
| 135 | + type: "question.asked", |
| 136 | + properties: { |
| 137 | + id: "question-caret", |
| 138 | + sessionID, |
| 139 | + questions: [ |
| 140 | + { |
| 141 | + header: "Continue", |
| 142 | + question: "Continue?", |
| 143 | + options: [{ label: "Yes", description: "Continue the session" }], |
| 144 | + }, |
| 145 | + ], |
| 146 | + tool: { messageID: "message-caret", callID: "call-caret" }, |
| 147 | + }, |
| 148 | + }, |
| 149 | + }) |
| 150 | + const question = page.locator('[data-component="dock-prompt"][data-kind="question"]') |
| 151 | + await expect(question).toBeVisible() |
| 152 | + await expect(editor).toHaveCount(0) |
| 153 | + |
| 154 | + await transport.send({ |
| 155 | + directory, |
| 156 | + payload: { type: "question.rejected", properties: { sessionID, requestID: "question-caret" } }, |
| 157 | + }) |
| 158 | + await expect(question).toHaveCount(0) |
| 159 | + await expect(editor).toBeVisible() |
| 160 | + await page.keyboard.press("x") |
| 161 | + |
| 162 | + await expect(editor).toHaveText(`${draft.slice(0, cursor)}x${draft.slice(cursor)}`) |
| 163 | +}) |
| 164 | + |
103 | 165 | async function mockServer( |
104 | 166 | page: Page, |
105 | 167 | requests: { |
|
0 commit comments