|
|
@@ -42,6 +42,8 @@ describe('web e2e: workspace management (create / rename / flat view / hover aff
|
|
|
let browser: Browser
|
|
|
let page: Page
|
|
|
let tripwire: ReturnType<typeof watchConsole>
|
|
|
+ let holdAttachment = false
|
|
|
+ let releaseAttachment: (() => void) | undefined
|
|
|
|
|
|
/**
|
|
|
* Raise the region header's directory dialog and drive it to a directory via
|
|
|
@@ -124,6 +126,22 @@ describe('web e2e: workspace management (create / rename / flat view / hover aff
|
|
|
await seedSession(scaffold, await readFile(SEED, 'utf8'), SEED_ID)
|
|
|
browser = await chromium.launch()
|
|
|
page = await newEnglishPage(browser)
|
|
|
+ await page.routeWebSocket('**/api/remote.mux', (route) => {
|
|
|
+ const server = route.connectToServer()
|
|
|
+ server.onMessage((message) => {
|
|
|
+ const frame = JSON.parse(String(message)) as {
|
|
|
+ type: string
|
|
|
+ value?: { type: string; workspace?: { sessionIds: string[] } }
|
|
|
+ }
|
|
|
+ if (holdAttachment && frame.type === 'item' && frame.value?.type === 'upsert'
|
|
|
+ && frame.value.workspace?.sessionIds.includes(SEED_ID) === true) {
|
|
|
+ holdAttachment = false
|
|
|
+ releaseAttachment = () => { route.send(message) }
|
|
|
+ return
|
|
|
+ }
|
|
|
+ route.send(message)
|
|
|
+ })
|
|
|
+ })
|
|
|
tripwire = watchConsole(page)
|
|
|
await page.goto(scaffold.authenticatedUrl, { waitUntil: 'load' })
|
|
|
await page.waitForSelector('[class*="frame"]', { timeout: 30_000 })
|
|
|
@@ -131,6 +149,7 @@ describe('web e2e: workspace management (create / rename / flat view / hover aff
|
|
|
|
|
|
afterAll(async () => {
|
|
|
await browser?.close()
|
|
|
+ releaseAttachment = undefined
|
|
|
await scaffold?.close()
|
|
|
})
|
|
|
|
|
|
@@ -211,6 +230,7 @@ describe('web e2e: workspace management (create / rename / flat view / hover aff
|
|
|
await adoptDirectory(scaffold.workspaceCwd, { waitForAgent: true })
|
|
|
const workspace = await scaffold.ctx.workspaceRegistry.resolveByPath(scaffold.workspaceCwd)
|
|
|
if (workspace === undefined) throw new Error('GUI did not register the existing project directory')
|
|
|
+ holdAttachment = true
|
|
|
await workspace.attachSession(SessionId(SEED_ID))
|
|
|
const header = (await scaffold.ctx.sessionPersistence.list())
|
|
|
.map(snapshot => snapshot.header)
|
|
|
@@ -220,23 +240,25 @@ describe('web e2e: workspace management (create / rename / flat view / hover aff
|
|
|
expect(await readFile(join(scaffold.workspaceCwd, 'workspace', 'a.txt'), 'utf8')).toBe('alpha\n')
|
|
|
await stat(seededLogPath)
|
|
|
|
|
|
- // Open the seeded (first/accounted) Session so deletion must preserve the
|
|
|
- // current selection while it moves into Ungrouped.
|
|
|
+ // The attachment stream can lag the Host commit while New Session is
|
|
|
+ // already visible. A row count or position cannot identify the seed.
|
|
|
const groupRow = page.locator('[role="treeitem"]').filter({ hasText: workspace.title }).first()
|
|
|
await groupRow.waitFor({ timeout: 10_000 })
|
|
|
// The header row is wrapped by its HoverCard anchor span, so the section
|
|
|
// is the nearest groupSection ancestor, not the immediate parent.
|
|
|
const groupSection = groupRow.locator('xpath=ancestor::*[contains(@class, "groupSection")][1]')
|
|
|
- await expect.poll(async () => {
|
|
|
- const count = await groupSection.locator('[role="treeitem"]').count()
|
|
|
- if (count < 2 && await groupRow.getAttribute('aria-expanded') !== 'true') {
|
|
|
- await groupRow.click()
|
|
|
- await page.waitForTimeout(50)
|
|
|
- }
|
|
|
- return await groupSection.locator('[role="treeitem"]').count()
|
|
|
- }, { timeout: 10_000 }).toBeGreaterThanOrEqual(2)
|
|
|
- const seededRow = groupSection.locator('[role="treeitem"]').nth(1)
|
|
|
- await seededRow.click()
|
|
|
+ if (await groupRow.getAttribute('aria-expanded') !== 'true') await groupRow.click()
|
|
|
+ const blankRow = groupSection.getByRole('treeitem', { name: 'New Session', exact: true })
|
|
|
+ await expect.poll(() => blankRow.getAttribute('aria-selected'), { timeout: 10_000 }).toBe('true')
|
|
|
+ // The seed is this account's only non-blank Session; its title changes on resume.
|
|
|
+ const seededRow = groupSection.locator('[role="treeitem"][aria-selected]')
|
|
|
+ .filter({ hasNot: page.getByText('New Session', { exact: true }) })
|
|
|
+ await expect.poll(() => releaseAttachment, { timeout: 10_000 }).toBeDefined()
|
|
|
+ expect(await seededRow.count()).toBe(0)
|
|
|
+ const deliverAttachment = releaseAttachment!
|
|
|
+ releaseAttachment = undefined
|
|
|
+ deliverAttachment()
|
|
|
+ await seededRow.click({ timeout: 10_000 })
|
|
|
await expect.poll(() => seededRow.getAttribute('aria-selected'), { timeout: 10_000 }).toBe('true')
|
|
|
|
|
|
await clickHoverAction(groupRow, `Workspace actions for ${workspace.title}`)
|