From 789e9daaf6cd37d7eb8b28bcb73dfe6eaf191f4c Mon Sep 17 00:00:00 2001 From: Yif <877193178@qq.com> Date: Thu, 23 Jul 2026 17:08:47 +0800 Subject: [PATCH] test(gui): cover the tooltip primitive and the inert expanded search control --- .../ui-primitives/tests/tooltip.spec.tsx | 100 ++++++++++++++++++ .../ui-sidebar/tests/sidebar-root.spec.tsx | 3 + 2 files changed, 103 insertions(+) create mode 100644 packages/client/ui-primitives/tests/tooltip.spec.tsx diff --git a/packages/client/ui-primitives/tests/tooltip.spec.tsx b/packages/client/ui-primitives/tests/tooltip.spec.tsx new file mode 100644 index 0000000000..c71124040d --- /dev/null +++ b/packages/client/ui-primitives/tests/tooltip.spec.tsx @@ -0,0 +1,100 @@ +// @vitest-environment jsdom +import { cleanup, fireEvent, render, screen } from '@testing-library/react' +import { afterEach, describe, expect, it, vi } from 'vitest' +import { Tooltip } from '@deepseek-ai/dsh-client-ui-primitives' + +afterEach(cleanup) + +describe('Tooltip', () => { + it('shows the bubble to the right on hover and hides it on leave', () => { + render( + + + , + ) + const anchor = screen.getByText('anchor') + fireEvent.mouseEnter(anchor) + const bubble = screen.getByRole('tooltip') + expect(bubble.textContent).toBe('Open sidebar') + expect(bubble.getAttribute('data-side')).toBe('right') + // jsdom rects are all-zero: right placement lands at the +10 gutter. + expect(bubble.style.left).toBe('10px') + expect(bubble.style.top).toBe('0px') + fireEvent.mouseLeave(anchor) + expect(screen.queryByRole('tooltip')).toBeNull() + }) + + it('supports bottom placement and the focus/blur channel', () => { + render( + + + , + ) + const anchor = screen.getByText('anchor') + fireEvent.focus(anchor) + const bubble = screen.getByRole('tooltip') + expect(bubble.getAttribute('data-side')).toBe('bottom') + expect(bubble.style.left).toBe('0px') + expect(bubble.style.top).toBe('8px') + fireEvent.blur(anchor) + expect(screen.queryByRole('tooltip')).toBeNull() + }) + + it('chains the anchor\'s own handlers ahead of the tooltip\'s', () => { + const onMouseEnter = vi.fn() + const onMouseLeave = vi.fn() + const onFocus = vi.fn() + const onBlur = vi.fn() + render( + + + , + ) + const anchor = screen.getByText('anchor') + fireEvent.mouseEnter(anchor) + fireEvent.mouseLeave(anchor) + fireEvent.focus(anchor) + fireEvent.blur(anchor) + expect(onMouseEnter).toHaveBeenCalledOnce() + expect(onMouseLeave).toHaveBeenCalledOnce() + expect(onFocus).toHaveBeenCalledOnce() + expect(onBlur).toHaveBeenCalledOnce() + }) + + it('suppresses the bubble while disabled without remounting the anchor', () => { + const { rerender } = render( + + + , + ) + const anchor = screen.getByText('anchor') + fireEvent.mouseEnter(anchor) + expect(screen.queryByRole('tooltip')).toBeNull() + rerender( + + + , + ) + // Same DOM node: toggling disabled never remounted the anchor. + expect(screen.getByText('anchor')).toBe(anchor) + fireEvent.mouseEnter(anchor) + expect(screen.getByRole('tooltip')).toBeTruthy() + }) + + it('drops an already-visible bubble when disabled flips mid-hover', () => { + const { rerender } = render( + + + , + ) + fireEvent.mouseEnter(screen.getByText('anchor')) + expect(screen.getByRole('tooltip')).toBeTruthy() + // e.g. clicking a rail control expands the sidebar: no mouseleave fires. + rerender( + + + , + ) + expect(screen.queryByRole('tooltip')).toBeNull() + }) +}) diff --git a/packages/client/ui-sidebar/tests/sidebar-root.spec.tsx b/packages/client/ui-sidebar/tests/sidebar-root.spec.tsx index d4d85bf3c0..e87d7fd4df 100644 --- a/packages/client/ui-sidebar/tests/sidebar-root.spec.tsx +++ b/packages/client/ui-sidebar/tests/sidebar-root.spec.tsx @@ -197,6 +197,9 @@ describe('SidebarRoot', () => { vi.useFakeTimers() try { const { onToggleSidebar } = mount(...projectData()) + // While expanded the search control is inert (the row click focuses instead). + act(() => { fireEvent.click(screen.getByLabelText('Search sessions')) }) + expect(onToggleSidebar).not.toHaveBeenCalled() act(() => { fireEvent.click(screen.getByLabelText('Collapse sidebar')) }) act(() => { vi.advanceTimersByTime(300) }) act(() => { fireEvent.click(screen.getByLabelText('Search sessions')) })