test(transcription): restore strong one-fetch regression guard
Sara on PR #629 round 3: the round-2 fix captured the fetch count AFTER typing '@', so a regression that re-introduced the legacy per-keystroke items() callback would have its '@'-keystroke fetch silently absorbed into the baseline. Drop the baseline subtraction and count every /api/persons fetch since render — typing '@' + fill('Walter') must total exactly one fetch. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -219,30 +219,36 @@ describe('PersonMentionEditor — AC-2/3: search input drives fetch', () => {
|
|||||||
expect(fetchesAfterSearch).toBe(1);
|
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
|
const fetchMock = vi
|
||||||
.fn()
|
.fn()
|
||||||
.mockResolvedValue({ ok: true, json: vi.fn().mockResolvedValue([AUGUSTE]) });
|
.mockResolvedValue({ ok: true, json: vi.fn().mockResolvedValue([AUGUSTE]) });
|
||||||
vi.stubGlobal('fetch', fetchMock);
|
vi.stubGlobal('fetch', fetchMock);
|
||||||
renderHost();
|
renderHost();
|
||||||
|
|
||||||
// Open the dropdown first so the search input is reachable. `fill` then
|
// Open the dropdown, then drive the search input via fill() — sidesteps
|
||||||
// drives the searchbox in one input event — sidesteps per-keystroke
|
// per-keystroke timing of userEvent.type that Sara flagged round 2.
|
||||||
// debounce timing on CI that Sara flagged on PR #629 round 2.
|
|
||||||
await userEvent.type(page.getByRole('textbox'), '@');
|
await userEvent.type(page.getByRole('textbox'), '@');
|
||||||
await vi.waitFor(async () => {
|
await vi.waitFor(async () => {
|
||||||
await expect.element(page.getByRole('searchbox')).toBeVisible();
|
await expect.element(page.getByRole('searchbox')).toBeVisible();
|
||||||
});
|
});
|
||||||
|
|
||||||
const fetchesBeforeSearch = fetchMock.mock.calls.length;
|
|
||||||
|
|
||||||
await page.getByRole('searchbox').fill('Walter');
|
await page.getByRole('searchbox').fill('Walter');
|
||||||
|
|
||||||
await new Promise((r) => setTimeout(r, SEARCH_DEBOUNCE_MS + POST_DEBOUNCE_SLACK_MS));
|
await new Promise((r) => setTimeout(r, SEARCH_DEBOUNCE_MS + POST_DEBOUNCE_SLACK_MS));
|
||||||
|
|
||||||
const personsFetches = fetchMock.mock.calls
|
// No baseline subtraction — count ALL /api/persons fetches since render.
|
||||||
.slice(fetchesBeforeSearch)
|
// If the legacy per-keystroke items() callback returns, typing `@` alone
|
||||||
.filter(([url]) => typeof url === 'string' && url.startsWith('/api/persons'));
|
// 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);
|
expect(personsFetches.length).toBe(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user