diff --git a/apps/web/src/components/bug-report/bug-report-button.test.tsx b/apps/web/src/components/bug-report/bug-report-button.test.tsx index f7282e6..09c6294 100644 --- a/apps/web/src/components/bug-report/bug-report-button.test.tsx +++ b/apps/web/src/components/bug-report/bug-report-button.test.tsx @@ -349,6 +349,82 @@ describe('BugReportButton (quick-260914-m97)', () => { expect(body.get('clientVersion')).toBe(''); expect(body.get('clientCommit')).toBe(''); }); + + /** + * Test 14/15 (quick-260921-ldf) sind der Nachweis fuer die Ursache des + * Wackeltests aus CI-Lauf 395. Der Dialog zog seinen Anfangszustand frueher + * per `useEffect` nach; React schrieb deshalb bei JEDEM Oeffnen zuerst einen + * falschen DOM-Zustand fest und korrigierte ihn erst einen Commit spaeter. + * Gemessen hielt der falsche Zustand zwei volle Makrotask-Runden — lange + * genug, dass der Browser ihn zeichnen konnte und der Test ihn gelegentlich + * sah. Die beiden Tests beobachten jeden Commit und dulden keinen einzigen + * falschen. + */ + function protokolliereCommits(): { commits: string[]; stop: () => void } { + const commits: string[] = []; + const obs = new MutationObserver(() => { + const dialog = document.querySelector('[role="dialog"]'); + if (!dialog) return; + const bild = dialog.querySelector('img'); + const box = document.getElementById('bug-report-attach') as HTMLInputElement | null; + const feld = document.getElementById('bug-report-description') as HTMLTextAreaElement | null; + const danke = dialog.textContent?.includes(T.sent) ?? false; + commits.push( + `bild=${bild ? 'ja' : 'nein'} haekchen=${box ? (box.checked ? 'an' : 'aus') : '-'} ` + + `gesperrt=${box ? box.disabled : '-'} text="${feld?.value ?? ''}" danke=${danke}`, + ); + }); + obs.observe(document.body, { childList: true, subtree: true, attributes: true }); + return { commits, stop: () => obs.disconnect() }; + } + + it('Test 14 (quick-260921-ldf): kein falscher Zwischenzustand — in KEINEM Commit steht das Vorschaubild mit ausgeschaltetem Haekchen', async () => { + const { commits, stop } = protokolliereCommits(); + try { + const user = userEvent.setup(); + await renderButton(); + await openDialog(user); + } finally { + stop(); + } + + expect(commits.length).toBeGreaterThan(0); + const falsche = commits.filter((c) => c.includes('bild=ja') && c.includes('haekchen=aus') && c.includes('gesperrt=false')); + expect(falsche).toEqual([]); + // Und der allererste Commit mit Dialog ist schon der richtige. + expect(commits[0]).toContain('bild=ja'); + expect(commits[0]).toContain('haekchen=an'); + }); + + it('Test 15 (quick-260921-ldf): erneutes Oeffnen startet frisch — kein alter Dankestext, kein alter Text, Haekchen wieder an', async () => { + mockFetch.mockResolvedValue(jsonResponse(200, { sent: true })); + const user = userEvent.setup(); + await renderButton(); + + // Erstes Oeffnen: Haekchen abwaehlen, Text tippen, senden, schliessen. + await openDialog(user); + await user.click(screen.getByRole('checkbox', { name: T.attachScreenshot })); + await user.type(screen.getByLabelText(T.descriptionLabel), 'alter Text'); + await user.click(screen.getByRole('button', { name: T.send })); + await screen.findByText(T.sent); + await user.click(screen.getByRole('button', { name: T.close })); + await waitFor(() => expect(screen.queryByRole('dialog')).toBeNull()); + + // Zweites Oeffnen: jeder Commit muss frisch sein. + const { commits, stop } = protokolliereCommits(); + try { + await openDialog(user); + } finally { + stop(); + } + + expect(commits.length).toBeGreaterThan(0); + expect(commits.filter((c) => c.includes('danke=true'))).toEqual([]); + expect(commits.filter((c) => c.includes('text="alter Text"'))).toEqual([]); + expect(commits.filter((c) => c.includes('bild=ja') && c.includes('haekchen=aus') && c.includes('gesperrt=false'))).toEqual([]); + expect(screen.getByRole('checkbox', { name: T.attachScreenshot })).toBeChecked(); + expect(screen.getByLabelText(T.descriptionLabel)).toHaveValue(''); + }); }); describe('computeCaptureSize (quick-260914-m97, reine Funktion)', () => {