196 lines
6.9 KiB
TypeScript
196 lines
6.9 KiB
TypeScript
import type { Page } from '@stablyai/playwright-test'
|
|
import { test, expect } from './helpers/orca-app'
|
|
import { waitForActiveWorktree, waitForSessionReady } from './helpers/store'
|
|
import {
|
|
markWorkspaceTerminalSlept,
|
|
seedLineageScenario,
|
|
seedWorkspaceAgentStatus,
|
|
seedWorkspaceLiveTerminal
|
|
} from './worktree-lineage-state'
|
|
|
|
function worktreeOption(page: Page, worktreeId: string) {
|
|
return page.locator(`[id="worktree-list-option-${encodeURIComponent(worktreeId)}"]`)
|
|
}
|
|
|
|
test.describe('Worktree Lineage', () => {
|
|
test.describe.configure({ mode: 'serial' })
|
|
|
|
test.beforeEach(async ({ orcaPage }) => {
|
|
await waitForSessionReady(orcaPage)
|
|
await waitForActiveWorktree(orcaPage)
|
|
})
|
|
|
|
test('renders existing child lineage in the sidebar', async ({ orcaPage }) => {
|
|
const { parentId, childId } = await seedLineageScenario(orcaPage)
|
|
const parentRow = worktreeOption(orcaPage, parentId)
|
|
const childRow = worktreeOption(orcaPage, childId)
|
|
|
|
await expect(parentRow).toBeVisible()
|
|
await parentRow.click()
|
|
await expect(parentRow).toHaveAttribute('aria-current', 'page')
|
|
|
|
await expect(childRow).toBeVisible()
|
|
const childToggle = parentRow.getByRole('button', { name: 'Hide 1 child workspace' })
|
|
await expect(childToggle).toBeVisible({ timeout: 10_000 })
|
|
await expect(childRow).toBeVisible()
|
|
|
|
const positions = await orcaPage.evaluate(
|
|
({ parentId, childId }) => {
|
|
const parent = document.getElementById(
|
|
`worktree-list-option-${encodeURIComponent(parentId)}`
|
|
)
|
|
const child = document.getElementById(`worktree-list-option-${encodeURIComponent(childId)}`)
|
|
if (!parent || !child) {
|
|
return null
|
|
}
|
|
return {
|
|
parentTop: parent.getBoundingClientRect().top,
|
|
childTop: child.getBoundingClientRect().top
|
|
}
|
|
},
|
|
{ parentId, childId }
|
|
)
|
|
expect(positions).not.toBeNull()
|
|
expect(positions!.childTop).toBeGreaterThan(positions!.parentTop)
|
|
|
|
await childToggle.click()
|
|
await expect(parentRow.getByRole('button', { name: 'Show 1 child workspace' })).toBeVisible()
|
|
await expect(childRow).toBeHidden()
|
|
|
|
await parentRow.getByRole('button', { name: 'Show 1 child workspace' }).click()
|
|
await orcaPage.evaluate(async (childId) => {
|
|
const store = window.__store
|
|
if (!store) {
|
|
throw new Error('window.__store is not available')
|
|
}
|
|
// Why: this test covers lineage row rendering. Clearing through the
|
|
// store keeps it focused on the render contract instead of nested
|
|
// context-menu hit testing.
|
|
await store.getState().updateWorktreeLineage(childId, { noParent: true })
|
|
}, childId)
|
|
await expect
|
|
.poll(
|
|
() =>
|
|
orcaPage.evaluate((childId) => {
|
|
const store = window.__store
|
|
return Boolean(store?.getState().worktreeLineageById[childId])
|
|
}, childId),
|
|
{
|
|
timeout: 10_000,
|
|
message: 'Child lineage entry did not clear from the store'
|
|
}
|
|
)
|
|
.toBe(false)
|
|
await expect(childRow).toBeVisible()
|
|
})
|
|
|
|
test('injects filtered parents structurally without showing a parent badge', async ({
|
|
orcaPage
|
|
}) => {
|
|
const { parentId, childId } = await seedLineageScenario(orcaPage)
|
|
|
|
await orcaPage.evaluate(
|
|
({ parentId, childId }) => {
|
|
const store = window.__store
|
|
if (!store) {
|
|
throw new Error('window.__store is not available')
|
|
}
|
|
store.setState((current) => ({
|
|
worktreesByRepo: Object.fromEntries(
|
|
Object.entries(current.worktreesByRepo).map(([repoId, repoWorktrees]) => [
|
|
repoId,
|
|
repoWorktrees.map((worktree) =>
|
|
worktree.id === parentId
|
|
? {
|
|
...worktree,
|
|
branch: worktree.branch || 'refs/heads/main',
|
|
isMainWorktree: true
|
|
}
|
|
: worktree
|
|
)
|
|
])
|
|
)
|
|
}))
|
|
const state = store.getState()
|
|
state.setHideDefaultBranchWorkspace(true)
|
|
state.setShowActiveOnly(true)
|
|
state.setActiveWorktree(childId)
|
|
},
|
|
{ parentId, childId }
|
|
)
|
|
|
|
const parentRow = worktreeOption(orcaPage, parentId)
|
|
const childRow = worktreeOption(orcaPage, childId)
|
|
|
|
await expect(parentRow).toBeVisible()
|
|
await expect(childRow).toBeVisible()
|
|
await expect(childRow).not.toContainText(/\bfrom\b/)
|
|
|
|
const positions = await orcaPage.evaluate(
|
|
({ parentId, childId }) => {
|
|
const parent = document.getElementById(
|
|
`worktree-list-option-${encodeURIComponent(parentId)}`
|
|
)
|
|
const child = document.getElementById(`worktree-list-option-${encodeURIComponent(childId)}`)
|
|
if (!parent || !child) {
|
|
return null
|
|
}
|
|
return {
|
|
parentTop: parent.getBoundingClientRect().top,
|
|
childTop: child.getBoundingClientRect().top
|
|
}
|
|
},
|
|
{ parentId, childId }
|
|
)
|
|
expect(positions).not.toBeNull()
|
|
expect(positions!.childTop).toBeGreaterThan(positions!.parentTop)
|
|
})
|
|
|
|
test('updates nested child preview status when the child terminal sleeps', async ({
|
|
orcaPage
|
|
}) => {
|
|
const { parentId, childId } = await seedLineageScenario(orcaPage)
|
|
const parentRow = worktreeOption(orcaPage, parentId)
|
|
const childRow = worktreeOption(orcaPage, childId)
|
|
|
|
await expect(parentRow).toBeVisible()
|
|
await expect(childRow).toBeVisible()
|
|
|
|
const childTabId = await seedWorkspaceLiveTerminal(orcaPage, childId)
|
|
await expect(childRow).toContainText('Active')
|
|
await childRow.click({ button: 'right' })
|
|
await expect(orcaPage.getByRole('menuitem', { name: 'Sleep' })).not.toHaveAttribute(
|
|
'data-disabled',
|
|
''
|
|
)
|
|
await orcaPage.keyboard.press('Escape')
|
|
|
|
await markWorkspaceTerminalSlept(orcaPage, { worktreeId: childId, tabId: childTabId })
|
|
await expect(childRow).toContainText('Inactive')
|
|
await childRow.click({ button: 'right' })
|
|
await expect(orcaPage.getByRole('menuitem', { name: 'Sleep' })).toHaveAttribute(
|
|
'data-disabled',
|
|
''
|
|
)
|
|
await orcaPage.keyboard.press('Escape')
|
|
})
|
|
|
|
test('shows parent and child agent rows while the parent workspace is active', async ({
|
|
orcaPage
|
|
}) => {
|
|
const { parentId, childId } = await seedLineageScenario(orcaPage)
|
|
const parentRow = worktreeOption(orcaPage, parentId)
|
|
const childRow = worktreeOption(orcaPage, childId)
|
|
|
|
await parentRow.click()
|
|
await expect(parentRow).toHaveAttribute('aria-current', 'page')
|
|
await expect(childRow).toBeVisible()
|
|
|
|
const parentAgentPrompt = await seedWorkspaceAgentStatus(orcaPage, parentId, 'PARENT')
|
|
const childAgentPrompt = await seedWorkspaceAgentStatus(orcaPage, childId, 'CHILD')
|
|
|
|
await expect(parentRow.locator(`span[title="${parentAgentPrompt}"]`)).toBeVisible()
|
|
await expect(childRow.locator(`span[title="${childAgentPrompt}"]`)).toBeVisible()
|
|
})
|
|
})
|