mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-07-09 16:00:59 +00:00
parent
1d7fa6c4bf
commit
6f9f310ba6
2 changed files with 44 additions and 7 deletions
|
|
@ -188,14 +188,16 @@ export const Login: Component<LoginProps> = (props) => {
|
|||
setError('');
|
||||
setLoading(true);
|
||||
|
||||
// Read values directly from the form DOM to handle password manager autofill
|
||||
// Password managers may fill fields without triggering input events,
|
||||
// leaving the SolidJS signals empty while the DOM has the actual values
|
||||
// Read values directly from the form DOM to handle password manager and
|
||||
// browser form-state restores that do not trigger Solid input/change events.
|
||||
const form = e.currentTarget as HTMLFormElement;
|
||||
const usernameInput = form.querySelector('#username') as HTMLInputElement;
|
||||
const passwordInput = form.querySelector('#password') as HTMLInputElement;
|
||||
const rememberInput = form.querySelector('#remember-me') as HTMLInputElement;
|
||||
const usernameValue = usernameInput?.value || username();
|
||||
const passwordValue = passwordInput?.value || password();
|
||||
const rememberLogin = rememberInput?.checked ?? rememberMe();
|
||||
setRememberMe(rememberLogin);
|
||||
|
||||
// Validate that we have credentials before attempting login
|
||||
if (!usernameValue || !passwordValue) {
|
||||
|
|
@ -215,7 +217,7 @@ export const Login: Component<LoginProps> = (props) => {
|
|||
body: JSON.stringify({
|
||||
username: usernameValue,
|
||||
password: passwordValue,
|
||||
rememberMe: rememberMe(),
|
||||
rememberMe: rememberLogin,
|
||||
}),
|
||||
skipAuth: true,
|
||||
});
|
||||
|
|
@ -229,7 +231,7 @@ export const Login: Component<LoginProps> = (props) => {
|
|||
} catch (_err) {
|
||||
// Ignore storage failures (private browsing, etc.)
|
||||
}
|
||||
persistRememberedUsername(usernameValue, rememberMe());
|
||||
persistRememberedUsername(usernameValue, rememberLogin);
|
||||
props.onLogin();
|
||||
} else if (response.status === 403) {
|
||||
// Account is locked
|
||||
|
|
@ -241,7 +243,7 @@ export const Login: Component<LoginProps> = (props) => {
|
|||
setError(data.message || 'Account temporarily locked due to too many failed attempts.');
|
||||
}
|
||||
// Clear the input fields
|
||||
if (!rememberMe()) setUsername('');
|
||||
if (!rememberLogin) setUsername('');
|
||||
setPassword('');
|
||||
} else if (response.status === 429) {
|
||||
// Rate limited
|
||||
|
|
@ -258,7 +260,7 @@ export const Login: Component<LoginProps> = (props) => {
|
|||
setError(data.message || 'Invalid username or password');
|
||||
}
|
||||
// Clear the input fields
|
||||
if (!rememberMe()) setUsername('');
|
||||
if (!rememberLogin) setUsername('');
|
||||
setPassword('');
|
||||
} else {
|
||||
setError(data.message || 'Server error. Please try again.');
|
||||
|
|
|
|||
|
|
@ -242,6 +242,41 @@ describe('Login', () => {
|
|||
);
|
||||
});
|
||||
|
||||
it('uses the submitted checkbox state when browser restore does not fire change', async () => {
|
||||
const mockOnLogin = vi.fn();
|
||||
mockFetch.mockResolvedValueOnce(
|
||||
new Response(JSON.stringify({ success: true }), {
|
||||
status: 200,
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
}),
|
||||
);
|
||||
const securityStatus = {
|
||||
hasAuthentication: true,
|
||||
hideLocalLogin: false,
|
||||
ssoEnabled: false,
|
||||
};
|
||||
|
||||
render(() => (
|
||||
<Login onLogin={mockOnLogin} hasAuth={true} securityStatus={securityStatus as any} />
|
||||
));
|
||||
|
||||
fireEvent.input(await screen.findByPlaceholderText('Username'), {
|
||||
target: { value: 'johannes' },
|
||||
});
|
||||
fireEvent.input(screen.getByPlaceholderText('Password'), { target: { value: 'secret' } });
|
||||
(screen.getByLabelText('Remember me') as HTMLInputElement).checked = true;
|
||||
fireEvent.click(screen.getByRole('button', { name: /sign in to pulse/i }));
|
||||
|
||||
await waitFor(() => expect(mockOnLogin).toHaveBeenCalledOnce());
|
||||
expect(mockLocalStorage.setItem).toHaveBeenCalledWith(
|
||||
STORAGE_KEYS.REMEMBERED_LOGIN_USERNAME,
|
||||
'johannes',
|
||||
);
|
||||
expect(mockLocalStorage.removeItem).not.toHaveBeenCalledWith(
|
||||
STORAGE_KEYS.REMEMBERED_LOGIN_USERNAME,
|
||||
);
|
||||
});
|
||||
|
||||
it('clears a previously remembered username after a successful non-remembered login', async () => {
|
||||
mockLocalStorageData[STORAGE_KEYS.REMEMBERED_LOGIN_USERNAME] = 'old-user';
|
||||
const mockOnLogin = vi.fn();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue