Skip to content

Commit 14c3573

Browse files
committed
refactor(app): share prompt dispatch and simplify quote tests
1 parent b7529a4 commit 14c3573

3 files changed

Lines changed: 159 additions & 168 deletions

File tree

‎packages/app/e2e/regression/timeline-stream.spec.ts‎

Lines changed: 85 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -2112,109 +2112,109 @@ test("quotes plain timeline text into the composer, with or without a comment",
21122112
await expect(chips).toHaveCount(0)
21132113
})
21142114

2115-
for (const scenario of [
2116-
{ working: false, behavior: "queue" },
2117-
{ working: false, behavior: "steer" },
2118-
{ working: true, behavior: "queue" },
2119-
{ working: true, behavior: "steer" },
2120-
] as const) {
2121-
test(`quote popup sends independently: ${scenario.working ? "working" : "idle"}, ${scenario.behavior} default`, async ({
2122-
page,
2123-
}) => {
2124-
const sent: string[] = []
2125-
2126-
await setupTimeline(page, {
2127-
settings: { followUpBehavior: scenario.behavior },
2128-
sessionStatus: { [sessionID]: { type: scenario.working ? "busy" : "idle" } },
2129-
messages: [userMessage(), assistantMessage([textPart("prt_direct_quote", "Keep this quoted passage.")])],
2130-
findFiles: () => ["src/api.ts"],
2131-
onPrompt: (input) => {
2132-
sent.push(String(input.body.delivery))
2133-
},
2134-
})
2135-
2136-
const answer = page.locator(
2137-
`[data-timeline-part-id="${renderedPartID("prt_direct_quote")}"] [data-slot="text-part-body"]`,
2138-
)
2115+
test("quote popup sends independently without consuming the draft or other quotes", async ({ page }) => {
2116+
const sent: string[] = []
21392117

2140-
await expect(answer).toHaveText("Keep this quoted passage.")
2141-
const editor = page.locator('[data-component="composer-editor"]')
2142-
2143-
await editor.fill("Keep my separate draft")
2118+
await setupTimeline(page, {
2119+
messages: [userMessage(), assistantMessage([textPart("prt_direct_quote", "Keep this quoted passage.")])],
2120+
onPrompt: (input) => void sent.push(String(input.body.delivery)),
2121+
})
21442122

2145-
const select = () =>
2146-
answer.evaluate((element) => {
2147-
const range = document.createRange()
2123+
const answer = page.locator(
2124+
`[data-timeline-part-id="${renderedPartID("prt_direct_quote")}"] [data-slot="text-part-body"]`,
2125+
)
21482126

2149-
range.selectNodeContents(element)
2150-
window.getSelection()?.removeAllRanges()
2151-
window.getSelection()?.addRange(range)
2152-
})
2127+
const editor = page.locator('[data-component="composer-editor"]')
21532128

2154-
const quote = page
2155-
.getByRole("toolbar", { name: "Quote selection" })
2156-
.getByRole("button", { name: "Quote", exact: true })
2129+
const quote = page
2130+
.getByRole("toolbar", { name: "Quote selection" })
2131+
.getByRole("button", { name: "Quote", exact: true })
21572132

2158-
await select()
2159-
await quote.click({ modifiers: ["Alt"] })
2160-
await select()
2161-
await quote.click()
2162-
const popup = page.locator('[data-component="composer-quote-comment"]')
2133+
await expect(answer).toHaveText("Keep this quoted passage.")
2134+
await editor.fill("Keep my separate draft")
2135+
await selectQuoteText(answer)
2136+
await quote.click({ modifiers: ["Alt"] })
2137+
await expect(editor).toBeFocused()
2138+
await selectQuoteText(answer)
2139+
await quote.click()
2140+
const popup = page.locator('[data-component="composer-quote-comment"]')
21632141

2164-
const comment = popup.locator("textarea")
2142+
const comment = popup.locator("textarea")
21652143

2166-
const action = popup.locator('[data-slot="line-comment-v2-submit"]')
2144+
const action = popup.getByRole("button")
21672145

2168-
const modifier = await page.evaluate(() => (/(Mac|iPod|iPhone|iPad)/.test(navigator.platform) ? "Meta" : "Control"))
2146+
const modifier = await page.evaluate(() => (/(Mac|iPod|iPhone|iPad)/.test(navigator.platform) ? "Meta" : "Control"))
21692147

2170-
const delivery = !scenario.working || scenario.behavior === "queue" ? "steer" : "queue"
2148+
await expect(comment).toBeFocused()
2149+
await comment.fill("Explain this please")
2150+
await action.focus()
2151+
await page.keyboard.down(modifier)
2152+
await expect(action).toHaveAccessibleName("Send quote")
2153+
await page.keyboard.press("Enter")
2154+
await page.keyboard.up(modifier)
2155+
await expect(popup).toHaveCount(0)
2156+
await expect.poll(() => sent).toEqual(["steer"])
2157+
await expect(editor).toHaveText("Keep my separate draft")
2158+
const remaining = page.locator('[data-component="composer-attachments"] [data-component="attachment-card"]')
21712159

2172-
const actionLabel = !scenario.working ? "Send quote" : delivery === "queue" ? "Queue quote" : "Steer with quote"
2160+
await expect(remaining).toHaveCount(1)
2161+
await expect(remaining).toContainText("Keep this quoted passage.")
2162+
await expect(remaining).not.toContainText("Explain this please")
2163+
})
21732164

2174-
const mentioning = scenario.working && scenario.behavior === "steer"
2165+
test("a pending quote send hides mentions and keeps the comment read-only", async ({ page }) => {
2166+
const sent: string[] = []
21752167

2176-
await expect(comment).toBeFocused()
2177-
await comment.fill(mentioning ? "Explain @src/a" : "Explain this please")
2168+
await setupTimeline(page, {
2169+
settings: { followUpBehavior: "steer" },
2170+
sessionStatus: { [sessionID]: { type: "busy" } },
2171+
messages: [userMessage(), assistantMessage([textPart("prt_quote_mentions", "Keep this quoted passage.")])],
2172+
findFiles: () => ["src/api.ts"],
2173+
onPrompt: (input) => void sent.push(String(input.body.delivery)),
2174+
})
21782175

2179-
if (mentioning) await expect(popup.locator('[data-slot="line-comment-v2-mention-item"]')).toHaveCount(1)
2180-
await expect(action).toHaveCount(1)
2181-
await action.hover()
2182-
await expect(page.getByRole("tooltip").filter({ hasText: actionLabel })).toBeVisible()
2183-
const bounds = await action.boundingBox()
2176+
const answer = page.locator(
2177+
`[data-timeline-part-id="${renderedPartID("prt_quote_mentions")}"] [data-slot="text-part-body"]`,
2178+
)
21842179

2185-
await page.keyboard.down(modifier)
2186-
await expect(action).toHaveAccessibleName(actionLabel)
2187-
expect(await action.boundingBox()).toEqual(bounds)
2188-
await page.keyboard.up(modifier)
2189-
await expect(action).toHaveAccessibleName("Add comment")
2180+
await expect(answer).toHaveText("Keep this quoted passage.")
2181+
await expect(page.locator('[data-component="composer-editor"]')).toBeFocused()
2182+
await selectQuoteText(answer)
2183+
await page
2184+
.getByRole("toolbar", { name: "Quote selection" })
2185+
.getByRole("button", { name: "Quote", exact: true })
2186+
.click()
2187+
const popup = page.locator('[data-component="composer-quote-comment"]')
21902188

2191-
const admission = await holdRoute(page, new RegExp(`/api/session/${sessionID}/prompt(\\?|$)`), {
2192-
method: "POST",
2193-
})
2189+
const comment = popup.locator("textarea")
21942190

2195-
if (mentioning) await action.click({ modifiers: [modifier] })
2191+
const modifier = await page.evaluate(() => (/(Mac|iPod|iPhone|iPad)/.test(navigator.platform) ? "Meta" : "Control"))
21962192

2197-
if (!mentioning)
2198-
await (!scenario.working && scenario.behavior === "queue" ? action : comment).press(`${modifier}+Enter`)
2199-
await admission.arrived
2200-
await expect(action).toBeDisabled()
2201-
await expect(comment).toHaveAttribute("readonly", "")
2202-
await expect(popup.locator('[data-component="loader-v2"]')).toBeVisible()
2193+
await comment.fill("Explain @src/a")
2194+
await expect(popup.locator('[data-slot="line-comment-v2-mention-item"]')).toHaveCount(1)
2195+
const admission = await holdRoute(page, new RegExp(`/api/session/${sessionID}/prompt(\\?|$)`), { method: "POST" })
2196+
2197+
await popup.locator('[data-slot="line-comment-v2-submit"]').click({ modifiers: [modifier] })
2198+
await admission.arrived
2199+
await expect(popup.getByRole("button")).toBeDisabled()
2200+
await expect(comment).toHaveAttribute("readonly", "")
2201+
await expect(popup.locator('[data-component="loader-v2"]')).toBeVisible()
2202+
await expect(popup.locator('[data-slot="line-comment-v2-mention-item"]')).toHaveCount(0)
2203+
await comment.press("Tab")
2204+
await expect(comment).toHaveValue("Explain @src/a")
2205+
expect(admission.requests).toHaveLength(1)
2206+
admission.release()
2207+
await expect(popup).toHaveCount(0)
2208+
await expect.poll(() => sent).toEqual(["queue"])
2209+
})
22032210

2204-
if (mentioning) {
2205-
await expect(popup.locator('[data-slot="line-comment-v2-mention-item"]')).toHaveCount(0)
2206-
await comment.press("Tab")
2207-
await expect(comment).toHaveValue("Explain @src/a")
2208-
}
2211+
function selectQuoteText(target: Locator) {
2212+
return target.evaluate((element) => {
2213+
const range = document.createRange()
22092214

2210-
expect(admission.requests).toHaveLength(1)
2211-
admission.release()
2212-
await expect(popup).toHaveCount(0)
2213-
await expect.poll(() => sent).toEqual([delivery])
2214-
await expect(editor).toHaveText("Keep my separate draft")
2215-
await expect(
2216-
page.locator('[data-component="composer-attachments"] [data-component="attachment-card"]'),
2217-
).toHaveCount(1)
2215+
range.selectNodeContents(element)
2216+
window.getSelection()?.removeAllRanges()
2217+
window.getSelection()?.addRange(range)
22182218
})
22192219
}
22202220

@@ -2233,13 +2233,7 @@ test("a failed direct quote send keeps the popup and comment for retry", async (
22332233
)
22342234

22352235
await expect(answer).toHaveText("Keep this quoted passage.")
2236-
await answer.evaluate((element) => {
2237-
const range = document.createRange()
2238-
2239-
range.selectNodeContents(element)
2240-
window.getSelection()?.removeAllRanges()
2241-
window.getSelection()?.addRange(range)
2242-
})
2236+
await selectQuoteText(answer)
22432237
await page
22442238
.getByRole("toolbar", { name: "Quote selection" })
22452239
.getByRole("button", { name: "Quote", exact: true })

‎packages/app/src/composer/submit.test.ts‎

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -84,14 +84,14 @@ function submitInput(
8484
commands: () => readonly { name: string }[] | undefined = () => [],
8585
history: string[] = [],
8686
clientCommand?: (text: string) => (() => void | Promise<void>) | undefined,
87-
delivery: ComposerDelivery = "steer",
87+
delivery: (alternate: boolean) => ComposerDelivery = () => "steer",
8888
) {
8989
return createComposerSubmit({
9090
adapter,
9191
mode: () => mode,
9292
commands,
9393
clientCommand,
94-
delivery: () => delivery,
94+
delivery,
9595
editor: () => undefined,
9696
queueScroll() {},
9797
addToHistory: (prompt) =>
@@ -205,7 +205,11 @@ describe("Composer submission", () => {
205205
undefined,
206206
undefined,
207207
undefined,
208-
delivery,
208+
(alternate) => {
209+
expect(alternate).toBe(true)
210+
211+
return delivery
212+
},
209213
)
210214

211215
expect(await submission.submitQuote("quote:c=selected", "Compare @src/api.ts")).toBe(true)
@@ -417,9 +421,15 @@ describe("Composer submission", () => {
417421
switchModel: async () => committed.promise,
418422
})
419423

420-
await submitInput(active(state, target), undefined, "normal", undefined, undefined, undefined, delivery).submit(
421-
new Event("submit"),
422-
)
424+
await submitInput(
425+
active(state, target),
426+
undefined,
427+
"normal",
428+
undefined,
429+
undefined,
430+
undefined,
431+
() => delivery,
432+
).submit(new Event("submit"))
423433

424434
expect(state.current()).toEqual([{ type: "text", content: "", start: 0, end: 0 }])
425435
expect(statuses).toEqual(["running"])
@@ -728,7 +738,7 @@ describe("Composer submission", () => {
728738
() => [],
729739
[],
730740
undefined,
731-
delivery,
741+
() => delivery,
732742
).submit(new Event("submit"))
733743
// The send settles after submit returns; let it and the move that follows it finish.
734744
await new Promise((resolve) => setTimeout(resolve, 0))

0 commit comments

Comments
 (0)