fix(account): Benutzernamenfeld mit autocomplete="nickname" statt "username"

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 <noreply@anthropic.com>
This commit is contained in:
ClaudeandClaude Opus 5.5 committed 2026-10-05 07:26:18 +00:00
1 parent 3e4bc21f00
commit 151e340fdd
4 files changed
+19 -7

No files matched your search

+12 -2
View File
@@ -74,7 +74,7 @@ describe('requireLogin: Öffnen und Schließen', () => {
it('startet auf Wunsch mit Registrieren oder Passwort vergessen', () => { it('startet auf Wunsch mit Registrieren oder Passwort vergessen', () => {
void requireLogin('register'); void requireLogin('register');
expect(title()).toBe('Registrieren'); 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').autocomplete).toBe('new-password');
expect(input('password-repeat').autocomplete).toBe('new-password'); expect(input('password-repeat').autocomplete).toBe('new-password');
}); });
@@ -289,7 +289,7 @@ describe('Registrieren: Benutzername', () => {
const field = input('username'); const field = input('username');
expect(field.type).toBe('text'); expect(field.type).toBe('text');
expect(field.required).toBe(true); expect(field.required).toBe(true);
expect(field.autocomplete).toBe('username'); expect(field.autocomplete).toBe('nickname');
expect(field.getAttribute('autocapitalize')).toBe('none'); expect(field.getAttribute('autocapitalize')).toBe('none');
expect(field.getAttribute('spellcheck')).toBe('false'); expect(field.getAttribute('spellcheck')).toBe('false');
expect(field.labels![0].textContent).toContain('Benutzername'); expect(field.labels![0].textContent).toContain('Benutzername');
@@ -298,6 +298,16 @@ describe('Registrieren: Benutzername', () => {
expect(document.activeElement).toBe(field); 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', () => { it('die Anmelden-Ansicht hat kein Namensfeld', () => {
void requireLogin('login'); void requireLogin('login');
expect(names()).toEqual(['email', 'password']); expect(names()).toEqual(['email', 'password']);
+2 -1
View File
@@ -283,7 +283,8 @@ function createLoginDialog(): (view: LoginView) => Promise<boolean> {
} }
if (forView === 'register') { 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 email = field(t('E-Mail'), 'email', 'email', 'email', draftEmail);
const password = field(t('Passwort'), 'password', 'password', 'new-password', '', 'auth-pw-hint'); const password = field(t('Passwort'), 'password', 'password', 'new-password', '', 'auth-pw-hint');
const repeat = field(t('Passwort wiederholen'), 'password-repeat', 'password', 'new-password'); const repeat = field(t('Passwort wiederholen'), 'password-repeat', 'password', 'new-password');
+2 -2
View File
@@ -81,9 +81,9 @@ describe('Dialog „Benutzername wählen“: Aufbau', () => {
expect(buttonByText('Später')!.type).toBe('button'); 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' }); void dialog.usernameDialog({ mode: 'prompt' });
expect(input().autocomplete).toBe('off'); expect(input().autocomplete).toBe('nickname');
expect(input().getAttribute('autocapitalize')).toBe('none'); expect(input().getAttribute('autocapitalize')).toBe('none');
expect(input().getAttribute('spellcheck')).toBe('false'); expect(input().getAttribute('spellcheck')).toBe('false');
expect(input().type).toBe('text'); expect(input().type).toBe('text');
+3 -2
View File
@@ -49,8 +49,9 @@ function build(opts: UsernameDialogOptions): Promise<string | null> {
type: 'text', type: 'text',
name: 'username', name: 'username',
id: 'username-name', id: 'username-name',
// Kein Anmeldefeld: der Browser soll hier nicht die gespeicherte Anmeldung (E-Mail) vorschlagen. // `nickname`, nicht `username`: der Name ist ein Spitzname, kein Anmeldename (angemeldet wird per E-Mail); so bleibt für Passwortmanager
autocomplete: 'off', // das E-Mail-Feld der Anmeldung der Anmeldename, und der Browser schlägt hier nicht die gespeicherte Anmeldung vor.
autocomplete: 'nickname',
autocapitalize: 'none', autocapitalize: 'none',
spellcheck: 'false', spellcheck: 'false',
'aria-required': 'true', 'aria-required': 'true',