test(web): settle complete folder queries before drilling

This commit is contained in:
Dudu-0223 committed 2026-09-17 14:38:24 +08:00
1 parent 5c507b12c6
commit 0dbe49a893
1 file changed
+15 -6
+15 -6
View File
@@ -47,6 +47,15 @@ async function settledSourceOption(menu: Locator): Promise<Locator> {
return source
}
// Clear retained suggestions and insert one complete query so an intermediate
// prefix cannot satisfy the caller's wait for a ready result row.
async function replaceReferenceQuery(page: Page, input: Locator, text: string): Promise<void> {
await writeComposerDraft(page, input, '')
await page.getByRole('listbox', { name: 'Trigger suggestions' }).waitFor({ state: 'hidden' })
await input.click()
await page.keyboard.insertText(text)
}
/** Build one closed source session with a stable title for reference discovery. */
function sourceSessionFixture(): string {
const session = Session.create(SessionId(SOURCE_SESSION_ID))
@@ -293,7 +302,7 @@ describe.skipIf(MODE === 'record')('web e2e: file and session references through
// Settle: Enter on the highlighted folder row resolves the folder itself
// as an atomic chip — folder glyph, no trigger character, one unit.
await writeComposerDraft(page, input, '@folderx')
await replaceReferenceQuery(page, input, '@folderx')
// First folder query on this page: allow the Host index a cold start.
await menu.getByRole('option', { name: /^folderx\// }).waitFor({ timeout: 60_000 })
await page.keyboard.press('Enter')
@@ -304,7 +313,7 @@ describe.skipIf(MODE === 'record')('web e2e: file and session references through
// Tab drills: the literal descent text stays editable and the open menu
// lists the folder's children.
await writeComposerDraft(page, input, '@folderx')
await replaceReferenceQuery(page, input, '@folderx')
await menu.getByRole('option', { name: /^folderx\// }).waitFor()
await page.keyboard.press('Tab')
await expect.poll(() => input.textContent()).toBe('@folderx/')
@@ -312,7 +321,7 @@ describe.skipIf(MODE === 'record')('web e2e: file and session references through
// The row chevron drills the same way by pointer, header included: a
// pointer descent reaches the same listing a Tab descent does.
await writeComposerDraft(page, input, '@folderx')
await replaceReferenceQuery(page, input, '@folderx')
const row = menu.getByRole('option', { name: /^folderx\// })
await row.waitFor()
await row.getByRole('button', { name: 'Browse folder' }).click()
@@ -337,12 +346,12 @@ describe.skipIf(MODE === 'record')('web e2e: file and session references through
const crumbs = page.getByRole('navigation', { name: 'Folder navigation' })
// A path the user typed carries its own context: no header.
await writeComposerDraft(page, input, '@folderx/')
await replaceReferenceQuery(page, input, '@folderx/')
await menu.getByRole('option', { name: /child\.txt/ }).waitFor({ timeout: 60_000 })
await expect.poll(() => crumbs.count()).toBe(0)
// The same listing reached by drilling owes the user the way back.
await writeComposerDraft(page, input, '@folderx')
await replaceReferenceQuery(page, input, '@folderx')
await menu.getByRole('option', { name: /^folderx\// }).waitFor()
await page.keyboard.press('Tab')
await menu.getByRole('option', { name: /child\.txt/ }).waitFor()
@@ -357,7 +366,7 @@ describe.skipIf(MODE === 'record')('web e2e: file and session references through
// A crumb above the current step re-lists that directory and keeps the
// header, which now names the step it returned to.
await writeComposerDraft(page, input, '@folderx/nested')
await replaceReferenceQuery(page, input, '@folderx/nested')
await expect.poll(() => menu.getByRole('option', { name: /child\.txt/ }).count()).toBe(0)
const nested = menu.getByRole('option', { name: /^nested\// })
await nested.waitFor()