/** * Der Block „Diese Arbeit unterstützen“ in „Über diese Software“. * * Geprüft wird beides, und das ist der Punkt: der Zustand von heute (keine * Adresse eingetragen – das Angebot fehlt vollständig) und der von morgen * (eine Adresse eingetragen – der Knopf öffnet genau sie). Die Komponente * bekommt das Ziel deshalb als Eigenschaft; die eine Stelle, an der die * Adresse steht, ist `shared/unterstuetzung.ts` und wird in * `tests/unterstuetzung.test.ts` geprüft. * * Das Angebot ist eine Bitte und kein Handel: Es schaltet nichts frei, es * nennt keinen Betrag, und es kommt von selbst nie wieder. Die letzten beiden * Zusagen stehen unten als eigene Prüfungen – sie sind der Grund, warum ein * Spendenknopf in dieser Anwendung überhaupt vertretbar ist. */ import { render, screen, waitFor, within } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import axe from 'axe-core'; import { afterEach, describe, expect, it, vi } from 'vitest'; import { Unterstuetzung } from '../src/renderer/src/components/ueber/Unterstuetzung'; import { KONTAKT } from '../src/shared/kontakt'; import { jsdomOptionen } from '../src/shared/wcag'; import { UNTERSTUETZUNG_KNOPF, UNTERSTUETZUNG_TITEL } from '../src/shared/unterstuetzung'; /** Eine erfundene, aber formgerechte Adresse – die echte steht noch nicht fest. */ const ZIEL = 'https://beispiel.example/unterstuetzen'; /** Attrappe des einzigen Kanals, den dieser Block braucht. */ function bruecke(oeffnen: (url: string) => Promise): void { vi.stubGlobal('lernApp', { unterstuetzungOeffnen: vi.fn(oeffnen) }); } function knopf(): HTMLElement { return screen.getByRole('button', { name: UNTERSTUETZUNG_KNOPF }); } afterEach(() => { vi.unstubAllGlobals(); }); describe('Unterstützungsangebot – wann es erscheint', () => { it('erscheint, sobald eine Adresse eingetragen ist', () => { bruecke(() => Promise.resolve(true)); render(); expect( screen.getByRole('heading', { name: UNTERSTUETZUNG_TITEL, level: 2 }), ).toBeInTheDocument(); expect(knopf()).toBeInTheDocument(); // Wer gleich den Browser verlässt, soll vorher lesen können, wohin. expect(screen.getByText(ZIEL)).toBeInTheDocument(); }); it('fehlt vollständig, wenn keine Adresse eingetragen ist', () => { /* Vollständig heißt: kein Knopf, keine Überschrift, kein leerer Block und auch keine Zeile „zurzeit nicht verfügbar“. Ein toter Knopf sagt zu, dass es dort weitergeht; eine Erklärung, warum gerade nichts geht, ist eine Auskunft über den Betreiber und keine über die Anwendung. */ bruecke(() => Promise.resolve(true)); const { container } = render(); expect(container).toBeEmptyDOMElement(); expect(screen.queryByRole('button')).toBeNull(); expect(screen.queryByRole('heading')).toBeNull(); }); it('fehlt vollständig, wenn der Anwendungskern den Kanal nicht anbietet', () => { /* Der Anwendungskern kann älter sein als die Oberfläche. Ein Knopf, der auf Druck erklären müsste, dass er nicht kann, wäre eine Zusage ohne Deckung – dieselbe Regel wie beim Kopierknopf im Meldeblock. */ vi.stubGlobal('lernApp', {}); const { container } = render(); expect(container).toBeEmptyDOMElement(); }); it('fehlt vollständig, wenn gar keine Brücke da ist', () => { /* Im Browser ohne Preload – etwa in diesen Tests, wenn niemand eine Attrappe setzt. */ const { container } = render(); expect(container).toBeEmptyDOMElement(); }); }); describe('Unterstützungsangebot – was der Knopf tut', () => { it('öffnet genau die angezeigte Adresse und keine andere', async () => { const oeffnen = vi.fn<(url: string) => Promise>().mockResolvedValue(true); vi.stubGlobal('lernApp', { unterstuetzungOeffnen: oeffnen }); const nutzer = userEvent.setup(); render(); await nutzer.click(knopf()); /* Auf die Aufrufliste und nicht auf `toHaveBeenCalledWith`: So fällt auch ein zweiter Aufruf mit einer anderen Adresse auf. */ expect(oeffnen.mock.calls).toEqual([[ZIEL]]); }); it('geht ausschließlich über den Kanal des Hauptprozesses', async () => { /* Die Anwendung öffnet von sich aus keine Verbindung nach außen – kein `fetch`, kein `net.request`, und auch kein `window.open`, das an der Prüfung im Hauptprozess vorbeiliefe. Der Knopf ist ein `