diff --git a/frontend/src/lib/components/AnnotationSidePanel.svelte.spec.ts b/frontend/src/lib/components/AnnotationSidePanel.svelte.spec.ts new file mode 100644 index 00000000..84745470 --- /dev/null +++ b/frontend/src/lib/components/AnnotationSidePanel.svelte.spec.ts @@ -0,0 +1,76 @@ +import { describe, it, expect, vi, afterEach } from 'vitest'; +import { cleanup, render } from 'vitest-browser-svelte'; +import { page } from 'vitest/browser'; +import AnnotationSidePanel from './AnnotationSidePanel.svelte'; + +afterEach(() => { + cleanup(); + vi.restoreAllMocks(); +}); + +vi.stubGlobal( + 'fetch', + vi.fn().mockResolvedValue({ + ok: true, + json: async () => [] + }) +); + +const baseProps = { + documentId: 'doc-1', + activeAnnotationPage: 1, + canComment: true, + currentUserId: 'user-1', + canAdmin: false, + onClose: vi.fn() +}; + +describe('AnnotationSidePanel – visibility', () => { + it('is hidden (translated off-screen) when activeAnnotationId is null', async () => { + render(AnnotationSidePanel, { ...baseProps, activeAnnotationId: null }); + const panel = document.querySelector('[data-testid="annotation-side-panel"]'); + expect(panel?.classList.contains('translate-x-full')).toBe(true); + expect(panel?.classList.contains('translate-x-0')).toBe(false); + }); + + it('is visible when activeAnnotationId is set', async () => { + render(AnnotationSidePanel, { ...baseProps, activeAnnotationId: 'ann-1' }); + const panel = document.querySelector('[data-testid="annotation-side-panel"]'); + expect(panel?.classList.contains('translate-x-0')).toBe(true); + expect(panel?.classList.contains('translate-x-full')).toBe(false); + }); +}); + +describe('AnnotationSidePanel – close button', () => { + it('calls onClose when the close button is clicked', async () => { + const onClose = vi.fn(); + render(AnnotationSidePanel, { ...baseProps, activeAnnotationId: 'ann-1', onClose }); + await page.getByRole('button', { name: /schließen/i }).click(); + expect(onClose).toHaveBeenCalledOnce(); + }); +}); + +describe('AnnotationSidePanel – targetCommentId forwarding', () => { + it('renders CommentThread when annotation is active', async () => { + render(AnnotationSidePanel, { + ...baseProps, + activeAnnotationId: 'ann-1', + targetCommentId: 'comment-42' + }); + // CommentThread renders inside the panel when activeAnnotationId is set + const panel = document.querySelector('[data-testid="annotation-side-panel"]'); + expect(panel).not.toBeNull(); + expect(panel?.classList.contains('translate-x-0')).toBe(true); + }); + + it('does not render CommentThread when annotation is null', async () => { + render(AnnotationSidePanel, { + ...baseProps, + activeAnnotationId: null, + targetCommentId: 'comment-42' + }); + // Panel is hidden and no fetch should have been triggered for comments + const panel = document.querySelector('[data-testid="annotation-side-panel"]'); + expect(panel?.classList.contains('translate-x-full')).toBe(true); + }); +}); diff --git a/frontend/src/lib/utils/mention.spec.ts b/frontend/src/lib/utils/mention.spec.ts index 301cbabe..5a7982be 100644 --- a/frontend/src/lib/utils/mention.spec.ts +++ b/frontend/src/lib/utils/mention.spec.ts @@ -96,6 +96,7 @@ describe('renderBody', () => { const mentions: MentionDTO[] = [{ id: 'uuid-1', firstName: 'Hans', lastName: 'Müller' }]; const result = renderBody('Hey @Hans Müller!', mentions); expect(result).toContain(' ({ PUBLIC_NOTIFICATION_POLL_MS: '60000' })); + afterEach(cleanup); const emptySnippet = createRawSnippet(() => ({ render: () => '' }));