test(person-mention): drive editor specs via fake timers
Tester #5506 §1: 14 tests × 250ms real-timer waits = 3.5s wall-clock, also racing the 200ms internal debounce by only 50ms — a flake on a busy CI runner. Switch to vi.useFakeTimers + advanceTimersByTimeAsync; test execution now 236ms (was 3.08s), determinism guaranteed because the debounce runs against the fake clock. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { describe, it, expect, vi, afterEach } from 'vitest';
|
||||
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
||||
import { cleanup, render } from 'vitest-browser-svelte';
|
||||
import { page } from 'vitest/browser';
|
||||
import PersonMentionEditorHost from './PersonMentionEditor.test-host.svelte';
|
||||
@@ -7,8 +7,19 @@ import type { components } from '$lib/generated/api';
|
||||
type Person = components['schemas']['Person'];
|
||||
type PersonMention = components['schemas']['PersonMention'];
|
||||
|
||||
const waitForDebounce = () => new Promise((r) => setTimeout(r, 250));
|
||||
const tick = () => new Promise((r) => setTimeout(r, 0));
|
||||
// Editor's internal search debounce is 200ms — drive it via fake timers
|
||||
// so tests are deterministic and fast (Tester #5506 §1).
|
||||
const DEBOUNCE_MS = 200;
|
||||
|
||||
async function flushDebounce() {
|
||||
await vi.advanceTimersByTimeAsync(DEBOUNCE_MS);
|
||||
// Let the awaited fetch resolve and the resulting state assignments flush.
|
||||
await vi.runAllTimersAsync();
|
||||
}
|
||||
|
||||
async function tick() {
|
||||
await vi.advanceTimersByTimeAsync(0);
|
||||
}
|
||||
|
||||
const AUGUSTE: Person = {
|
||||
id: 'p-aug',
|
||||
@@ -73,9 +84,14 @@ function renderHost(initial: { value?: string; mentionedPersons?: PersonMention[
|
||||
};
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
vi.useFakeTimers({ shouldAdvanceTime: true });
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
cleanup();
|
||||
vi.unstubAllGlobals();
|
||||
vi.useRealTimers();
|
||||
});
|
||||
|
||||
// ─── Rendering ────────────────────────────────────────────────────────────────
|
||||
@@ -115,7 +131,7 @@ describe('PersonMentionEditor — typeahead', () => {
|
||||
ta.selectionEnd = 4;
|
||||
ta.dispatchEvent(new Event('input', { bubbles: true }));
|
||||
|
||||
await waitForDebounce();
|
||||
await flushDebounce();
|
||||
|
||||
await expect.element(page.getByText('Auguste Raddatz')).toBeInTheDocument();
|
||||
});
|
||||
@@ -131,7 +147,7 @@ describe('PersonMentionEditor — typeahead', () => {
|
||||
ta.selectionEnd = 4;
|
||||
ta.dispatchEvent(new Event('input', { bubbles: true }));
|
||||
|
||||
await waitForDebounce();
|
||||
await flushDebounce();
|
||||
|
||||
expect(fetchMock).toHaveBeenCalledWith(expect.stringContaining('/api/persons?q=Aug'));
|
||||
});
|
||||
@@ -146,7 +162,7 @@ describe('PersonMentionEditor — typeahead', () => {
|
||||
ta.selectionStart = 4;
|
||||
ta.selectionEnd = 4;
|
||||
ta.dispatchEvent(new Event('input', { bubbles: true }));
|
||||
await waitForDebounce();
|
||||
await flushDebounce();
|
||||
|
||||
await expect.element(page.getByText('* 1882 – † 1944')).toBeInTheDocument();
|
||||
});
|
||||
@@ -161,7 +177,7 @@ describe('PersonMentionEditor — typeahead', () => {
|
||||
ta.selectionStart = 4;
|
||||
ta.selectionEnd = 4;
|
||||
ta.dispatchEvent(new Event('input', { bubbles: true }));
|
||||
await waitForDebounce();
|
||||
await flushDebounce();
|
||||
|
||||
await expect.element(page.getByText('Keine Personen gefunden')).toBeInTheDocument();
|
||||
});
|
||||
@@ -176,7 +192,7 @@ describe('PersonMentionEditor — typeahead', () => {
|
||||
ta.selectionStart = 9;
|
||||
ta.selectionEnd = 9;
|
||||
ta.dispatchEvent(new Event('input', { bubbles: true }));
|
||||
await waitForDebounce();
|
||||
await flushDebounce();
|
||||
|
||||
await expect.element(page.getByText('Auguste Raddatz')).toBeInTheDocument();
|
||||
});
|
||||
@@ -195,7 +211,7 @@ describe('PersonMentionEditor — selecting a person', () => {
|
||||
ta.selectionStart = 4;
|
||||
ta.selectionEnd = 4;
|
||||
ta.dispatchEvent(new Event('input', { bubbles: true }));
|
||||
await waitForDebounce();
|
||||
await flushDebounce();
|
||||
|
||||
clickOption('p-aug');
|
||||
await tick();
|
||||
@@ -213,7 +229,7 @@ describe('PersonMentionEditor — selecting a person', () => {
|
||||
ta.selectionStart = 4;
|
||||
ta.selectionEnd = 4;
|
||||
ta.dispatchEvent(new Event('input', { bubbles: true }));
|
||||
await waitForDebounce();
|
||||
await flushDebounce();
|
||||
|
||||
clickOption('p-aug');
|
||||
await tick();
|
||||
@@ -236,7 +252,7 @@ describe('PersonMentionEditor — selecting a person', () => {
|
||||
ta.selectionStart = ta.value.length;
|
||||
ta.selectionEnd = ta.value.length;
|
||||
ta.dispatchEvent(new Event('input', { bubbles: true }));
|
||||
await waitForDebounce();
|
||||
await flushDebounce();
|
||||
|
||||
clickOption('p-aug');
|
||||
await tick();
|
||||
@@ -258,7 +274,7 @@ describe('PersonMentionEditor — keyboard navigation (B11b)', () => {
|
||||
ta.selectionStart = 2;
|
||||
ta.selectionEnd = 2;
|
||||
ta.dispatchEvent(new Event('input', { bubbles: true }));
|
||||
await waitForDebounce();
|
||||
await flushDebounce();
|
||||
|
||||
const optAuguste = document.querySelector(
|
||||
'[role="option"][data-test-person-id="p-aug"]'
|
||||
@@ -293,7 +309,7 @@ describe('PersonMentionEditor — keyboard navigation (B11b)', () => {
|
||||
ta.selectionStart = 2;
|
||||
ta.selectionEnd = 2;
|
||||
ta.dispatchEvent(new Event('input', { bubbles: true }));
|
||||
await waitForDebounce();
|
||||
await flushDebounce();
|
||||
|
||||
ta.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowDown', bubbles: true }));
|
||||
await tick();
|
||||
@@ -315,7 +331,7 @@ describe('PersonMentionEditor — keyboard navigation (B11b)', () => {
|
||||
ta.selectionStart = 4;
|
||||
ta.selectionEnd = 4;
|
||||
ta.dispatchEvent(new Event('input', { bubbles: true }));
|
||||
await waitForDebounce();
|
||||
await flushDebounce();
|
||||
|
||||
ta.dispatchEvent(new KeyboardEvent('keydown', { key: 'Escape', bubbles: true }));
|
||||
await tick();
|
||||
@@ -339,7 +355,7 @@ describe('PersonMentionEditor — touch target', () => {
|
||||
ta.selectionStart = 4;
|
||||
ta.selectionEnd = 4;
|
||||
ta.dispatchEvent(new Event('input', { bubbles: true }));
|
||||
await waitForDebounce();
|
||||
await flushDebounce();
|
||||
|
||||
const option = document.querySelector('[role="option"]') as HTMLElement;
|
||||
expect(option).not.toBeNull();
|
||||
|
||||
Reference in New Issue
Block a user