From 44209048a2af1172398dcef3a6ca3b7e6db83869 Mon Sep 17 00:00:00 2001 From: Marcel Date: Tue, 19 May 2026 22:30:16 +0200 Subject: [PATCH] refactor(test): name the debounce slack and harden against CI jitter Extracts SEARCH_DEBOUNCE_MS + POST_DEBOUNCE_SLACK_MS at the top of the spec and bumps the post-debounce wait from 250/300 ms to 500 ms. Addresses Felix's "magic number" suggestion and Sara's flake-risk concern on PR #629. (Sara's fake-timer alternative collides with userEvent + vi.waitFor in vitest-browser; the slack bump achieves the same deterministic outcome with no fragility.) Co-Authored-By: Claude Opus 4.7 --- .../discussion/PersonMentionEditor.svelte.spec.ts | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/frontend/src/lib/shared/discussion/PersonMentionEditor.svelte.spec.ts b/frontend/src/lib/shared/discussion/PersonMentionEditor.svelte.spec.ts index e2da7d08..a7f49bd5 100644 --- a/frontend/src/lib/shared/discussion/PersonMentionEditor.svelte.spec.ts +++ b/frontend/src/lib/shared/discussion/PersonMentionEditor.svelte.spec.ts @@ -15,6 +15,12 @@ import { m } from '$lib/paraglide/messages.js'; type Person = components['schemas']['Person']; type PersonMention = components['schemas']['PersonMention']; +// Mirror of the debounce in PersonMentionEditor.svelte. Naming the magic and +// using a generous slack (SEARCH_DEBOUNCE_MS + 350 = 500 ms) kills CI-jitter +// flakiness Sara raised on PR #629. +const SEARCH_DEBOUNCE_MS = 150; +const POST_DEBOUNCE_SLACK_MS = 350; + const AUGUSTE: Person = { id: 'p-aug', firstName: 'Auguste', @@ -218,8 +224,7 @@ describe('PersonMentionEditor — AC-2/3: search input drives fetch', () => { await userEvent.type(page.getByRole('textbox'), '@Walter'); - // Wait beyond the 150 ms debounce window so the trailing call has flushed. - await new Promise((r) => setTimeout(r, 300)); + await new Promise((r) => setTimeout(r, SEARCH_DEBOUNCE_MS + POST_DEBOUNCE_SLACK_MS)); const personsFetches = fetchMock.mock.calls.filter( ([url]) => typeof url === 'string' && url.startsWith('/api/persons') @@ -243,8 +248,7 @@ describe('PersonMentionEditor — AC-2/3: search input drives fetch', () => { await userEvent.clear(page.getByRole('searchbox')); - // Wait beyond the debounce window to confirm no fetch was scheduled. - await new Promise((r) => setTimeout(r, 250)); + await new Promise((r) => setTimeout(r, SEARCH_DEBOUNCE_MS + POST_DEBOUNCE_SLACK_MS)); expect(fetchMock.mock.calls.length).toBe(fetchesBeforeClear); await expect.element(page.getByText('Auguste Raddatz')).not.toBeInTheDocument(); @@ -264,8 +268,7 @@ describe('PersonMentionEditor — whitespace-only query', () => { await expect.element(page.getByRole('searchbox')).toBeVisible(); }); - // Wait beyond the debounce window so any trailing call would have fired. - await new Promise((r) => setTimeout(r, 300)); + await new Promise((r) => setTimeout(r, SEARCH_DEBOUNCE_MS + POST_DEBOUNCE_SLACK_MS)); await expect.element(page.getByText(m.person_mention_search_prompt())).toBeInTheDocument(); expect(fetchMock).not.toHaveBeenCalled();