fix(shell): geschlossenes Spenden-Banner kommt beim Sprachwechsel auch ohne localStorage nicht zurück
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>
This commit is contained in:
1 parent
af05e5f1f1
commit
0bcdfe258d
3 files changed
+74
-11
No files matched your search
@@ -1,14 +1,19 @@
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
import { setLang } from '../i18n/i18n';
|
||||
import { mountDonateBanner } from './donate-banner';
|
||||
|
||||
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(() => {
|
||||
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;
|
||||
@@ -16,8 +21,26 @@ beforeEach(() => {
|
||||
|
||||
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, '');
|
||||
@@ -68,19 +91,39 @@ describe('mountDonateBanner', () => {
|
||||
});
|
||||
|
||||
it('localStorage wirft beim Lesen: Banner wird gezeigt', () => {
|
||||
vi.spyOn(Storage.prototype, 'getItem').mockImplementation(() => {
|
||||
throw new DOMException('blockiert', 'SecurityError');
|
||||
});
|
||||
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', () => {
|
||||
vi.spyOn(Storage.prototype, 'setItem').mockImplementation(() => {
|
||||
throw new DOMException('voll', 'QuotaExceededError');
|
||||
});
|
||||
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);
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user