Fix AuthSettings - hide when no server configured and show warning when offline (#6723)

* Fix AuthSettings - hide if no server configured and show warning if offline

* Add release notes for auth settings fix

* Refactor AuthSettings component to always display OpenID label hint and remove offline condition check in tests
This commit is contained in:
Matiss Janis Aboltins
2026-01-20 19:00:07 +01:00
committed by GitHub
parent d4144f4b9c
commit 6a9df6562c
3 changed files with 154 additions and 0 deletions

View File

@@ -0,0 +1,127 @@
import { render, screen } from '@testing-library/react';
import { vi } from 'vitest';
import { AuthSettings } from './AuthSettings';
import {
useLoginMethod,
useMultiuserEnabled,
} from '@desktop-client/components/ServerContext';
import { useSyncServerStatus } from '@desktop-client/hooks/useSyncServerStatus';
import { TestProvider } from '@desktop-client/redux/mock';
vi.mock('@desktop-client/hooks/useSyncServerStatus', () => ({
useSyncServerStatus: vi.fn(),
}));
vi.mock('@desktop-client/components/ServerContext', () => ({
useMultiuserEnabled: vi.fn(),
useLoginMethod: vi.fn(),
}));
describe('AuthSettings', () => {
beforeEach(() => {
vi.clearAllMocks();
});
it('does not render when server status is no-server', () => {
vi.mocked(useSyncServerStatus).mockReturnValue('no-server');
vi.mocked(useMultiuserEnabled).mockReturnValue(false);
vi.mocked(useLoginMethod).mockReturnValue('password');
const { container } = render(<AuthSettings />, { wrapper: TestProvider });
expect(container.firstChild).toBeNull();
});
describe('when server is offline', () => {
beforeEach(() => {
vi.mocked(useSyncServerStatus).mockReturnValue('offline');
});
it('disables buttons and shows warning when login method is password', () => {
vi.mocked(useMultiuserEnabled).mockReturnValue(false);
vi.mocked(useLoginMethod).mockReturnValue('password');
render(<AuthSettings />, { wrapper: TestProvider });
const startUsingButton = screen.getByRole('button', {
name: /start using openid/i,
});
expect(startUsingButton).toBeDisabled();
const warningText = screen.getByText(
/server is offline\. openid settings are unavailable\./i,
);
expect(warningText).toBeInTheDocument();
});
it('disables buttons and shows warning when login method is openid', () => {
vi.mocked(useMultiuserEnabled).mockReturnValue(false);
vi.mocked(useLoginMethod).mockReturnValue('openid');
render(<AuthSettings />, { wrapper: TestProvider });
const disableButton = screen.getByRole('button', {
name: /disable openid/i,
});
expect(disableButton).toBeDisabled();
const warningText = screen.getByText(
/server is offline\. openid settings are unavailable\./i,
);
expect(warningText).toBeInTheDocument();
});
});
describe('when server is online', () => {
beforeEach(() => {
vi.mocked(useSyncServerStatus).mockReturnValue('online');
});
it('renders normally with password login method', () => {
vi.mocked(useMultiuserEnabled).mockReturnValue(false);
vi.mocked(useLoginMethod).mockReturnValue('password');
render(<AuthSettings />, { wrapper: TestProvider });
const startUsingButton = screen.getByRole('button', {
name: /start using openid/i,
});
expect(startUsingButton).not.toBeDisabled();
const warningText = screen.queryByText(
/server is offline\. openid settings are unavailable\./i,
);
expect(warningText).not.toBeInTheDocument();
});
it('renders normally with openid login method', () => {
vi.mocked(useMultiuserEnabled).mockReturnValue(false);
vi.mocked(useLoginMethod).mockReturnValue('openid');
render(<AuthSettings />, { wrapper: TestProvider });
const disableButton = screen.getByRole('button', {
name: /disable openid/i,
});
expect(disableButton).not.toBeDisabled();
const warningText = screen.queryByText(
/server is offline\. openid settings are unavailable\./i,
);
expect(warningText).not.toBeInTheDocument();
});
it('shows multi-user warning when multiuser is enabled', () => {
vi.mocked(useMultiuserEnabled).mockReturnValue(true);
vi.mocked(useLoginMethod).mockReturnValue('openid');
render(<AuthSettings />, { wrapper: TestProvider });
const warningText = screen.getByText(
/disabling openid will deactivate multi-user mode\./i,
);
expect(warningText).toBeInTheDocument();
});
});
});

View File

@@ -5,6 +5,7 @@ import { Button } from '@actual-app/components/button';
import { Label } from '@actual-app/components/label';
import { Text } from '@actual-app/components/text';
import { theme } from '@actual-app/components/theme';
import { View } from '@actual-app/components/view';
import { Setting } from './UI';
@@ -12,6 +13,7 @@ import {
useLoginMethod,
useMultiuserEnabled,
} from '@desktop-client/components/ServerContext';
import { useSyncServerStatus } from '@desktop-client/hooks/useSyncServerStatus';
import { pushModal } from '@desktop-client/modals/modalsSlice';
import { useDispatch } from '@desktop-client/redux';
@@ -21,6 +23,14 @@ export function AuthSettings() {
const multiuserEnabled = useMultiuserEnabled();
const loginMethod = useLoginMethod();
const dispatch = useDispatch();
const serverStatus = useSyncServerStatus();
// Hide the OpenID block entirely when no server is configured
if (serverStatus === 'no-server') {
return null;
}
const isOffline = serverStatus === 'offline';
return (
<Setting
@@ -32,6 +42,15 @@ export function AuthSettings() {
{loginMethod === 'openid' ? t('enabled') : t('disabled')}
</label>
</label>
{isOffline && (
<View>
<Text style={{ paddingTop: 5, color: theme.warningText }}>
<Trans>
Server is offline. OpenID settings are unavailable.
</Trans>
</Text>
</View>
)}
{loginMethod === 'password' && (
<>
<Button
@@ -40,6 +59,7 @@ export function AuthSettings() {
marginTop: '10px',
}}
variant="normal"
isDisabled={isOffline}
onPress={() =>
dispatch(
pushModal({
@@ -66,6 +86,7 @@ export function AuthSettings() {
marginTop: '10px',
}}
variant="normal"
isDisabled={isOffline}
onPress={() =>
dispatch(
pushModal({