fix(router): redirect authenticated login visits to dashboard

This commit is contained in:
BigHulk
2026-09-22 18:22:57 +08:00
parent 73f784ea95
commit 50144205a0
2 changed files with 81 additions and 1 deletions
+79
View File
@@ -0,0 +1,79 @@
import "@testing-library/jest-dom/vitest";
import { cleanup, render, screen, waitFor } from "@testing-library/react";
import type { ReactNode } from "react";
import Router from "src/Router";
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
const { authState } = vi.hoisted(() => ({ authState: { authenticated: true } }));
vi.mock("src/context", () => ({ useAuthState: () => authState }));
vi.mock("src/hooks", () => ({
useHealth: () => ({ data: { status: "OK", setup: true }, isLoading: false, isError: false }),
}));
vi.mock("src/components", () => ({
Page: ({ children }: { children: ReactNode }) => children,
SiteContainer: ({ children }: { children: ReactNode }) => children,
SiteHeader: () => null,
SiteMenu: () => null,
SiteFooter: () => null,
LoadingPage: () => <div>Loading</div>,
Unhealthy: () => <div>Unhealthy</div>,
ErrorNotFound: () => <h1>Not found</h1>,
}));
vi.mock("src/pages/Dashboard", () => ({ default: () => <h1>Dashboard</h1> }));
vi.mock("src/pages/Login", () => ({ default: () => <h1>Login</h1> }));
vi.mock("src/pages/Nginx/ProxyHosts", () => ({ default: () => <h1>Proxy hosts</h1> }));
describe("Router", () => {
beforeEach(() => {
authState.authenticated = true;
window.history.replaceState(null, "", "/");
});
afterEach(() => {
cleanup();
vi.restoreAllMocks();
});
it.each(["/login", "/login/", "/login?next=/users#form"])(
"redirects an authenticated visit to %s to the dashboard",
async (path) => {
window.history.replaceState(null, "", path);
const replaceState = vi.spyOn(window.history, "replaceState");
render(<Router />);
expect(await screen.findByRole("heading", { name: "Dashboard" })).toBeVisible();
expect(window.location.pathname).toBe("/");
expect(window.location.search).toBe("");
expect(window.location.hash).toBe("");
expect(replaceState).toHaveBeenCalledWith(expect.anything(), "", "/");
},
);
it("shows the login form when signed out and redirects after sign-in", async () => {
authState.authenticated = false;
window.history.replaceState(null, "", "/login");
const { rerender } = render(<Router />);
expect(await screen.findByRole("heading", { name: "Login" })).toBeVisible();
expect(window.location.pathname).toBe("/login");
authState.authenticated = true;
rerender(<Router />);
expect(await screen.findByRole("heading", { name: "Dashboard" })).toBeVisible();
await waitFor(() => expect(window.location.pathname).toBe("/"));
});
it.each([
["/", "Dashboard"],
["/nginx/proxy", "Proxy hosts"],
["/unknown", "Not found"],
])("preserves the existing route for %s", async (path, heading) => {
window.history.replaceState(null, "", path);
render(<Router />);
expect(await screen.findByRole("heading", { name: heading })).toBeVisible();
expect(window.location.pathname).toBe(path);
});
});
+2 -1
View File
@@ -1,5 +1,5 @@
import { lazy, Suspense } from "react";
import { BrowserRouter, Route, Routes } from "react-router-dom";
import { BrowserRouter, Navigate, Route, Routes } from "react-router-dom";
import {
ErrorNotFound,
LoadingPage,
@@ -61,6 +61,7 @@ function Router() {
<Suspense fallback={<LoadingPage noLogo />}>
<Routes>
<Route path="*" element={<ErrorNotFound />} />
<Route path="/login" element={<Navigate to="/" replace />} />
<Route path="/certificates" element={<Certificates />} />
<Route path="/access" element={<Access />} />
<Route path="/audit-log" element={<AuditLog />} />