Просмотр исходного кода

test(client): cover menu dismiss, goal pause, and permission label paths

Close the per-file coverage gaps the new UI behavior introduced: MenuView
pointer-outside dismiss (all guard branches), the GoalBar pause action,
the ui-slash injected onDismiss face, and the non-kebab permission name
passthrough.
Yif 2 месяцев назад
Родитель
Сommit
82a43a7240

+ 7 - 0
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(<GoalBar goal={makeGoal()} {...actions} />)
+    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(<GoalBar goal={makeGoal({ phase: 'paused' })} {...actions} />)

+ 5 - 0
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/)

+ 3 - 0
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/)
   })

+ 42 - 0
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<MenuState>(openState())
+    const onDismiss = vi.fn()
+    render(
+      <div data-composer-card="">
+        <MenuView menu={menu} onPick={vi.fn()} onDismiss={onDismiss} t={t} />
+        <button type="button" data-testid="composer-button" />
+      </div>,
+    )
+    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')