Svelte defers DOM updates to microtasks; .query() is a synchronous snapshot that can fire before the element disappears — making the absence assertions in AnnotationShape and AnnotationLayer non-deterministic. Sweeps all 4 instances across both spec files (Sara's ≤5 threshold). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
178 lines
4.6 KiB
TypeScript
178 lines
4.6 KiB
TypeScript
import { describe, it, expect, vi, afterEach } from 'vitest';
|
|
import { cleanup, render } from 'vitest-browser-svelte';
|
|
import { page } from 'vitest/browser';
|
|
import AnnotationShape from './AnnotationShape.svelte';
|
|
|
|
afterEach(cleanup);
|
|
|
|
function makeAnnotation(id = 'ann-1') {
|
|
return {
|
|
id,
|
|
documentId: 'doc-1',
|
|
pageNumber: 1,
|
|
x: 0.1,
|
|
y: 0.1,
|
|
width: 0.3,
|
|
height: 0.2,
|
|
color: '#00C7B1',
|
|
createdAt: new Date().toISOString()
|
|
};
|
|
}
|
|
|
|
describe('AnnotationShape', () => {
|
|
it('renders the annotation element', async () => {
|
|
render(AnnotationShape, {
|
|
annotation: makeAnnotation(),
|
|
isHovered: false,
|
|
isActive: false,
|
|
onclick: () => {},
|
|
onpointerenter: () => {},
|
|
onpointerleave: () => {}
|
|
});
|
|
|
|
await expect.element(page.getByTestId('annotation-ann-1')).toBeInTheDocument();
|
|
});
|
|
|
|
it('does not show delete button when showDelete is false', async () => {
|
|
render(AnnotationShape, {
|
|
annotation: makeAnnotation(),
|
|
isHovered: true,
|
|
isActive: false,
|
|
showDelete: false,
|
|
onDeleteRequest: vi.fn(),
|
|
onclick: () => {},
|
|
onpointerenter: () => {},
|
|
onpointerleave: () => {}
|
|
});
|
|
|
|
await expect.element(page.getByTestId('annotation-delete-ann-1')).not.toBeInTheDocument();
|
|
});
|
|
|
|
it('does not show delete button when showDelete is true but neither hovered nor active', async () => {
|
|
render(AnnotationShape, {
|
|
annotation: makeAnnotation(),
|
|
isHovered: false,
|
|
isActive: false,
|
|
showDelete: true,
|
|
onDeleteRequest: vi.fn(),
|
|
onclick: () => {},
|
|
onpointerenter: () => {},
|
|
onpointerleave: () => {}
|
|
});
|
|
|
|
await expect.element(page.getByTestId('annotation-delete-ann-1')).not.toBeInTheDocument();
|
|
});
|
|
|
|
it('shows delete button when showDelete is true and isHovered is true', async () => {
|
|
render(AnnotationShape, {
|
|
annotation: makeAnnotation(),
|
|
isHovered: true,
|
|
isActive: false,
|
|
showDelete: true,
|
|
onDeleteRequest: vi.fn(),
|
|
onclick: () => {},
|
|
onpointerenter: () => {},
|
|
onpointerleave: () => {}
|
|
});
|
|
|
|
await expect.element(page.getByTestId('annotation-delete-ann-1')).toBeInTheDocument();
|
|
});
|
|
|
|
it('shows delete button when showDelete is true and isActive is true', async () => {
|
|
render(AnnotationShape, {
|
|
annotation: makeAnnotation(),
|
|
isHovered: false,
|
|
isActive: true,
|
|
showDelete: true,
|
|
onDeleteRequest: vi.fn(),
|
|
onclick: () => {},
|
|
onpointerenter: () => {},
|
|
onpointerleave: () => {}
|
|
});
|
|
|
|
await expect.element(page.getByTestId('annotation-delete-ann-1')).toBeInTheDocument();
|
|
});
|
|
|
|
it('calls onDeleteRequest when delete button is clicked', async () => {
|
|
const onDeleteRequest = vi.fn();
|
|
|
|
render(AnnotationShape, {
|
|
annotation: makeAnnotation(),
|
|
isHovered: true,
|
|
isActive: false,
|
|
showDelete: true,
|
|
onDeleteRequest,
|
|
onclick: () => {},
|
|
onpointerenter: () => {},
|
|
onpointerleave: () => {}
|
|
});
|
|
|
|
const deleteBtn = page.getByTestId('annotation-delete-ann-1');
|
|
await deleteBtn.click();
|
|
|
|
expect(onDeleteRequest).toHaveBeenCalledOnce();
|
|
});
|
|
|
|
it('does not call onclick when delete button is clicked', async () => {
|
|
const onclick = vi.fn();
|
|
const onDeleteRequest = vi.fn();
|
|
|
|
render(AnnotationShape, {
|
|
annotation: makeAnnotation(),
|
|
isHovered: true,
|
|
isActive: false,
|
|
showDelete: true,
|
|
onDeleteRequest,
|
|
onclick,
|
|
onpointerenter: () => {},
|
|
onpointerleave: () => {}
|
|
});
|
|
|
|
const deleteBtn = page.getByTestId('annotation-delete-ann-1');
|
|
await deleteBtn.click();
|
|
|
|
expect(onclick).not.toHaveBeenCalled();
|
|
expect(onDeleteRequest).toHaveBeenCalledOnce();
|
|
});
|
|
|
|
it('calls onDeleteRequest when Delete key is pressed on the annotation', async () => {
|
|
const onDeleteRequest = vi.fn();
|
|
|
|
render(AnnotationShape, {
|
|
annotation: makeAnnotation(),
|
|
isHovered: false,
|
|
isActive: true,
|
|
showDelete: true,
|
|
onDeleteRequest,
|
|
onclick: () => {},
|
|
onpointerenter: () => {},
|
|
onpointerleave: () => {}
|
|
});
|
|
|
|
const annotationEl = page.getByTestId('annotation-ann-1').element() as HTMLElement;
|
|
annotationEl.dispatchEvent(new KeyboardEvent('keydown', { key: 'Delete', bubbles: true }));
|
|
|
|
expect(onDeleteRequest).toHaveBeenCalledOnce();
|
|
});
|
|
|
|
it('does not call onDeleteRequest on Delete key when showDelete is false', async () => {
|
|
const onDeleteRequest = vi.fn();
|
|
|
|
render(AnnotationShape, {
|
|
annotation: makeAnnotation(),
|
|
isHovered: false,
|
|
isActive: true,
|
|
showDelete: false,
|
|
onDeleteRequest,
|
|
onclick: () => {},
|
|
onpointerenter: () => {},
|
|
onpointerleave: () => {}
|
|
});
|
|
|
|
const annotationEl = page.getByTestId('annotation-ann-1').element() as HTMLElement;
|
|
annotationEl.dispatchEvent(new KeyboardEvent('keydown', { key: 'Delete', bubbles: true }));
|
|
|
|
expect(onDeleteRequest).not.toHaveBeenCalled();
|
|
});
|
|
});
|