diff --git a/frontend/src/lib/shared/discussion/PersonMentionEditor.svelte.spec.ts b/frontend/src/lib/shared/discussion/PersonMentionEditor.svelte.spec.ts index 0620ecfb..6d43d56b 100644 --- a/frontend/src/lib/shared/discussion/PersonMentionEditor.svelte.spec.ts +++ b/frontend/src/lib/shared/discussion/PersonMentionEditor.svelte.spec.ts @@ -219,30 +219,36 @@ describe('PersonMentionEditor — AC-2/3: search input drives fetch', () => { expect(fetchesAfterSearch).toBe(1); }); - it('fires exactly one /api/persons fetch when the user searches for Walter (debounced)', async () => { + it('fires exactly one /api/persons fetch when the user searches for Walter (regression guard)', async () => { + // Regression guard: a previous version of PersonMentionEditor had a + // duplicated `items()` callback in the Tiptap suggestion config that + // fetched per-keystroke in addition to the debounced search-input fetch + // (Markus & Felix round-1). To catch that regression, we must NOT + // subtract any baseline — every fetch from render onwards counts. + // Sara on PR #629 round 3. const fetchMock = vi .fn() .mockResolvedValue({ ok: true, json: vi.fn().mockResolvedValue([AUGUSTE]) }); vi.stubGlobal('fetch', fetchMock); renderHost(); - // Open the dropdown first so the search input is reachable. `fill` then - // drives the searchbox in one input event — sidesteps per-keystroke - // debounce timing on CI that Sara flagged on PR #629 round 2. + // Open the dropdown, then drive the search input via fill() — sidesteps + // per-keystroke timing of userEvent.type that Sara flagged round 2. await userEvent.type(page.getByRole('textbox'), '@'); await vi.waitFor(async () => { await expect.element(page.getByRole('searchbox')).toBeVisible(); }); - - const fetchesBeforeSearch = fetchMock.mock.calls.length; - await page.getByRole('searchbox').fill('Walter'); await new Promise((r) => setTimeout(r, SEARCH_DEBOUNCE_MS + POST_DEBOUNCE_SLACK_MS)); - const personsFetches = fetchMock.mock.calls - .slice(fetchesBeforeSearch) - .filter(([url]) => typeof url === 'string' && url.startsWith('/api/persons')); + // No baseline subtraction — count ALL /api/persons fetches since render. + // If the legacy per-keystroke items() callback returns, typing `@` alone + // would already produce one fetch and `fill('Walter')` another, breaking + // this assertion. + const personsFetches = fetchMock.mock.calls.filter( + ([url]) => typeof url === 'string' && url.startsWith('/api/persons') + ); expect(personsFetches.length).toBe(1); });