test(sidebar): harden #8865 project-group sleep-filter coverage

Add adversarial cases for explicit project filters, multi-member groups,
ungrouped neighbors, and buildRows membership/count preservation.

Co-authored-by: bbingz <zzb@gxsmjx.com>
This commit is contained in:
Neil
2026-07-16 17:31:16 -07:00
co-authored by bbingz
parent 0b0c2def8d
commit 1b88fa08c5
2 changed files with 135 additions and 0 deletions
@@ -131,4 +131,72 @@ describe('getEmptyProjectPlaceholderRepoIds', () => {
}).size
).toBe(0)
})
it('still respects explicit project filters for sleep-filtered grouped members', () => {
const selected: Repo = { ...repo, id: 'repo-selected', projectGroupId: 'group-1' }
const filteredOut: Repo = { ...repo, id: 'repo-hidden', projectGroupId: 'group-1' }
const selectedWt: Worktree = { ...worktree, id: 'wt-selected', repoId: selected.id }
const hiddenWt: Worktree = { ...worktree, id: 'wt-hidden', repoId: filteredOut.id }
expect(
Array.from(
getEmptyProjectPlaceholderRepoIds({
groupBy: 'repo',
repos: [selected, filteredOut],
worktreesByRepo: {
[selected.id]: [selectedWt],
[filteredOut.id]: [hiddenWt]
},
// Why: simulate Hide sleeping removing every card while the project
// filter still intentionally excludes `filteredOut`.
visibleWorktrees: [],
filterRepoIds: [selected.id]
})
)
).toEqual([selected.id])
})
it('placeholders only the fully-filtered members of a multi-project group', () => {
const sleeping: Repo = { ...repo, id: 'repo-sleeping', projectGroupId: 'group-1' }
const awake: Repo = { ...repo, id: 'repo-awake', projectGroupId: 'group-1' }
const sleepingWt: Worktree = { ...worktree, id: 'wt-sleeping', repoId: sleeping.id }
const awakeWt: Worktree = { ...worktree, id: 'wt-awake', repoId: awake.id }
expect(
Array.from(
getEmptyProjectPlaceholderRepoIds({
groupBy: 'repo',
repos: [sleeping, awake],
worktreesByRepo: {
[sleeping.id]: [sleepingWt],
[awake.id]: [awakeWt]
},
visibleWorktrees: [awakeWt],
filterRepoIds: []
})
)
).toEqual([sleeping.id])
})
it('does not placeholder ungrouped neighbors of a filtered grouped member', () => {
const grouped: Repo = { ...repo, id: 'repo-grouped', projectGroupId: 'group-1' }
const ungrouped: Repo = { ...repo, id: 'repo-ungrouped' }
const groupedWt: Worktree = { ...worktree, id: 'wt-grouped', repoId: grouped.id }
const ungroupedWt: Worktree = { ...worktree, id: 'wt-ungrouped', repoId: ungrouped.id }
expect(
Array.from(
getEmptyProjectPlaceholderRepoIds({
groupBy: 'repo',
repos: [grouped, ungrouped],
worktreesByRepo: {
[grouped.id]: [groupedWt],
[ungrouped.id]: [ungroupedWt]
},
visibleWorktrees: [],
filterRepoIds: []
})
)
).toEqual([grouped.id])
})
})
@@ -1933,6 +1933,73 @@ describe('project groups', () => {
expect(rows[0]).toMatchObject({ label: 'Platform' })
})
it('keeps sleep-filtered Project Group members as empty project headers', () => {
// Why: #8865 — Hide sleeping removes workspace cards; membership placeholders
// must still project the grouped project header so the group count stays honest.
const group: ProjectGroup = {
id: 'group-1',
name: 'Platform',
parentPath: '/platform',
parentGroupId: null,
createdFrom: 'folder-scan',
tabOrder: 0,
isCollapsed: false,
color: null,
createdAt: 1,
updatedAt: 1
}
const sleepingRepo: Repo = {
...repo,
id: 'repo-sleeping',
displayName: 'sleeping-project',
projectGroupId: group.id
}
const awakeRepo: Repo = {
...repo,
id: 'repo-awake',
displayName: 'awake-project',
projectGroupId: group.id
}
const awakeWorktree: Worktree = {
...worktree,
id: 'wt-awake',
repoId: awakeRepo.id,
path: '/tmp/awake'
}
const rows = buildRows(
'repo',
[awakeWorktree],
new Map([
[sleepingRepo.id, sleepingRepo],
[awakeRepo.id, awakeRepo]
]),
null,
new Set(),
undefined,
undefined,
undefined,
{},
new Map([[awakeWorktree.id, awakeWorktree]]),
false,
undefined,
[group],
new Set([sleepingRepo.id])
)
expect(rows[0]).toMatchObject({
type: 'header',
key: 'project-group:group-1',
count: 2
})
// Why: empty/placeholder projects sort after projects with visible activity.
expect(rows.filter((row) => row.type === 'header').map((row) => row.key)).toEqual([
'project-group:group-1',
`repo:${awakeRepo.id}`,
`repo:${sleepingRepo.id}`
])
})
it('renders ungrouped repos as top-level repo rows when Project Groups exist', () => {
const group: ProjectGroup = {
id: 'group-1',