test(tag-input): rename waitForDebounce to waitForFetch and reduce to 50ms
fetchSuggestions has no debounce; the wait is purely for the async mock to resolve. The old name implied semantics that don't exist and added ~4.5s to the suite (13 uses × 350ms). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -3,7 +3,7 @@ import { cleanup, render } from 'vitest-browser-svelte';
|
|||||||
import { page, userEvent } from 'vitest/browser';
|
import { page, userEvent } from 'vitest/browser';
|
||||||
import TagInput, { type Tag } from './TagInput.svelte';
|
import TagInput, { type Tag } from './TagInput.svelte';
|
||||||
|
|
||||||
const waitForDebounce = () => new Promise((r) => setTimeout(r, 350));
|
const waitForFetch = () => new Promise((r) => setTimeout(r, 50));
|
||||||
const tick = () => new Promise((r) => setTimeout(r, 0));
|
const tick = () => new Promise((r) => setTimeout(r, 0));
|
||||||
|
|
||||||
function mockFetchWithTagObjects(tags: Tag[]) {
|
function mockFetchWithTagObjects(tags: Tag[]) {
|
||||||
@@ -168,7 +168,7 @@ describe('TagInput – autocomplete', () => {
|
|||||||
render(TagInput, { tags: [], allowCreation: true });
|
render(TagInput, { tags: [], allowCreation: true });
|
||||||
const input = page.getByRole('textbox');
|
const input = page.getByRole('textbox');
|
||||||
await input.fill('Fa');
|
await input.fill('Fa');
|
||||||
await waitForDebounce();
|
await waitForFetch();
|
||||||
await expect.element(page.getByRole('option', { name: 'Familie' })).toBeInTheDocument();
|
await expect.element(page.getByRole('option', { name: 'Familie' })).toBeInTheDocument();
|
||||||
await expect.element(page.getByRole('option', { name: 'Freunde' })).toBeInTheDocument();
|
await expect.element(page.getByRole('option', { name: 'Freunde' })).toBeInTheDocument();
|
||||||
await page.screenshot({ path: 'test-results/screenshots/tag-input-suggestions.png' });
|
await page.screenshot({ path: 'test-results/screenshots/tag-input-suggestions.png' });
|
||||||
@@ -179,7 +179,7 @@ describe('TagInput – autocomplete', () => {
|
|||||||
render(TagInput, { tags: [], allowCreation: true });
|
render(TagInput, { tags: [], allowCreation: true });
|
||||||
const input = page.getByRole('textbox');
|
const input = page.getByRole('textbox');
|
||||||
await input.fill('F');
|
await input.fill('F');
|
||||||
await waitForDebounce();
|
await waitForFetch();
|
||||||
expect(fetch).not.toHaveBeenCalled();
|
expect(fetch).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -188,7 +188,7 @@ describe('TagInput – autocomplete', () => {
|
|||||||
render(TagInput, { tags: [{ name: 'Familie' }], allowCreation: true });
|
render(TagInput, { tags: [{ name: 'Familie' }], allowCreation: true });
|
||||||
const input = page.getByRole('textbox');
|
const input = page.getByRole('textbox');
|
||||||
await input.fill('Fr');
|
await input.fill('Fr');
|
||||||
await waitForDebounce();
|
await waitForFetch();
|
||||||
await expect.element(page.getByRole('option', { name: 'Familie' })).not.toBeInTheDocument();
|
await expect.element(page.getByRole('option', { name: 'Familie' })).not.toBeInTheDocument();
|
||||||
await expect.element(page.getByRole('option', { name: 'Freunde' })).toBeInTheDocument();
|
await expect.element(page.getByRole('option', { name: 'Freunde' })).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
@@ -198,7 +198,7 @@ describe('TagInput – autocomplete', () => {
|
|||||||
render(TagInput, { tags: [], allowCreation: true });
|
render(TagInput, { tags: [], allowCreation: true });
|
||||||
const input = page.getByRole('textbox');
|
const input = page.getByRole('textbox');
|
||||||
await input.fill('Fa');
|
await input.fill('Fa');
|
||||||
await waitForDebounce();
|
await waitForFetch();
|
||||||
document.querySelector<HTMLElement>('[role="option"]')!.click();
|
document.querySelector<HTMLElement>('[role="option"]')!.click();
|
||||||
await tick();
|
await tick();
|
||||||
await expect.element(page.getByText('Familie')).toBeInTheDocument();
|
await expect.element(page.getByText('Familie')).toBeInTheDocument();
|
||||||
@@ -211,7 +211,7 @@ describe('TagInput – autocomplete', () => {
|
|||||||
render(TagInput, { tags: [], allowCreation: true });
|
render(TagInput, { tags: [], allowCreation: true });
|
||||||
const input = page.getByRole('textbox');
|
const input = page.getByRole('textbox');
|
||||||
await input.fill('__');
|
await input.fill('__');
|
||||||
await waitForDebounce();
|
await waitForFetch();
|
||||||
await userEvent.keyboard('{ArrowDown}'); // index 0 → Aachen
|
await userEvent.keyboard('{ArrowDown}'); // index 0 → Aachen
|
||||||
await userEvent.keyboard('{ArrowDown}'); // index 1 → Berlin
|
await userEvent.keyboard('{ArrowDown}'); // index 1 → Berlin
|
||||||
await userEvent.keyboard('{Enter}');
|
await userEvent.keyboard('{Enter}');
|
||||||
@@ -223,7 +223,7 @@ describe('TagInput – autocomplete', () => {
|
|||||||
render(TagInput, { tags: [], allowCreation: true });
|
render(TagInput, { tags: [], allowCreation: true });
|
||||||
const input = page.getByRole('textbox');
|
const input = page.getByRole('textbox');
|
||||||
await input.fill('Fa');
|
await input.fill('Fa');
|
||||||
await waitForDebounce();
|
await waitForFetch();
|
||||||
await expect.element(page.getByRole('option', { name: 'Familie' })).toBeInTheDocument();
|
await expect.element(page.getByRole('option', { name: 'Familie' })).toBeInTheDocument();
|
||||||
document.body.click();
|
document.body.click();
|
||||||
await tick();
|
await tick();
|
||||||
@@ -238,7 +238,7 @@ describe('TagInput – autocomplete', () => {
|
|||||||
render(TagInput, { tags: [], allowCreation: true });
|
render(TagInput, { tags: [], allowCreation: true });
|
||||||
const input = page.getByRole('textbox');
|
const input = page.getByRole('textbox');
|
||||||
await input.fill('El');
|
await input.fill('El');
|
||||||
await waitForDebounce();
|
await waitForFetch();
|
||||||
const options = Array.from(document.querySelectorAll('[role="option"]'));
|
const options = Array.from(document.querySelectorAll('[role="option"]'));
|
||||||
const texts = options.map((el) => el.textContent?.replace(/›/g, '').trim() ?? '');
|
const texts = options.map((el) => el.textContent?.replace(/›/g, '').trim() ?? '');
|
||||||
expect(texts.indexOf('Eltern')).toBeLessThan(texts.indexOf('Kind'));
|
expect(texts.indexOf('Eltern')).toBeLessThan(texts.indexOf('Kind'));
|
||||||
@@ -252,7 +252,7 @@ describe('TagInput – autocomplete', () => {
|
|||||||
render(TagInput, { tags: [], allowCreation: true });
|
render(TagInput, { tags: [], allowCreation: true });
|
||||||
const input = page.getByRole('textbox');
|
const input = page.getByRole('textbox');
|
||||||
await input.fill('Fa');
|
await input.fill('Fa');
|
||||||
await waitForDebounce();
|
await waitForFetch();
|
||||||
await expect.element(page.getByRole('listbox')).toBeInTheDocument();
|
await expect.element(page.getByRole('listbox')).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -269,7 +269,7 @@ describe('TagInput – tree-aware ordering', () => {
|
|||||||
render(TagInput, { tags: [], allowCreation: true });
|
render(TagInput, { tags: [], allowCreation: true });
|
||||||
const input = page.getByRole('textbox');
|
const input = page.getByRole('textbox');
|
||||||
await input.fill('Brief');
|
await input.fill('Brief');
|
||||||
await waitForDebounce();
|
await waitForFetch();
|
||||||
const options = Array.from(document.querySelectorAll('[role="option"]'));
|
const options = Array.from(document.querySelectorAll('[role="option"]'));
|
||||||
const texts = options.map((el) => el.textContent?.replace(/›/g, '').trim() ?? '');
|
const texts = options.map((el) => el.textContent?.replace(/›/g, '').trim() ?? '');
|
||||||
expect(texts).toContain('Briefe');
|
expect(texts).toContain('Briefe');
|
||||||
@@ -287,7 +287,7 @@ describe('TagInput – tree-aware ordering', () => {
|
|||||||
render(TagInput, { tags: [], allowCreation: true });
|
render(TagInput, { tags: [], allowCreation: true });
|
||||||
const input = page.getByRole('textbox');
|
const input = page.getByRole('textbox');
|
||||||
await input.fill('Hochzeit');
|
await input.fill('Hochzeit');
|
||||||
await waitForDebounce();
|
await waitForFetch();
|
||||||
const options = Array.from(document.querySelectorAll('[role="option"]'));
|
const options = Array.from(document.querySelectorAll('[role="option"]'));
|
||||||
const texts = options.map((el) => el.textContent?.replace(/›/g, '').trim() ?? '');
|
const texts = options.map((el) => el.textContent?.replace(/›/g, '').trim() ?? '');
|
||||||
expect(texts.indexOf('Briefe')).toBeLessThan(texts.indexOf('Hochzeit'));
|
expect(texts.indexOf('Briefe')).toBeLessThan(texts.indexOf('Hochzeit'));
|
||||||
@@ -305,7 +305,7 @@ describe('TagInput – tree-aware ordering', () => {
|
|||||||
render(TagInput, { tags: [], allowCreation: true });
|
render(TagInput, { tags: [], allowCreation: true });
|
||||||
const input = page.getByRole('textbox');
|
const input = page.getByRole('textbox');
|
||||||
await input.fill('Hochzeit');
|
await input.fill('Hochzeit');
|
||||||
await waitForDebounce();
|
await waitForFetch();
|
||||||
await userEvent.keyboard('{ArrowDown}'); // first item: Briefe (context ancestor)
|
await userEvent.keyboard('{ArrowDown}'); // first item: Briefe (context ancestor)
|
||||||
await userEvent.keyboard('{Enter}');
|
await userEvent.keyboard('{Enter}');
|
||||||
// successful add clears the input; if .tag unwrap is missing, addTag returns early
|
// successful add clears the input; if .tag unwrap is missing, addTag returns early
|
||||||
@@ -320,7 +320,7 @@ describe('TagInput – tree-aware ordering', () => {
|
|||||||
render(TagInput, { tags: [{ id: 'p1', name: 'Briefe' }], allowCreation: true });
|
render(TagInput, { tags: [{ id: 'p1', name: 'Briefe' }], allowCreation: true });
|
||||||
const input = page.getByRole('textbox');
|
const input = page.getByRole('textbox');
|
||||||
await input.fill('Fa');
|
await input.fill('Fa');
|
||||||
await waitForDebounce();
|
await waitForFetch();
|
||||||
await expect.element(page.getByRole('option', { name: 'Familienbriefe' })).toBeInTheDocument();
|
await expect.element(page.getByRole('option', { name: 'Familienbriefe' })).toBeInTheDocument();
|
||||||
// the already-selected parent must not appear as a suggestion
|
// the already-selected parent must not appear as a suggestion
|
||||||
const optionNames = Array.from(document.querySelectorAll('[role="option"]')).map(
|
const optionNames = Array.from(document.querySelectorAll('[role="option"]')).map(
|
||||||
@@ -356,7 +356,7 @@ describe('TagInput – onTextInput callback', () => {
|
|||||||
render(TagInput, { tags: [], allowCreation: false, onTextInput });
|
render(TagInput, { tags: [], allowCreation: false, onTextInput });
|
||||||
const input = page.getByRole('textbox');
|
const input = page.getByRole('textbox');
|
||||||
await input.fill('Ka');
|
await input.fill('Ka');
|
||||||
await waitForDebounce();
|
await waitForFetch();
|
||||||
const option = page.getByRole('option', { name: 'Kaufvertrag' });
|
const option = page.getByRole('option', { name: 'Kaufvertrag' });
|
||||||
await option.click();
|
await option.click();
|
||||||
await expect.poll(() => onTextInput.mock.calls.at(-1)).toEqual(['']);
|
await expect.poll(() => onTextInput.mock.calls.at(-1)).toEqual(['']);
|
||||||
|
|||||||
Reference in New Issue
Block a user