diff --git a/packages/client/ui-goal/tests/goalbar.spec.tsx b/packages/client/ui-goal/tests/goalbar.spec.tsx index ce622bccd4..38a943d457 100644 --- a/packages/client/ui-goal/tests/goalbar.spec.tsx +++ b/packages/client/ui-goal/tests/goalbar.spec.tsx @@ -104,6 +104,13 @@ describe('GoalBar', () => { expect(screen.getByRole('textbox', { name: 'Goal objective' })).toBeTruthy() }) + it('active goal: the pause action pauses', () => { + const actions = makeActions() + render() + fireEvent.click(screen.getByRole('button', { name: 'Pause goal' })) + expect(actions.onPause).toHaveBeenCalledTimes(1) + }) + it('paused goal: "Paused Goal" with a resume action before edit', () => { const actions = makeActions() render() diff --git a/packages/client/ui-permission/tests/browser-plugin.spec.ts b/packages/client/ui-permission/tests/browser-plugin.spec.ts index 167cf17362..5f9125db53 100644 --- a/packages/client/ui-permission/tests/browser-plugin.spec.ts +++ b/packages/client/ui-permission/tests/browser-plugin.spec.ts @@ -85,6 +85,11 @@ describe('ui-permission browser plugin', () => { const again = await c.ui.options(proj, new AbortController().signal) expect(again.find(option => option.id === 'workspace-write')?.active).toBe(true) expect(again.find(option => option.id === 'read-only')?.detail).toBe('Reads only.') + // Kebab-case names title-case; non-kebab host-configured names pass through. + expect(again.map(option => option.label)).toEqual(['Read Only', 'Workspace Write', 'Danger Full Access']) + b.values.set(sid('s1'), { ...SELECT, options: [{ value: 'plain', name: 'Ask Every Time' }] }) + const passthrough = await c.ui.options(proj, new AbortController().signal) + expect(passthrough[0]?.label).toBe('Ask Every Time') // A projection that vanished between availability and open throws. expect(() => c.ui.options({ sessionId: sid('ghost') }, new AbortController().signal)) .toThrow(/not available on this host/) diff --git a/packages/client/ui-slash/tests/apply.spec.ts b/packages/client/ui-slash/tests/apply.spec.ts index 5a8b2b6c37..2e20dc96f8 100644 --- a/packages/client/ui-slash/tests/apply.spec.ts +++ b/packages/client/ui-slash/tests/apply.spec.ts @@ -84,6 +84,9 @@ describe('apply', () => { // The pick face routes into the controller pipeline (closed menu → no-op). injected.onPick('command', 0) expect(controller.menu.getSnapshot().open).toBe(false) + // The dismiss face routes into the controller too (closed menu → no-op). + injected.onDismiss() + expect(controller.menu.getSnapshot().open).toBe(false) // An unknown session id fails loud (no silent scope miss). expect(() => injectEntry(sid('ghost'))).toThrow(/resolved no scope/) }) diff --git a/packages/client/ui-slash/tests/menu-view.spec.tsx b/packages/client/ui-slash/tests/menu-view.spec.tsx index b18bbd6b30..1c74340575 100644 --- a/packages/client/ui-slash/tests/menu-view.spec.tsx +++ b/packages/client/ui-slash/tests/menu-view.spec.tsx @@ -142,6 +142,48 @@ describe('MenuView', () => { expect(screen.getByRole('listbox').style.maxHeight).toBe('88px') }) + it('pointerdown outside the menu (no composer card ancestor) dismisses', () => { + const { onDismiss } = mount(openState()) + fireEvent.pointerDown(document.body) + expect(onDismiss).toHaveBeenCalledTimes(1) + }) + + it('pointerdown inside the list does not dismiss', () => { + const { onDismiss } = mount(openState()) + fireEvent.pointerDown(screen.getAllByRole('option')[0]!) + expect(onDismiss).not.toHaveBeenCalled() + }) + + it('pointerdown inside the surrounding composer card does not dismiss; outside it does', () => { + const menu = createSnapshotStore(openState()) + const onDismiss = vi.fn() + render( +
+ +
, + ) + fireEvent.pointerDown(screen.getByTestId('composer-button')) + expect(onDismiss).not.toHaveBeenCalled() + fireEvent.pointerDown(document.body) + expect(onDismiss).toHaveBeenCalledTimes(1) + }) + + it('ignores a pointerdown whose target is not a DOM node', () => { + const { onDismiss } = mount(openState()) + const ev = new Event('pointerdown', { bubbles: true }) + Object.defineProperty(ev, 'target', { value: {} }) + document.dispatchEvent(ev) + expect(onDismiss).not.toHaveBeenCalled() + }) + + it('closing the menu removes the dismiss listener', () => { + const { menu, onDismiss } = mount(openState()) + act(() => { menu.set(CLOSED) }) + fireEvent.pointerDown(document.body) + expect(onDismiss).not.toHaveBeenCalled() + }) + it('mousedown on a row picks (source, index) and prevents the focus steal', () => { const { onPick } = mount(openState()) const options = screen.getAllByRole('option')