diff --git a/frontend/src/lib/document/transcription/TranscriptionEditView.svelte.spec.ts b/frontend/src/lib/document/transcription/TranscriptionEditView.svelte.spec.ts index 701cb288..ec53e911 100644 --- a/frontend/src/lib/document/transcription/TranscriptionEditView.svelte.spec.ts +++ b/frontend/src/lib/document/transcription/TranscriptionEditView.svelte.spec.ts @@ -1,6 +1,6 @@ import { describe, it, expect, vi, afterEach } from 'vitest'; import { cleanup, render } from 'vitest-browser-svelte'; -import { page } from 'vitest/browser'; +import { page, userEvent } from 'vitest/browser'; import TranscriptionEditView from './TranscriptionEditView.svelte'; import { createConfirmService, CONFIRM_KEY } from '$lib/shared/services/confirm.svelte.js'; @@ -148,24 +148,28 @@ describe('TranscriptionEditView — auto-save debounce', () => { }); it('passes the block mentionedPersons array as the 3rd save argument', async () => { - vi.useFakeTimers(); const onSaveBlock = vi.fn().mockResolvedValue(undefined); const blockWithMention = { ...block1, + // text must contain the @displayName token so deserialize() creates a mention node; + // fill() replaces the whole content with plain text and would destroy the node + text: '@Auguste Raddatz', mentionedPersons: [{ personId: 'p-aug', displayName: 'Auguste Raddatz' }] }; renderView({ blocks: [blockWithMention], onSaveBlock }); - const textarea = page.getByRole('textbox').first(); - await textarea.fill('Hallo @Auguste Raddatz'); + // type() focuses the element (cursor at position 0) then inserts without replacing the + // existing mention node. Fake timers interfere with keyboard CDP so use real timers + // + vi.waitFor to catch the 1500 ms debounce. + await userEvent.type(page.getByRole('textbox').first(), 'Hallo '); - vi.advanceTimersByTime(1500); - await vi.runAllTimersAsync(); - - expect(onSaveBlock).toHaveBeenCalledWith('b1', 'Hallo @Auguste Raddatz', [ - { personId: 'p-aug', displayName: 'Auguste Raddatz' } - ]); - vi.useRealTimers(); + await vi.waitFor( + () => + expect(onSaveBlock).toHaveBeenCalledWith('b1', 'Hallo @Auguste Raddatz', [ + { personId: 'p-aug', displayName: 'Auguste Raddatz' } + ]), + { timeout: 3000 } + ); }); it('resets debounce timer on rapid successive changes', async () => { @@ -238,8 +242,9 @@ describe('TranscriptionEditView — flush on blur', () => { const textarea = page.getByRole('textbox').first(); await textarea.fill('Blur text'); - // Blur before 1500ms debounce fires — locator.blur() not available, use native DOM - const el = document.querySelector('textarea') as HTMLTextAreaElement; + // Blur before 1500ms debounce fires — locator.blur() not available, use native DOM. + // PersonMentionEditor uses a contenteditable div (role=textbox), not a