mirror of
https://github.com/stablyai/orca.git
synced 2026-11-13 17:37:40 +00:00
test: remove low-value sidebar and prompt disclosure suites
This commit is contained in:
@@ -1,144 +0,0 @@
|
||||
// @vitest-environment happy-dom
|
||||
import { act, cleanup, fireEvent, render, screen, waitFor } from '@testing-library/react'
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import { TooltipProvider } from '@/components/ui/tooltip'
|
||||
import { AutomationPromptDisclosure } from './AutomationPromptDisclosure'
|
||||
|
||||
const END_MARKER = 'SYNTHETIC-END-MARKER'
|
||||
const LONG_PROMPT = [
|
||||
'Synthetic prompt preview validation only.',
|
||||
'',
|
||||
...Array.from(
|
||||
{ length: 20 },
|
||||
(_, index) =>
|
||||
`Synthetic step ${index + 1}: inspect placeholder input and summarize placeholder output.`
|
||||
),
|
||||
'',
|
||||
END_MARKER
|
||||
].join('\n')
|
||||
|
||||
// Why: happy-dom has no layout, so the clamped paragraph's overflow is driven by these values.
|
||||
const layout = { scrollHeight: 0, clientHeight: 0 }
|
||||
const resizeCallbacks = new Set<() => void>()
|
||||
|
||||
class ResizeObserverStub {
|
||||
private readonly callback: () => void
|
||||
constructor(callback: () => void) {
|
||||
this.callback = callback
|
||||
}
|
||||
observe(): void {
|
||||
resizeCallbacks.add(this.callback)
|
||||
}
|
||||
unobserve(): void {}
|
||||
disconnect(): void {
|
||||
resizeCallbacks.delete(this.callback)
|
||||
}
|
||||
}
|
||||
|
||||
const writeClipboardText = vi.fn<(text: string) => Promise<void>>()
|
||||
|
||||
beforeEach(() => {
|
||||
layout.scrollHeight = 0
|
||||
layout.clientHeight = 0
|
||||
vi.spyOn(HTMLElement.prototype, 'scrollHeight', 'get').mockImplementation(
|
||||
() => layout.scrollHeight
|
||||
)
|
||||
vi.spyOn(HTMLElement.prototype, 'clientHeight', 'get').mockImplementation(
|
||||
() => layout.clientHeight
|
||||
)
|
||||
vi.stubGlobal('ResizeObserver', ResizeObserverStub)
|
||||
writeClipboardText.mockReset().mockResolvedValue(undefined)
|
||||
Object.assign(window, { api: { ui: { writeClipboardText } } })
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
cleanup()
|
||||
resizeCallbacks.clear()
|
||||
vi.restoreAllMocks()
|
||||
vi.unstubAllGlobals()
|
||||
})
|
||||
|
||||
function renderPrompt(prompt: string): void {
|
||||
render(
|
||||
<TooltipProvider>
|
||||
<AutomationPromptDisclosure prompt={prompt} />
|
||||
</TooltipProvider>
|
||||
)
|
||||
}
|
||||
|
||||
function promptFor(toggle: HTMLElement): HTMLElement {
|
||||
const id = toggle.getAttribute('aria-controls')
|
||||
const element = id ? document.getElementById(id) : null
|
||||
if (!element) {
|
||||
throw new Error('Toggle does not control a prompt element')
|
||||
}
|
||||
return element
|
||||
}
|
||||
|
||||
describe('AutomationPromptDisclosure', () => {
|
||||
it('shows a short prompt in full without an expand control', () => {
|
||||
layout.scrollHeight = 40
|
||||
layout.clientHeight = 40
|
||||
renderPrompt('Synthetic short prompt.')
|
||||
|
||||
expect(screen.getByText('Synthetic short prompt.')).toBeTruthy()
|
||||
expect(screen.queryByRole('button', { name: 'Show more' })).toBeNull()
|
||||
})
|
||||
|
||||
it('collapses a long prompt behind Show more and reveals all of it on expand', () => {
|
||||
layout.scrollHeight = 600
|
||||
layout.clientHeight = 160
|
||||
renderPrompt(LONG_PROMPT)
|
||||
|
||||
const showMore = screen.getByRole('button', { name: 'Show more' })
|
||||
expect(showMore.getAttribute('aria-expanded')).toBe('false')
|
||||
const prompt = promptFor(showMore)
|
||||
expect(prompt.textContent).toBe(LONG_PROMPT)
|
||||
|
||||
showMore.focus()
|
||||
fireEvent.click(showMore)
|
||||
|
||||
const showLess = screen.getByRole('button', { name: 'Show less' })
|
||||
expect(showLess.getAttribute('aria-expanded')).toBe('true')
|
||||
expect(document.activeElement).toBe(showLess)
|
||||
expect(promptFor(showLess)).toBe(prompt)
|
||||
expect(prompt.textContent).toContain(END_MARKER)
|
||||
|
||||
fireEvent.click(showLess)
|
||||
expect(screen.getByRole('button', { name: 'Show more' }).getAttribute('aria-expanded')).toBe(
|
||||
'false'
|
||||
)
|
||||
})
|
||||
|
||||
it('drops the expand control and keeps focus on the prompt when it stops overflowing', () => {
|
||||
layout.scrollHeight = 300
|
||||
layout.clientHeight = 160
|
||||
renderPrompt(`Synthetic resize focus validation. ${'placeholder '.repeat(48)}`)
|
||||
|
||||
const showMore = screen.getByRole('button', { name: 'Show more' })
|
||||
const prompt = promptFor(showMore)
|
||||
showMore.focus()
|
||||
|
||||
layout.scrollHeight = 160
|
||||
act(() => {
|
||||
for (const callback of resizeCallbacks) {
|
||||
callback()
|
||||
}
|
||||
})
|
||||
|
||||
expect(screen.queryByRole('button', { name: 'Show more' })).toBeNull()
|
||||
expect(document.activeElement).toBe(prompt)
|
||||
})
|
||||
|
||||
it('copies the full prompt text', async () => {
|
||||
layout.scrollHeight = 600
|
||||
layout.clientHeight = 160
|
||||
renderPrompt(LONG_PROMPT)
|
||||
|
||||
fireEvent.click(screen.getByRole('button', { name: 'Copy prompt' }))
|
||||
|
||||
expect(writeClipboardText).toHaveBeenCalledTimes(1)
|
||||
expect(writeClipboardText).toHaveBeenCalledWith(LONG_PROMPT)
|
||||
await waitFor(() => expect(screen.getByRole('button', { name: 'Copied' })).toBeTruthy())
|
||||
})
|
||||
})
|
||||
-72
@@ -1,72 +0,0 @@
|
||||
// @vitest-environment happy-dom
|
||||
import { act } from 'react'
|
||||
import { createRoot } from 'react-dom/client'
|
||||
import { expect, it, vi } from 'vitest'
|
||||
import { createUIStore } from '@/store/slices/ui/ui-slice-test-harness'
|
||||
import { useSidebarWorkspaceDeleteKeyboard } from './use-sidebar-workspace-delete-keyboard'
|
||||
import { resolveSidebarWorkspaceDeleteIntent } from './sidebar-workspace-delete'
|
||||
import { repo, worktree } from '../grouping/worktree-list-groups-test-fixtures'
|
||||
|
||||
const store = createUIStore()
|
||||
vi.mock('@/store', () => ({ useAppStore: { getState: () => store.getState() } }))
|
||||
vi.mock('../../workspace-actions/deletion/delete-worktree-flow', () => ({
|
||||
runWorktreeDelete: vi.fn(),
|
||||
runWorktreeBatchDelete: vi.fn()
|
||||
}))
|
||||
vi.mock('../../workspace-actions/deletion/folder-workspace-delete', () => ({
|
||||
deleteFolderWorkspaceUnlessUnsaved: vi.fn()
|
||||
}))
|
||||
Object.assign(globalThis, { IS_REACT_ACT_ENVIRONMENT: true })
|
||||
|
||||
it('resolves the initial delete gesture once and never resolves held repeats at fleet scale', () => {
|
||||
store.setState({ repos: [repo], deleteStateByWorktreeId: {} })
|
||||
for (const size of [750, 7500]) {
|
||||
const selectedWorktrees = Array.from({ length: size }, (_, index) => ({
|
||||
...worktree,
|
||||
id: `primary-${index}`,
|
||||
isMainWorktree: true
|
||||
}))
|
||||
const resolve = vi.fn(() =>
|
||||
resolveSidebarWorkspaceDeleteIntent({
|
||||
selectedWorktrees,
|
||||
focusedWorktree: selectedWorktrees[0],
|
||||
repoMap: new Map([[repo.id, repo]])
|
||||
})
|
||||
)
|
||||
function Probe() {
|
||||
const keyboard = useSidebarWorkspaceDeleteKeyboard({
|
||||
keybindings: {},
|
||||
resolveDeleteIntent: resolve,
|
||||
dropPreview: () => {}
|
||||
})
|
||||
return <div tabIndex={0} onKeyDown={keyboard.handleDeleteKeyDown} />
|
||||
}
|
||||
const container = document.createElement('div')
|
||||
document.body.append(container)
|
||||
const root = createRoot(container)
|
||||
try {
|
||||
act(() => root.render(<Probe />))
|
||||
const target = container.firstElementChild!
|
||||
for (let index = 0; index < 21; index++) {
|
||||
act(() =>
|
||||
target.dispatchEvent(
|
||||
new KeyboardEvent('keydown', {
|
||||
bubbles: true,
|
||||
cancelable: true,
|
||||
key: 'Backspace',
|
||||
code: 'Backspace',
|
||||
shiftKey: true,
|
||||
metaKey: navigator.userAgent.includes('Mac'),
|
||||
ctrlKey: !navigator.userAgent.includes('Mac'),
|
||||
repeat: index > 0
|
||||
})
|
||||
)
|
||||
)
|
||||
}
|
||||
expect(resolve).toHaveBeenCalledTimes(1)
|
||||
} finally {
|
||||
act(() => root.unmount())
|
||||
container.remove()
|
||||
}
|
||||
}
|
||||
})
|
||||
-136
@@ -1,136 +0,0 @@
|
||||
// @vitest-environment happy-dom
|
||||
import type { ComponentProps } from 'react'
|
||||
import { act, cleanup, fireEvent, render } from '@testing-library/react'
|
||||
import { afterEach, expect, it, vi } from 'vitest'
|
||||
import { useAppStore } from '@/store'
|
||||
import { TooltipProvider } from '@/components/ui/tooltip'
|
||||
import WorktreeContextMenu from '../../workspace-actions/context-menu/WorktreeContextMenu'
|
||||
import type WorktreeCard from '../../workspace-card/surface/WorktreeCard'
|
||||
import { renderFolderWorkspaceOption, type FolderWorkspaceRowContext } from './folder-row'
|
||||
import type { FolderWorkspaceItemRow } from '../listing/renderable-rows'
|
||||
import { WORKTREE_ROW_DRAG_INITIAL_STATE } from '../drag/row-state'
|
||||
import { folderWorkspaceToWorktree } from '../../../../../../shared/folder-workspaces/folder-workspace-worktree'
|
||||
import type { Worktree } from '../../../../../../shared/worktree/types'
|
||||
import { worktree } from '../grouping/worktree-list-groups-test-fixtures'
|
||||
|
||||
// Keep the real menu model/view at the card prop seam; omit unrelated card presentation.
|
||||
vi.mock('../../workspace-card/surface/WorktreeCard', () => ({
|
||||
default: (props: ComponentProps<typeof WorktreeCard>) => (
|
||||
<WorktreeContextMenu
|
||||
worktree={props.worktree}
|
||||
selectedWorktrees={props.selectedWorktrees}
|
||||
onContextMenuSelect={(event) =>
|
||||
props.onContextMenuSelect?.(event, props.worktree) ?? [props.worktree]
|
||||
}
|
||||
>
|
||||
<span>{props.worktree.displayName}</span>
|
||||
</WorktreeContextMenu>
|
||||
)
|
||||
}))
|
||||
|
||||
afterEach(() => {
|
||||
cleanup()
|
||||
useAppStore.setState(useAppStore.getInitialState(), true)
|
||||
})
|
||||
it.each([750, 7500])(
|
||||
'does no aggregate selection work for closed folder menus at %i selected rows',
|
||||
(size) => {
|
||||
const folders: FolderWorkspaceItemRow[] = Array.from({ length: 10 }, (_, index) => ({
|
||||
type: 'folder-workspace',
|
||||
key: `folder-${index}`,
|
||||
depth: 0,
|
||||
groupDepth: 0,
|
||||
projectGroup: {
|
||||
id: 'group',
|
||||
name: 'Folders',
|
||||
parentPath: null,
|
||||
parentGroupId: null,
|
||||
createdFrom: 'manual',
|
||||
tabOrder: 0,
|
||||
isCollapsed: false,
|
||||
color: null,
|
||||
createdAt: 0,
|
||||
updatedAt: 0
|
||||
},
|
||||
folderWorkspace: {
|
||||
id: `folder-${index}`,
|
||||
projectGroupId: 'group',
|
||||
name: `Folder ${index}`,
|
||||
folderPath: `/tmp/tri35-folder-${index}`,
|
||||
linkedTask: null,
|
||||
comment: '',
|
||||
isArchived: false,
|
||||
isUnread: false,
|
||||
isPinned: false,
|
||||
sortOrder: index,
|
||||
lastActivityAt: 0,
|
||||
createdAt: 0,
|
||||
updatedAt: 0
|
||||
}
|
||||
}))
|
||||
let selectedOwnerReads = 0
|
||||
const selection = Array.from({ length: size }, (_, index): Worktree => {
|
||||
const item =
|
||||
index < folders.length
|
||||
? folderWorkspaceToWorktree(folders[index].folderWorkspace)
|
||||
: { ...worktree, id: `worktree-${index}` }
|
||||
Object.defineProperty(item, 'runtimeOwnerEnvironmentId', {
|
||||
get: () => {
|
||||
selectedOwnerReads += 1
|
||||
return undefined
|
||||
}
|
||||
})
|
||||
return item
|
||||
})
|
||||
// The list owns this collection; unopened folder menus must not traverse it.
|
||||
const ctx: FolderWorkspaceRowContext & { selectedWorktrees: readonly Worktree[] } = {
|
||||
groupBy: 'none',
|
||||
newCardStyle: false,
|
||||
settings: null,
|
||||
activeWorktreeId: null,
|
||||
activeWorkspaceExecutionHostId: null,
|
||||
currentWorktreeId: null,
|
||||
selectedWorktreeIds: new Set(folders.map((row) => `local|folder:${row.folderWorkspace.id}`)),
|
||||
selectedWorktrees: selection,
|
||||
repoMap: new Map(),
|
||||
worktreeMap: new Map(),
|
||||
worktreeLineageById: {},
|
||||
workspaceLineageByChildKey: {},
|
||||
crossStoreChildrenByFolderId: new Map(),
|
||||
prCache: null,
|
||||
hostedReviewCache: null,
|
||||
getCachedFolderWorkspacePathStatus: () => null,
|
||||
onSelectionGesture: () => false,
|
||||
onContextMenuSelect: () => selection,
|
||||
onImmediateActivate: () => {},
|
||||
onRowClickCapture: () => {},
|
||||
onRowPointerDown: () => {},
|
||||
worktreeDragState: WORKTREE_ROW_DRAG_INITIAL_STATE,
|
||||
nativeLineageDropTargetIdentity: null
|
||||
}
|
||||
const view = render(
|
||||
<TooltipProvider>
|
||||
{folders.map((row) => renderFolderWorkspaceOption({ ctx, row }))}
|
||||
</TooltipProvider>
|
||||
)
|
||||
expect(selectedOwnerReads).toBe(0)
|
||||
view.rerender(
|
||||
<TooltipProvider>
|
||||
{folders.map((row) =>
|
||||
renderFolderWorkspaceOption({
|
||||
ctx: { ...ctx, selectedWorktreeIds: new Set(ctx.selectedWorktreeIds) },
|
||||
row
|
||||
})
|
||||
)}
|
||||
</TooltipProvider>
|
||||
)
|
||||
// Ten mounted folders require zero selected-row owner lookups until a menu opens, at either fleet size.
|
||||
expect(selectedOwnerReads).toBe(0)
|
||||
const first = view.container.querySelector('[data-worktree-context-menu-scope]')
|
||||
if (!(first instanceof HTMLElement)) {
|
||||
throw new Error('Missing folder menu scope')
|
||||
}
|
||||
act(() => fireEvent.contextMenu(first))
|
||||
expect(selectedOwnerReads).toBeGreaterThan(0)
|
||||
}
|
||||
)
|
||||
Reference in New Issue
Block a user