Das Banner merkte sich „geschlossen“ nur in localStorage. Bei gesperrtem Speicher (privates Fenster, gesperrte Website-Daten) baute der Neuaufbau des Rahmens beim Sprachwechsel (main.ts) es wieder auf. - donate-banner.ts: zusätzlich ein Merker im Speicher des Moduls (closedThisSession); er gilt neben localStorage. - Tests: donate-banner.test.ts lädt je Test eine frische Modulkopie (der Merker lebt im Modul); neue Fälle: gesperrter Speicher, erneutes Einhängen nach dem Schließen (bleibt weg) und ohne Schließen (kommt wieder); main.test.ts: Sprachwechsel nach dem Schließen bei gesperrtem Speicher. Die beiden schon vorhandenen Fälle zum gesperrten Speicher sperren jetzt wirklich (vi.stubGlobal): vi.spyOn(Storage.prototype, …) greift in happy-dom nicht, die Fälle waren leer. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
130 lines
5.2 KiB
TypeScript
130 lines
5.2 KiB
TypeScript
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
||
|
||
const KEY = 'aircard.donateBannerClosed';
|
||
const URL_ = 'https://buymeacoffee.com/aircard';
|
||
const TEXT = '☕ Gefällt dir AirCard-IOS? Spendier uns einen Kaffee – damit das Projekt weiterläuft.';
|
||
|
||
// Das Modul merkt sich im Speicher, dass das Banner geschlossen wurde (siehe unten): jeder Test bekommt darum eine frische Kopie von ihm
|
||
// und von i18n.ts (die Sprache wird an dieser Kopie gesetzt).
|
||
let mountDonateBanner: typeof import('./donate-banner').mountDonateBanner;
|
||
let setLang: typeof import('../i18n/i18n').setLang;
|
||
let slot: HTMLElement;
|
||
|
||
beforeEach(async () => {
|
||
vi.resetModules();
|
||
({ mountDonateBanner } = await import('./donate-banner'));
|
||
({ setLang } = await import('../i18n/i18n'));
|
||
localStorage.clear();
|
||
document.body.innerHTML = '<div id="donate-banner"></div>';
|
||
slot = document.getElementById('donate-banner') as HTMLElement;
|
||
});
|
||
|
||
afterEach(() => {
|
||
vi.restoreAllMocks();
|
||
vi.unstubAllGlobals();
|
||
});
|
||
|
||
/**
|
||
* Ein Speicher, der den Zugriff verweigert (privates Fenster, gesperrte Website-Daten). `vi.spyOn(Storage.prototype, …)` greift in
|
||
* happy-dom nicht, darum wird `localStorage` selbst ersetzt.
|
||
*/
|
||
function blockStorage(block: { read?: boolean; write?: boolean }): void {
|
||
const denied = () => new DOMException('blockiert', 'SecurityError');
|
||
vi.stubGlobal('localStorage', {
|
||
getItem: () => {
|
||
if (block.read) throw denied();
|
||
return null;
|
||
},
|
||
setItem: () => {
|
||
if (block.write) throw denied();
|
||
},
|
||
});
|
||
}
|
||
|
||
describe('mountDonateBanner', () => {
|
||
it('leere URL: nichts wird gerendert', () => {
|
||
mountDonateBanner(slot, '');
|
||
expect(slot.childElementCount).toBe(0);
|
||
mountDonateBanner(slot, ' ');
|
||
expect(slot.childElementCount).toBe(0);
|
||
});
|
||
|
||
it('zeigt Text, Link „Unterstützen →“ (neuer Tab) und Schließen-Knopf', () => {
|
||
mountDonateBanner(slot, URL_);
|
||
expect(slot.querySelector('.donate-banner-text')?.textContent).toBe(TEXT);
|
||
const link = slot.querySelector<HTMLAnchorElement>('a.donate-banner-link')!;
|
||
expect(link.textContent).toBe('Unterstützen →');
|
||
expect(link.getAttribute('href')).toBe(URL_);
|
||
expect(link.target).toBe('_blank');
|
||
expect(link.rel).toBe('noopener noreferrer');
|
||
expect(link.hasAttribute('data-link')).toBe(false);
|
||
expect(slot.querySelector('button')?.getAttribute('aria-label')).toBe('Banner schließen');
|
||
});
|
||
|
||
it('Englisch: Text, Link und Schließen-Knopf übersetzt, Zeichen (☕, →) und Link unverändert', () => {
|
||
setLang('en');
|
||
mountDonateBanner(slot, URL_);
|
||
expect(slot.querySelector('.donate-banner-text')?.textContent).toBe('☕ Enjoying AirCard-IOS? Buy us a coffee – it keeps the project running.');
|
||
const link = slot.querySelector<HTMLAnchorElement>('a.donate-banner-link')!;
|
||
expect(link.textContent).toBe('Support us →');
|
||
expect(link.getAttribute('href')).toBe(URL_);
|
||
expect(slot.querySelector('button')?.getAttribute('aria-label')).toBe('Close banner');
|
||
});
|
||
|
||
it('Schließen setzt das Flag und entfernt das Banner', () => {
|
||
mountDonateBanner(slot, URL_);
|
||
slot.querySelector('button')!.click();
|
||
expect(localStorage.getItem(KEY)).toBe('1');
|
||
expect(slot.childElementCount).toBe(0);
|
||
});
|
||
|
||
it('Flag gesetzt: nichts wird gerendert', () => {
|
||
localStorage.setItem(KEY, '1');
|
||
mountDonateBanner(slot, URL_);
|
||
expect(slot.childElementCount).toBe(0);
|
||
});
|
||
|
||
it('zweimal eingehängt gibt es nur ein Banner', () => {
|
||
mountDonateBanner(slot, URL_);
|
||
mountDonateBanner(slot, URL_);
|
||
expect(slot.querySelectorAll('.donate-banner')).toHaveLength(1);
|
||
});
|
||
|
||
it('localStorage wirft beim Lesen: Banner wird gezeigt', () => {
|
||
blockStorage({ read: true });
|
||
expect(() => mountDonateBanner(slot, URL_)).not.toThrow();
|
||
expect(slot.querySelectorAll('.donate-banner')).toHaveLength(1);
|
||
});
|
||
|
||
it('localStorage wirft beim Schreiben: Banner verschwindet trotzdem, kein Fehler', () => {
|
||
blockStorage({ write: true });
|
||
mountDonateBanner(slot, URL_);
|
||
expect(() => slot.querySelector('button')!.click()).not.toThrow();
|
||
expect(slot.childElementCount).toBe(0);
|
||
});
|
||
|
||
it('localStorage gesperrt: das geschlossene Banner kommt beim erneuten Einhängen (Sprachwechsel) nicht zurück', () => {
|
||
blockStorage({ read: true, write: true });
|
||
mountDonateBanner(slot, URL_);
|
||
slot.querySelector('button')!.click();
|
||
expect(slot.childElementCount).toBe(0);
|
||
mountDonateBanner(slot, URL_); // so baut main.ts den Rahmen bei einem Sprachwechsel neu auf
|
||
expect(slot.childElementCount).toBe(0);
|
||
});
|
||
|
||
it('ohne Schließen kommt das Banner beim erneuten Einhängen wieder, auch bei gesperrtem Speicher', () => {
|
||
blockStorage({ read: true, write: true });
|
||
mountDonateBanner(slot, URL_);
|
||
mountDonateBanner(slot, URL_); // neu aufgebaut, ohne dass jemand es geschlossen hat
|
||
expect(slot.querySelectorAll('.donate-banner')).toHaveLength(1);
|
||
});
|
||
|
||
it('der Speicher lässt sich schreiben, aber nicht lesen: nach dem Schließen bleibt das Banner ebenfalls weg', () => {
|
||
blockStorage({ read: true });
|
||
mountDonateBanner(slot, URL_);
|
||
slot.querySelector('button')!.click();
|
||
mountDonateBanner(slot, URL_);
|
||
expect(slot.childElementCount).toBe(0);
|
||
});
|
||
});
|