diff --git a/web/src/auth/require-auth.test.tsx b/web/src/auth/require-auth.test.tsx
index 643e9cc..d0faa1a 100644
--- a/web/src/auth/require-auth.test.tsx
+++ b/web/src/auth/require-auth.test.tsx
@@ -1,16 +1,21 @@
import { screen, waitFor } from "@testing-library/react";
import { http, HttpResponse } from "msw";
import { expect, test } from "vitest";
-import { Route, Routes } from "react-router-dom";
+import { Route, Routes, useLocation } from "react-router-dom";
import { server } from "../test/server";
import { renderApp } from "../test/render";
import { RequireAuth } from "./require-auth";
+function LoginStub() {
+ const location = useLocation();
+ return
login page {location.search}
;
+}
+
function tree() {
return (
- login page} />
+ } />
}>
secret objects} />
@@ -23,8 +28,8 @@ test("renders children when authenticated", async () => {
expect(await screen.findByText("secret objects")).toBeInTheDocument();
});
-test("redirects to /login when unauthenticated", async () => {
+test("redirects unauthenticated users to /login carrying the attempted path", async () => {
server.use(http.get("/api/admin/me", () => new HttpResponse(null, { status: 401 })));
renderApp(tree(), { route: "/objects" });
- await waitFor(() => expect(screen.getByText("login page")).toBeInTheDocument());
+ await waitFor(() => expect(screen.getByText(/from=%2Fobjects/)).toBeInTheDocument());
});
diff --git a/web/src/auth/require-auth.tsx b/web/src/auth/require-auth.tsx
index 23876f3..7adb927 100644
--- a/web/src/auth/require-auth.tsx
+++ b/web/src/auth/require-auth.tsx
@@ -1,14 +1,18 @@
-import { Navigate, Outlet } from "react-router-dom";
+import { Navigate, Outlet, useLocation } from "react-router-dom";
import { useMe } from "../api/queries";
import { AppShellSkeleton } from "@/components/ui/skeletons";
export function RequireAuth() {
const { data: user, isLoading } = useMe();
+ const location = useLocation();
if (isLoading) return ;
- if (!user) return ;
+ if (!user) {
+ const from = encodeURIComponent(location.pathname + location.search);
+ return ;
+ }
return ;
}
diff --git a/web/src/shell/user-menu.test.tsx b/web/src/shell/user-menu.test.tsx
index 70085b9..784c478 100644
--- a/web/src/shell/user-menu.test.tsx
+++ b/web/src/shell/user-menu.test.tsx
@@ -1,7 +1,7 @@
import { expect, test } from "vitest";
import { screen, waitFor, within } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
-import { http, HttpResponse } from "msw";
+import { delay, http, HttpResponse } from "msw";
import { server } from "../test/server";
import { renderApp } from "../test/render";
import { UserMenu } from "./user-menu";
@@ -33,3 +33,22 @@ test("opens the menu showing email + role and signs out", async () => {
await userEvent.click(signOut);
await waitFor(() => expect(loggedOut).toBe(true));
});
+
+test("shows a pending state on Sign out while logging out", async () => {
+ server.use(
+ http.post("/api/admin/logout", async () => {
+ await delay(50);
+ return new HttpResponse(null, { status: 204 });
+ }),
+ );
+
+ renderApp();
+
+ const trigger = await screen.findByRole("button", { name: /editor@example.com/ });
+ await userEvent.click(trigger);
+
+ const menu = within(document.body);
+ await userEvent.click(await menu.findByText("Sign out"));
+
+ expect(await menu.findByText(/signing out/i)).toBeInTheDocument();
+});
diff --git a/web/src/shell/user-menu.tsx b/web/src/shell/user-menu.tsx
index a8779be..643feb1 100644
--- a/web/src/shell/user-menu.tsx
+++ b/web/src/shell/user-menu.tsx
@@ -35,7 +35,9 @@ export function UserMenu() {
{me.role}
-
+
);