From 151e340fdda010c620f7b47fcdc44fdbe1689d66 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 5 Oct 2026 07:26:18 +0000 Subject: [PATCH] fix(account): Benutzernamenfeld mit autocomplete="nickname" statt "username" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Angemeldet wird per E-Mail: Das Namensfeld der Registrierung und das Feld im Namensdialog sind ein Spitzname, kein Anmeldename. Mit `username` würden Passwortmanager den Namen statt der E-Mail als Anmeldenamen speichern und später in die Anmeldung füllen. Tests angepasst, dazu einer, der prüft, dass in keiner Ansicht des Login-Dialogs ein Feld `autocomplete="username"` trägt. Co-Authored-By: Claude Opus 5.5 --- web/src/account/login-dialog.test.ts | 14 ++++++++++++-- web/src/account/login-dialog.ts | 3 ++- web/src/account/username-dialog.test.ts | 4 ++-- web/src/account/username-dialog.ts | 5 +++-- 4 files changed, 19 insertions(+), 7 deletions(-) diff --git a/web/src/account/login-dialog.test.ts b/web/src/account/login-dialog.test.ts index 50c256d..577bf44 100644 --- a/web/src/account/login-dialog.test.ts +++ b/web/src/account/login-dialog.test.ts @@ -74,7 +74,7 @@ describe('requireLogin: Öffnen und Schließen', () => { it('startet auf Wunsch mit Registrieren oder Passwort vergessen', () => { void requireLogin('register'); expect(title()).toBe('Registrieren'); - expect(input('username').autocomplete).toBe('username'); + expect(input('username').autocomplete).toBe('nickname'); expect(input('password').autocomplete).toBe('new-password'); expect(input('password-repeat').autocomplete).toBe('new-password'); }); @@ -289,7 +289,7 @@ describe('Registrieren: Benutzername', () => { const field = input('username'); expect(field.type).toBe('text'); expect(field.required).toBe(true); - expect(field.autocomplete).toBe('username'); + expect(field.autocomplete).toBe('nickname'); expect(field.getAttribute('autocapitalize')).toBe('none'); expect(field.getAttribute('spellcheck')).toBe('false'); expect(field.labels![0].textContent).toContain('Benutzername'); @@ -298,6 +298,16 @@ describe('Registrieren: Benutzername', () => { expect(document.activeElement).toBe(field); }); + it('kein Feld trägt autocomplete „username“: angemeldet wird per E-Mail, für Passwortmanager bleibt sie der Anmeldename', () => { + const tokens = () => [...dialogEl().querySelectorAll('input')].map((i) => i.autocomplete); + void requireLogin('register'); + expect(tokens()).toEqual(['nickname', 'email', 'new-password', 'new-password']); + buttonByText('Anmelden').click(); + expect(tokens()).toEqual(['email', 'current-password']); + buttonByText('Passwort vergessen?').click(); + expect(tokens()).toEqual(['email']); + }); + it('die Anmelden-Ansicht hat kein Namensfeld', () => { void requireLogin('login'); expect(names()).toEqual(['email', 'password']); diff --git a/web/src/account/login-dialog.ts b/web/src/account/login-dialog.ts index cdaf2b9..ad71b93 100644 --- a/web/src/account/login-dialog.ts +++ b/web/src/account/login-dialog.ts @@ -283,7 +283,8 @@ function createLoginDialog(): (view: LoginView) => Promise { } if (forView === 'register') { - const username = field(t('Benutzername'), 'username', 'text', 'username', draftUsername, 'auth-name-hint'); + // `nickname`, nicht `username`: angemeldet wird per E-Mail, und ein Passwortmanager soll das E-Mail-Feld als Anmeldenamen behalten. + const username = field(t('Benutzername'), 'username', 'text', 'nickname', draftUsername, 'auth-name-hint'); const email = field(t('E-Mail'), 'email', 'email', 'email', draftEmail); const password = field(t('Passwort'), 'password', 'password', 'new-password', '', 'auth-pw-hint'); const repeat = field(t('Passwort wiederholen'), 'password-repeat', 'password', 'new-password'); diff --git a/web/src/account/username-dialog.test.ts b/web/src/account/username-dialog.test.ts index 5773969..2a02a2d 100644 --- a/web/src/account/username-dialog.test.ts +++ b/web/src/account/username-dialog.test.ts @@ -81,9 +81,9 @@ describe('Dialog „Benutzername wählen“: Aufbau', () => { expect(buttonByText('Später')!.type).toBe('button'); }); - it('das Feld ist kein Anmeldefeld: keine Vorschläge des Browsers, keine Großschreibung, keine Rechtschreibprüfung', () => { + it('das Feld ist ein Spitzname, kein Anmeldename (autocomplete „nickname“, nie „username“): keine Großschreibung, keine Rechtschreibprüfung', () => { void dialog.usernameDialog({ mode: 'prompt' }); - expect(input().autocomplete).toBe('off'); + expect(input().autocomplete).toBe('nickname'); expect(input().getAttribute('autocapitalize')).toBe('none'); expect(input().getAttribute('spellcheck')).toBe('false'); expect(input().type).toBe('text'); diff --git a/web/src/account/username-dialog.ts b/web/src/account/username-dialog.ts index ec289de..f3b607a 100644 --- a/web/src/account/username-dialog.ts +++ b/web/src/account/username-dialog.ts @@ -49,8 +49,9 @@ function build(opts: UsernameDialogOptions): Promise { type: 'text', name: 'username', id: 'username-name', - // Kein Anmeldefeld: der Browser soll hier nicht die gespeicherte Anmeldung (E-Mail) vorschlagen. - autocomplete: 'off', + // `nickname`, nicht `username`: der Name ist ein Spitzname, kein Anmeldename (angemeldet wird per E-Mail); so bleibt für Passwortmanager + // das E-Mail-Feld der Anmeldung der Anmeldename, und der Browser schlägt hier nicht die gespeicherte Anmeldung vor. + autocomplete: 'nickname', autocapitalize: 'none', spellcheck: 'false', 'aria-required': 'true',