feat(frontend): polish web (logout, 401→pairing, reconnexion globale) (#13)
Flow logout : POST /api/logout puis retour à l'écran de pairing et déconnexion du live. 401 renvoie au pairing sans boucle. Bannière de reconnexion couvrant le cas live-only et le serveur indisponible. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@ -90,3 +90,32 @@ describe("WebSession unauthorized handling", () => {
|
||||
expect(session.isPaired()).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("WebSession logout", () => {
|
||||
it("POSTs /api/logout same-origin and clears the flag on success", async () => {
|
||||
const store = memStore();
|
||||
const { fetchImpl, calls } = fetchReturning(200, { revoked: true });
|
||||
const session = new WebSession({ baseUrl: "https://host/", fetchImpl, store });
|
||||
session.markPaired();
|
||||
|
||||
await session.logout();
|
||||
|
||||
expect(session.isPaired()).toBe(false);
|
||||
expect(calls[0].url).toBe("https://host/api/logout");
|
||||
const init = calls[0].init as { method: string; credentials: string };
|
||||
expect(init.method).toBe("POST");
|
||||
expect(init.credentials).toBe("same-origin");
|
||||
});
|
||||
|
||||
it("still clears the flag when the server is unreachable (best-effort)", async () => {
|
||||
const store = memStore();
|
||||
const fetchImpl: FetchLike = async () => {
|
||||
throw new Error("network down");
|
||||
};
|
||||
const session = new WebSession({ baseUrl: "https://host", fetchImpl, store });
|
||||
session.markPaired();
|
||||
|
||||
await expect(session.logout()).resolves.toBeUndefined();
|
||||
expect(session.isPaired()).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user