/** @vitest-environment jsdom */ import { cleanup, fireEvent, render, screen, within, } from '@testing-library/react'; import { afterEach, describe, expect, it, vi } from 'vitest'; import { CanvasCardCornerActions } from './CanvasCardCornerActions'; import { OverflowActions } from './OverflowActions'; afterEach(cleanup); describe('操作收纳与卡片角标', () => { it('具名入口可收纳全部动作,并优先向上展开', () => { render( , ); const trigger = screen.getByRole('button', { name: '素材处理' }); expect(screen.getAllByRole('button')).toHaveLength(1); vi.spyOn(trigger, 'getBoundingClientRect').mockReturnValue({ left: 200, right: 300, top: 350, bottom: 380, width: 100, height: 30, x: 200, y: 350, toJSON: () => ({}), }); const height = vi .spyOn(HTMLElement.prototype, 'scrollHeight', 'get') .mockReturnValue(100); fireEvent.mouseEnter(trigger); const group = screen.getByRole('group', { name: '素材处理操作' }); expect(parseFloat(group.style.top)).toBeLessThan(350); expect( within(group) .getAllByRole('button') .map((b) => b.textContent), ).toEqual(['快速编辑', '改造']); fireEvent.keyDown(document.body, { key: 'Escape' }); expect(screen.queryByRole('group')).toBeNull(); height.mockRestore(); vi.restoreAllMocks(); }); it('前五项可见,跳过分隔符和空 fragment,悬停展示剩余项并执行原回调', () => { const click = vi.fn(); render( <> , ); expect(screen.getAllByRole('button')).toHaveLength(6); expect(screen.queryByText('六')).toBeNull(); fireEvent.mouseEnter(screen.getByRole('button', { name: '更多' })); const group = screen.getByRole('group', { name: '更多操作' }); expect( within(group) .getAllByRole('button') .map((b) => b.textContent), ).toEqual(['六', '七']); expect((screen.getByText('七') as HTMLButtonElement).disabled).toBe(true); fireEvent.click(screen.getByText('六')); expect(click).toHaveBeenCalledTimes(1); expect(screen.queryByRole('group')).toBeNull(); }); it('悬停后点击仍展开、移入浮层不消失、外部关闭,Escape 恢复焦点', () => { vi.useFakeTimers(); render( , ); const more = screen.getByRole('button', { name: '更多' }); fireEvent.mouseEnter(more); fireEvent.click(more); fireEvent.mouseLeave(more); fireEvent.mouseEnter(screen.getByRole('group')); vi.advanceTimersByTime(200); expect(screen.getByText('二')).toBeTruthy(); fireEvent.keyDown(screen.getByText('二'), { key: 'Escape' }); expect(screen.queryByRole('group')).toBeNull(); expect(document.activeElement).toBe(more); fireEvent.click(more); fireEvent.pointerDown(document.body); expect(screen.queryByRole('group')).toBeNull(); vi.useRealTimers(); }); it('不溢出不显示更多,默认保持 Web 原样', () => { const view = render( , ); expect(screen.queryByText('更多')).toBeNull(); view.rerender( {Array.from({ length: 10 }, (_, i) => ( ))} , ); expect(screen.getAllByRole('button')).toHaveLength(10); }); it('动作减少至不溢出后关闭浮层,恢复动作不会自动重开', () => { const view = render( , ); fireEvent.mouseEnter(screen.getByRole('button', { name: '更多' })); expect(screen.getByRole('group')).toBeTruthy(); view.rerender( , ); expect(screen.queryByRole('group')).toBeNull(); view.rerender( , ); expect(screen.queryByRole('group')).toBeNull(); }); it('浮层在窗口右下边界向上展开,方向键跳过禁用项', () => { render( , ); const more = screen.getByRole('button', { name: '更多' }); vi.spyOn(more, 'getBoundingClientRect').mockReturnValue({ left: window.innerWidth - 35, right: window.innerWidth, top: window.innerHeight - 40, bottom: window.innerHeight - 10, width: 35, height: 30, x: 0, y: 0, toJSON: () => ({}), }); // jsdom 没有真实布局,提供浮层测量以验证向上定位。 const height = vi .spyOn(HTMLElement.prototype, 'scrollHeight', 'get') .mockReturnValue(200); fireEvent.click(more); const panel = screen.getByRole('group'); expect(parseFloat(panel.style.top)).toBeLessThan(window.innerHeight - 40); screen.getByText('二').focus(); fireEvent.keyDown(screen.getByText('二'), { key: 'ArrowDown' }); expect(document.activeElement).toBe(screen.getByText('四')); fireEvent.keyDown(screen.getByText('四'), { key: 'Home' }); expect(document.activeElement).toBe(screen.getByText('二')); height.mockRestore(); vi.restoreAllMocks(); }); it('卡片角标阻断指针和键盘冒泡,类型及信息分别执行', () => { const parent = vi.fn(), kind = vi.fn(), info = vi.fn(); render(
, ); const label = screen.getByRole('button', { name: '素材类型' }); fireEvent.pointerDown(label); fireEvent.click(label); fireEvent.keyDown(label, { key: 'Enter' }); fireEvent.click(screen.getByRole('button', { name: '资源信息' })); expect(kind).toHaveBeenCalledTimes(2); expect(info).toHaveBeenCalledOnce(); expect(parent).not.toHaveBeenCalled(); }); });