mirror of
https://github.com/Psychotoxical/psysonic.git
synced 2026-07-22 07:15:47 +00:00
2b1ad1542a
Continues the App.tsx slim-down. Two pieces move out of the monolith: - `src/app/AppRoutes.tsx` — the route table and its 32 lazy page imports. AppShell now renders `<AppRoutes />` inside the existing `<Suspense>`; the `perfFlags.disableMainRouteContentMount` placeholder stays in AppShell because that branch is a layout concern, not a routing one. `useIsMobile()` moves inside AppRoutes so the `/now-playing` mobile swap stays self-contained. - `src/app/RequireAuth.tsx` — the 4-line auth guard, with a focused test that covers all three reject paths (no login, no active server id, empty server list) plus the happy path. MainApp imports it from the new file instead of routing through the App.tsx re-export. Side-cleanup of imports that B.2 had already orphaned in App.tsx (`version`, `initAudioListeners`, `lazy`, `Routes`, `Route`, `Navigate`, `MobilePlayerView`). `App.tsx` 1308 → 1232 LOC. `AppShell` stays in App.tsx for Phase C.2. Pre-PR check: PASS (frontend tests, tsc, coverage gates, prod build, backend tests, clippy, backend coverage gates).
78 lines
2.3 KiB
TypeScript
78 lines
2.3 KiB
TypeScript
/**
|
|
* Route guard for the main webview. Verifies that each auth-store precondition
|
|
* (logged-in flag, an active server id, at least one stored server) is enforced
|
|
* independently before the children render.
|
|
*/
|
|
import { afterEach, describe, expect, it, vi } from 'vitest';
|
|
import { render, cleanup } from '@testing-library/react';
|
|
|
|
const { authState } = vi.hoisted(() => ({
|
|
authState: {
|
|
isLoggedIn: true,
|
|
activeServerId: 'srv-1',
|
|
servers: [{ id: 'srv-1', name: 'home' }],
|
|
},
|
|
}));
|
|
|
|
vi.mock('../store/authStore', () => ({
|
|
useAuthStore: () => authState,
|
|
}));
|
|
|
|
vi.mock('react-router-dom', () => ({
|
|
Navigate: ({ to }: { to: string }) => <div data-testid="redirect" data-to={to} />,
|
|
}));
|
|
|
|
import { RequireAuth } from './RequireAuth';
|
|
|
|
afterEach(() => {
|
|
cleanup();
|
|
authState.isLoggedIn = true;
|
|
authState.activeServerId = 'srv-1';
|
|
authState.servers = [{ id: 'srv-1', name: 'home' }];
|
|
});
|
|
|
|
describe('RequireAuth', () => {
|
|
it('renders children when fully authenticated', () => {
|
|
const { getByTestId, queryByTestId } = render(
|
|
<RequireAuth>
|
|
<div data-testid="protected" />
|
|
</RequireAuth>,
|
|
);
|
|
expect(getByTestId('protected')).toBeTruthy();
|
|
expect(queryByTestId('redirect')).toBeNull();
|
|
});
|
|
|
|
it('redirects to /login when not logged in', () => {
|
|
authState.isLoggedIn = false;
|
|
const { getByTestId, queryByTestId } = render(
|
|
<RequireAuth>
|
|
<div data-testid="protected" />
|
|
</RequireAuth>,
|
|
);
|
|
expect(queryByTestId('protected')).toBeNull();
|
|
expect(getByTestId('redirect').getAttribute('data-to')).toBe('/login');
|
|
});
|
|
|
|
it('redirects to /login when no active server id is set', () => {
|
|
authState.activeServerId = null as unknown as string;
|
|
const { queryByTestId, getByTestId } = render(
|
|
<RequireAuth>
|
|
<div data-testid="protected" />
|
|
</RequireAuth>,
|
|
);
|
|
expect(queryByTestId('protected')).toBeNull();
|
|
expect(getByTestId('redirect').getAttribute('data-to')).toBe('/login');
|
|
});
|
|
|
|
it('redirects to /login when the server list is empty', () => {
|
|
authState.servers = [];
|
|
const { queryByTestId, getByTestId } = render(
|
|
<RequireAuth>
|
|
<div data-testid="protected" />
|
|
</RequireAuth>,
|
|
);
|
|
expect(queryByTestId('protected')).toBeNull();
|
|
expect(getByTestId('redirect').getAttribute('data-to')).toBe('/login');
|
|
});
|
|
});
|