mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-06-23 23:31:47 +00:00
test(front): add test suite frontend (WIP)
This commit is contained in:
@@ -0,0 +1,82 @@
|
||||
import { render, screen, fireEvent, act } from '../../../tests/helpers/render';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import { ContextMenu } from './ContextMenu';
|
||||
import { Trash2, Edit } from 'lucide-react';
|
||||
|
||||
const makeMenu = (x = 100, y = 200, overrides?: object[]) => ({
|
||||
x,
|
||||
y,
|
||||
items: overrides ?? [
|
||||
{ label: 'Edit', icon: Edit, onClick: vi.fn() },
|
||||
{ label: 'Delete', icon: Trash2, onClick: vi.fn(), danger: true },
|
||||
],
|
||||
});
|
||||
|
||||
describe('ContextMenu', () => {
|
||||
const onClose = vi.fn();
|
||||
|
||||
beforeEach(() => {
|
||||
onClose.mockClear();
|
||||
});
|
||||
|
||||
it('FE-COMP-CTX-001: renders nothing when menu is null', () => {
|
||||
render(<ContextMenu menu={null} onClose={onClose} />);
|
||||
expect(document.body.querySelector('[style*="z-index: 999999"]')).toBeNull();
|
||||
});
|
||||
|
||||
it('FE-COMP-CTX-002: renders menu items at the specified position', () => {
|
||||
render(<ContextMenu menu={makeMenu(150, 250)} onClose={onClose} />);
|
||||
expect(screen.getByText('Edit')).toBeTruthy();
|
||||
expect(screen.getByText('Delete')).toBeTruthy();
|
||||
|
||||
// Portal root div has position fixed at the given coords
|
||||
const portal = document.body.querySelector('[style*="position: fixed"]') as HTMLElement;
|
||||
expect(portal.style.left).toBe('150px');
|
||||
expect(portal.style.top).toBe('250px');
|
||||
});
|
||||
|
||||
it('FE-COMP-CTX-003: clicking a menu item calls its onClick and onClose', async () => {
|
||||
const onClick = vi.fn();
|
||||
const menu = makeMenu(100, 200, [{ label: 'Copy', onClick }]);
|
||||
const user = userEvent.setup();
|
||||
render(<ContextMenu menu={menu} onClose={onClose} />);
|
||||
|
||||
await user.click(screen.getByText('Copy'));
|
||||
expect(onClick).toHaveBeenCalledOnce();
|
||||
// onClose is called once by the button handler and once by the document click listener
|
||||
expect(onClose).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('FE-COMP-CTX-004: divider items render as a separator without text', () => {
|
||||
const menu = makeMenu(100, 200, [
|
||||
{ label: 'Item A', onClick: vi.fn() },
|
||||
{ divider: true },
|
||||
{ label: 'Item B', onClick: vi.fn() },
|
||||
]);
|
||||
render(<ContextMenu menu={menu} onClose={onClose} />);
|
||||
expect(screen.getByText('Item A')).toBeTruthy();
|
||||
expect(screen.getByText('Item B')).toBeTruthy();
|
||||
// Divider should not have any button text
|
||||
const buttons = screen.getAllByRole('button');
|
||||
expect(buttons).toHaveLength(2);
|
||||
});
|
||||
|
||||
it('FE-COMP-CTX-005: danger items have red color styling', () => {
|
||||
const menu = makeMenu(100, 200, [
|
||||
{ label: 'Remove', onClick: vi.fn(), danger: true },
|
||||
]);
|
||||
render(<ContextMenu menu={menu} onClose={onClose} />);
|
||||
const btn = screen.getByRole('button', { name: /remove/i });
|
||||
// Danger buttons use color #ef4444 inline style
|
||||
expect(btn.style.color).toBe('rgb(239, 68, 68)');
|
||||
});
|
||||
|
||||
it('FE-COMP-CTX-006: clicking outside the menu closes it via document click listener', () => {
|
||||
render(<ContextMenu menu={makeMenu()} onClose={onClose} />);
|
||||
// Document click event triggers the close handler
|
||||
act(() => {
|
||||
document.dispatchEvent(new MouseEvent('click', { bubbles: true }));
|
||||
});
|
||||
expect(onClose).toHaveBeenCalledOnce();
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user