feat(transcription): decouple @mention display text from person search (#380) #629

Open
marcel wants to merge 41 commits from feat/issue-380-decouple-mention-search into main
Showing only changes of commit 51cb8e7e22 - Show all commits

View File

@@ -8,6 +8,7 @@
import { describe, it, expect, vi, afterEach } from 'vitest';
import { cleanup, render } from 'vitest-browser-svelte';
import { page, userEvent } from 'vitest/browser';
import { tick } from 'svelte';
import PersonMentionEditorHost from './PersonMentionEditor.test-fixture.svelte';
import type { components } from '$lib/generated/api';
import { m } from '$lib/paraglide/messages.js';
@@ -18,7 +19,12 @@ import { SEARCH_DEBOUNCE_MS } from './mentionConstants';
type Person = components['schemas']['Person'];
type PersonMention = components['schemas']['PersonMention'];
// Slack on top of the debounce to absorb CI jitter (total 500 ms is generous).
/**
* Headroom above SEARCH_DEBOUNCE_MS for the debounce-window wait
* assertions in this file. 350 ms is calibrated against CI-runner jitter
* we observed pre-#629; dropping it below ~200 ms reintroduces flake.
* See PR #629 round-2 review comment #10935 (Sara).
*/
const POST_DEBOUNCE_SLACK_MS = 350;
const AUGUSTE: Person = {
@@ -275,6 +281,9 @@ describe('PersonMentionEditor — AC-2/3: search input drives fetch', () => {
await userEvent.clear(page.getByRole('searchbox'));
// Negative assertion: wait past the debounce window to confirm no
// trailing fetch was scheduled. Removing this wait would mask a
// re-introduction of the keystroke-driven items() fetch.
await new Promise((r) => setTimeout(r, SEARCH_DEBOUNCE_MS + POST_DEBOUNCE_SLACK_MS));
expect(fetchMock.mock.calls.length).toBe(fetchesBeforeClear);
@@ -332,7 +341,10 @@ describe('PersonMentionEditor — stale-response race', () => {
// The stale fetch now resolves with persons. The dropdown must stay empty.
resolveFetch({ ok: true, json: () => Promise.resolve([AUGUSTE]) });
await new Promise((r) => setTimeout(r, 50));
// Flush pending Svelte reactivity so any (non-)update from the stale
// fetch resolution has landed before we assert. expect.element already
// polls, so no fixed-timeout fallback is needed. Sara on PR #629 round 4.
await tick();
await expect.element(page.getByText('Auguste Raddatz')).not.toBeInTheDocument();
});
@@ -528,7 +540,10 @@ describe('PersonMentionEditor — AC-1: typed text as displayName', () => {
await vi.waitFor(() => {
expect(host.snapshot.mentionedPersons).toHaveLength(1);
expect(host.snapshot.mentionedPersons[0].displayName.length).toBeLessThanOrEqual(100);
// Tight assertion: input is 105 chars, cap is exactly 100. Using
// `toHaveLength(100)` discriminates "clip works" from "clip works
// AND nothing weakened it to e.g. 95". Sara on PR #629 round 4.
expect(host.snapshot.mentionedPersons[0].displayName).toHaveLength(100);
});
});