diff --git a/web/src/account/account-menu.test.ts b/web/src/account/account-menu.test.ts index f915684..4844193 100644 --- a/web/src/account/account-menu.test.ts +++ b/web/src/account/account-menu.test.ts @@ -262,6 +262,57 @@ describe('Konto-Menü: Benutzername', () => { expect(buttonByText('Benutzername ändern')).toBeDefined(); }); + it('meldet onUsernameChange denselben Namen noch einmal (Lesen des eigenen Profils nach dem Start), bleibt das Menü unangetastet: offen und mit dem Fokus', () => { + signedIn(); + mocks.name = 'Kenso'; + menu.ready(); + avatar()!.click(); + const before = { avatar: avatar(), panel: panel(), head: head() }; + const signOut = buttonByText('Abmelden')!; + signOut.focus(); + setName('u1', 'Kenso'); + expect(avatar()).toBe(before.avatar); // dieselben Elemente, nichts wurde neu gebaut + expect(panel()).toBe(before.panel); + expect(head()).toBe(before.head); + expect(panel()?.hidden).toBe(false); + expect(avatar()?.getAttribute('aria-expanded')).toBe('true'); + expect(document.activeElement).toBe(signOut); + expect(nameLine()?.textContent).toBe('@Kenso'); + }); + + it('ein anderer Name oder nur eine andere Schreibweise zeichnet dagegen neu', () => { + signedIn(); + mocks.name = 'kenso'; + menu.ready(); + const before = avatar(); + setName('u1', 'Kenso'); // „kenso“ → „Kenso“ ist erlaubt (Spec § 4) und soll zu sehen sein + expect(avatar()).not.toBe(before); + expect(nameLine()?.textContent).toBe('@Kenso'); + const second = avatar(); + setName('u1', 'Mia'); + expect(avatar()).not.toBe(second); + expect(nameLine()?.textContent).toBe('@Mia'); + }); + + it('die Namensmeldung eines anderen Kontos ändert nichts am Menü des angemeldeten', () => { + signedIn(); + mocks.name = 'Kenso'; // der Name des angemeldeten Kontos bleibt + menu.ready(); + avatar()!.click(); + const before = panel(); + for (const fn of [...mocks.nameListeners]) fn('u2', 'Mia'); // das Konto u2 hat sich umbenannt (etwa in einem eben beendeten Aufruf) + expect(panel()).toBe(before); + expect(panel()?.hidden).toBe(false); + expect(nameLine()?.textContent).toBe('@Kenso'); + }); + + it('abgemeldet bleibt der Anmelden-Knopf stehen, auch wenn ein Name gemeldet wird (das Konto, das speicherte, ist nicht mehr angemeldet)', () => { + menu.ready(); + const signIn = buttonByText('Anmelden'); + for (const fn of [...mocks.nameListeners]) fn('u2', 'Mia'); + expect(buttonByText('Anmelden')).toBe(signIn); + }); + it('nach dem Abmelden ist das Menü unabhängig vom Namen wieder „Anmelden“', () => { signedIn(); mocks.name = 'Kenso'; diff --git a/web/src/account/account-menu.ts b/web/src/account/account-menu.ts index 7205161..63fadaa 100644 --- a/web/src/account/account-menu.ts +++ b/web/src/account/account-menu.ts @@ -20,10 +20,14 @@ export function mountAccountMenu(slot: HTMLElement): AccountMenu { let isReady = false; // Hebt die Dokument-Listener des gerade offenen Menüs wieder auf (null: kein Menü gezeichnet). let detach: (() => void) | null = null; + // Der Name, den das gezeichnete Menü zeigt (null: keiner oder gar kein Konto-Menü). Meldet `onUsernameChange` denselben Namen noch einmal + // (etwa das Lesen des eigenen Profils nach dem Start), bleibt das Menü stehen: ein neu gebautes Dropdown wäre zu und verlöre den Fokus. + let shownName: string | null = null; function render(): void { detach?.(); detach = null; + shownName = null; if (!isReady) { slot.replaceChildren(); return; @@ -50,6 +54,7 @@ export function mountAccountMenu(slot: HTMLElement): AccountMenu { ); // Mit Benutzernamen steht „@name“ oben und die E-Mail klein darunter; ohne Namen (noch nicht gelesen oder nicht gewählt) nur die E-Mail. const name = myUsername(); + shownName = name; const email = el('span', { class: 'account-email', title: user.email }, user.email); const head = el( 'div', @@ -112,7 +117,10 @@ export function mountAccountMenu(slot: HTMLElement): AccountMenu { const off = onAuthChange(render); // Der Name steht fest oder ändert sich (gelesen nach dem Start, gespeichert im Dialog): das Menü zeigt gleich den neuen. - const offName = onUsernameChange(render); + const offName = onUsernameChange(() => { + if (myUsername() === shownName) return; // nichts Neues zu zeigen (derselbe Name, oder ein anderes Konto hat gespeichert) + render(); + }); render(); return {