From 015353d970fb9d18cb453bb0fda1e1c15c6d11b0 Mon Sep 17 00:00:00 2001 From: Brooklyn Nicholson Date: Mon, 27 Jul 2026 22:03:37 -0500 Subject: [PATCH] test(desktop): cover short actions labels and tip-less close buttons --- .../sidebar/projects/project-menu.test.tsx | 6 +-- .../projects/workspace-header.test.tsx | 4 +- .../sidebar/session-actions-menu.test.tsx | 8 ++-- .../src/app/chat/sidebar/session-row.test.tsx | 4 +- .../settings/toolset-config-panel.test.tsx | 8 ++-- .../desktop/src/components/ui/dialog.test.tsx | 42 ++----------------- 6 files changed, 19 insertions(+), 53 deletions(-) diff --git a/apps/desktop/src/app/chat/sidebar/projects/project-menu.test.tsx b/apps/desktop/src/app/chat/sidebar/projects/project-menu.test.tsx index 54f23f5bcf1..e0df9f2a5c0 100644 --- a/apps/desktop/src/app/chat/sidebar/projects/project-menu.test.tsx +++ b/apps/desktop/src/app/chat/sidebar/projects/project-menu.test.tsx @@ -29,7 +29,7 @@ vi.mock('@/i18n', () => ({ projects: { copyPath: 'Copy path', deleteConfirm: 'This cannot be undone.', - menu: 'Project actions', + menu: 'Actions', menuAddFolder: 'Add folder', menuAppearance: 'Appearance', menuDelete: 'Delete', @@ -91,7 +91,7 @@ describe('ProjectMenu', () => { it('does not wrap the kebab trigger in a Tip', () => { render() - const button = screen.getByRole('button', { name: 'Project actions' }) + const button = screen.getByRole('button', { name: 'Actions' }) expect(tipTrigger(button)).toBeNull() }) @@ -101,7 +101,7 @@ describe('ProjectMenu', () => { it('opens the appearance popover through the kebab trigger when anchorRef is absent', async () => { render() - const trigger = screen.getByRole('button', { name: 'Project actions' }) + const trigger = screen.getByRole('button', { name: 'Actions' }) openTriggerMenu(trigger) diff --git a/apps/desktop/src/app/chat/sidebar/projects/workspace-header.test.tsx b/apps/desktop/src/app/chat/sidebar/projects/workspace-header.test.tsx index f28aa84fb54..e03c14ac1f6 100644 --- a/apps/desktop/src/app/chat/sidebar/projects/workspace-header.test.tsx +++ b/apps/desktop/src/app/chat/sidebar/projects/workspace-header.test.tsx @@ -11,7 +11,7 @@ vi.mock('@/i18n', () => ({ sidebar: { projects: { copyPath: 'Copy path', - menu: 'Project actions', + menu: 'Actions', removeWorktree: 'Remove worktree', reveal: 'Reveal in file manager', startWork: 'New worktree' @@ -66,7 +66,7 @@ describe('WorkspaceMenu', () => { it('does not wrap the kebab trigger in a Tip', () => { render() - const button = screen.getByRole('button', { name: 'Project actions' }) + const button = screen.getByRole('button', { name: 'Actions' }) expect(tipTrigger(button)).toBeNull() }) }) diff --git a/apps/desktop/src/app/chat/sidebar/session-actions-menu.test.tsx b/apps/desktop/src/app/chat/sidebar/session-actions-menu.test.tsx index 0a257af2054..d3e197958f1 100644 --- a/apps/desktop/src/app/chat/sidebar/session-actions-menu.test.tsx +++ b/apps/desktop/src/app/chat/sidebar/session-actions-menu.test.tsx @@ -24,7 +24,6 @@ vi.mock('@/i18n', () => ({ sidebar: { projects: { menuAppearance: 'Appearance', noColor: 'No color' }, row: { - actionsFor: (title: string) => `Actions for ${title}`, archive: 'Archive', branchFrom: 'Branch from here', copyId: 'Copy ID', @@ -33,10 +32,11 @@ vi.mock('@/i18n', () => ({ hideTabBar: 'Hide tab bar', pin: 'Pin', rename: 'Rename', - renameDesc: 'Rename this session', + renameDesc: 'Leave empty to clear.', renameFailed: 'Rename failed', renameTitle: 'Rename session', renamed: 'Renamed', + sessionActions: 'Session actions', unpin: 'Unpin', untitledPlaceholder: 'Untitled' } @@ -74,7 +74,7 @@ vi.mock('@/store/windows', () => ({ function renderMenu() { return render( - @@ -85,7 +85,7 @@ describe('SessionActionsMenu', () => { it('opens the dropdown on click without a tooltip on the kebab', async () => { renderMenu() - const trigger = screen.getByRole('button', { name: 'Actions for My session' }) + const trigger = screen.getByRole('button', { name: 'Session actions' }) expect(trigger.closest('[data-slot="tooltip-trigger"]')).toBeNull() diff --git a/apps/desktop/src/app/chat/sidebar/session-row.test.tsx b/apps/desktop/src/app/chat/sidebar/session-row.test.tsx index 561dd5cc5d7..dced1a0931f 100644 --- a/apps/desktop/src/app/chat/sidebar/session-row.test.tsx +++ b/apps/desktop/src/app/chat/sidebar/session-row.test.tsx @@ -18,13 +18,13 @@ vi.mock('@/i18n', () => ({ t: { sidebar: { row: { - actionsFor: (title: string) => `Actions for ${title}`, ageMin: 'm', ageNow: 'now', backgroundRunning: 'Running in background', finishedUnread: 'Finished', handoffOrigin: (platform: string) => `Started on ${platform}`, needsInput: 'Needs input', + sessionActions: 'Session actions', sessionRunning: 'Running', waitingForAnswer: 'Waiting for answer' } @@ -131,7 +131,7 @@ describe('SidebarSessionRow', () => { /> ) - const kebab = screen.getByRole('button', { name: 'Actions for Hermes doctor health check results' }) + const kebab = screen.getByRole('button', { name: 'Session actions' }) expect(tipTrigger(kebab)).toBeNull() }) diff --git a/apps/desktop/src/app/settings/toolset-config-panel.test.tsx b/apps/desktop/src/app/settings/toolset-config-panel.test.tsx index a633e892aff..6fd5d359087 100644 --- a/apps/desktop/src/app/settings/toolset-config-panel.test.tsx +++ b/apps/desktop/src/app/settings/toolset-config-panel.test.tsx @@ -297,7 +297,7 @@ describe('ToolsetConfigPanel', () => { fireEvent.click(elevenlabs) // Open the credential actions menu (Radix opens on pointerdown), then "Set". - const trigger = await screen.findByRole('button', { name: /Actions for ELEVENLABS_API_KEY/ }) + const trigger = await screen.findByRole('button', { name: /^Actions$/ }) fireEvent.pointerDown(trigger, { button: 0, ctrlKey: false, pointerType: 'mouse' }) fireEvent.click(await screen.findByRole('menuitem', { name: 'Set' })) @@ -655,7 +655,7 @@ describe('ToolsetConfigPanel', () => { // Save a key: the pill must go Ready from the local envState patch even // though the (now stale) server status still says needs_keys. - const trigger = await screen.findByRole('button', { name: /Actions for ELEVENLABS_API_KEY/ }) + const trigger = await screen.findByRole('button', { name: /^Actions$/ }) fireEvent.pointerDown(trigger, { button: 0, ctrlKey: false, pointerType: 'mouse' }) fireEvent.click(await screen.findByRole('menuitem', { name: 'Set' })) fireEvent.change(await screen.findByPlaceholderText('ElevenLabs API key'), { target: { value: 'sk-live' } }) @@ -929,7 +929,7 @@ describe('ToolsetConfigPanel', () => { const { ToolsetConfigPanel } = await import('./toolset-config-panel') render() - const trigger = await screen.findByRole('button', { name: /Actions for ELEVENLABS_API_KEY/ }) + const trigger = await screen.findByRole('button', { name: /^Actions$/ }) fireEvent.pointerDown(trigger, { button: 0, ctrlKey: false, pointerType: 'mouse' }) fireEvent.click(await screen.findByRole('menuitem', { name: 'Manage in API Keys' })) @@ -949,7 +949,7 @@ describe('ToolsetConfigPanel', () => { fireEvent.click(elevenLabs) await waitFor(() => expect(elevenLabs.getAttribute('aria-pressed')).toBe('true')) - const trigger = await screen.findByRole('button', { name: /Actions for ELEVENLABS_API_KEY/ }) + const trigger = await screen.findByRole('button', { name: /^Actions$/ }) fireEvent.pointerDown(trigger, { button: 0, ctrlKey: false, pointerType: 'mouse' }) await screen.findByRole('menuitem', { name: 'Set' }) diff --git a/apps/desktop/src/components/ui/dialog.test.tsx b/apps/desktop/src/components/ui/dialog.test.tsx index afebf60760d..354aa755132 100644 --- a/apps/desktop/src/components/ui/dialog.test.tsx +++ b/apps/desktop/src/components/ui/dialog.test.tsx @@ -1,4 +1,4 @@ -import { cleanup, fireEvent, render, screen, waitFor } from '@testing-library/react' +import { cleanup, fireEvent, render, screen } from '@testing-library/react' import { afterEach, describe, expect, it, vi } from 'vitest' import { Dialog, DialogContent, DialogTitle, preventCloseButtonAutoFocus } from './dialog' @@ -51,7 +51,7 @@ describe('DialogContent close button', () => { expect(onOpenChange).toHaveBeenCalledWith(false) }) - it('does not show the tooltip immediately on open when the dialog opts out of autofocus (no hover/focus yet)', async () => { + it('close button has no tip (X is the label)', () => { render( @@ -60,11 +60,8 @@ describe('DialogContent close button', () => { ) - // Radix would otherwise autofocus the close button on open (this dialog has - // no input), which also triggers the tooltip via focus. Dialogs with no - // input (e.g. the updates overlay) opt into `preventCloseButtonAutoFocus` - // explicitly — this is no longer dialog.tsx's default for every dialog. - expect(screen.getByRole('button')).toBeTruthy() + const close = screen.getByRole('button', { name: /close/i }) + expect(close.closest('[data-slot="tooltip-trigger"]')).toBeNull() expect(screen.queryByRole('tooltip')).toBeNull() }) @@ -105,35 +102,4 @@ describe('DialogContent close button', () => { expect(event.defaultPrevented).toBe(true) }) - // Skipped: pre-existing test, unrelated to the onOpenAutoFocus scoping this - // file is actually about (that's fully covered by the three tests above). - // The tooltip's open transition is driven by a real, un-act()-wrapped timer - // inside Radix/Tip, and on the Linux CI runner it consistently never fires - // within any timeout tried (1000ms/3000ms), while passing reliably in a full - // local run on Windows — an environment-specific flake, not a regression - // from this change. Needs its own investigation (e.g. Radix/jsdom version - // pinning, timer/act handling) rather than a timeout bump. - it.skip('shows the tooltip on focus (Radix opens on focus as well as hover; jsdom cannot reliably simulate real pointer hover)', async () => { - render( - - {/* No input here, so without this opt-out Radix's real autofocus would - land on the close button on mount and race with the manual - fireEvent.focus below (same reason updates-overlay.tsx opts out). */} - - Test dialog - - - ) - - const closeButton = screen.getByRole('button', { name: /close/i }) - closeButton.focus() - - await waitFor( - () => { - const tooltip = screen.getByRole('tooltip') - expect(tooltip.textContent).toMatch(/close/i) - }, - { timeout: 3000 } - ) - }) })