import { afterEach, describe, expect, test, vi } from "vitest"; import { screen, waitFor, within } from "@testing-library/react"; import { userEvent } from "@testing-library/user-event"; import { Manage } from "../../pages/Manage.tsx"; import { calls, fail, route } from "../fetch.ts"; import { makeCategory, makeSession, wire } from "../fixtures.ts"; import { renderApp } from "../render.tsx"; const instance = vi.hoisted(() => ({ federationIncoming: true, federationOutgoing: true, })); vi.mock("../../config/instance.ts", async (importOriginal) => ({ ...await importOriginal(), get FEDERATION_INCOMING() { return instance.federationIncoming; }, get FEDERATION_OUTGOING() { return instance.federationOutgoing; }, })); const admin = makeSession({ id: "ann", username: "ann", role: "admin" }); function open(session = admin) { route("GET /api/federation/peers", () => [], { fallback: true }); const user = userEvent.setup(); renderApp(, { route: "/manage", path: "/manage", session }); return user; } describe("Manage", () => { afterEach(() => { instance.federationIncoming = true; instance.federationOutgoing = true; }); test("an admin manages categories", async () => { const music = makeCategory({ id: "c1", slug: "music", name: "Music" }); let list = [music]; route("GET /api/categories", () => wire(list)); route("POST /api/categories", (req) => { const c = makeCategory({ id: "c2", ...(req.body as object) }); list = [...list, c]; return wire(c); }); route("DELETE /api/categories/:id", () => { list = list.filter((c) => c.id !== "c1"); return null; }); const user = open(); await user.click( await screen.findByRole("button", { name: "Add category" }), ); await user.type(screen.getByRole("textbox", { name: "Name" }), "Cinema"); await user.type( screen.getByRole("textbox", { name: "URL slug" }), "cinema", ); await user.click(screen.getByRole("button", { name: "Save" })); expect(await screen.findByRole("button", { name: "Cinema" })) .toBeInTheDocument(); expect(calls("POST /api/categories")[0].body).toEqual({ name: "Cinema", slug: "cinema", }); await user.click(screen.getByRole("button", { name: "Music" })); await user.click(screen.getByRole("button", { name: "Delete" })); await user.click( within(screen.getByRole("alertdialog")).getByRole("button", { name: "Delete category", }), ); await waitFor(() => expect(screen.queryByRole("button", { name: "Music" })).not .toBeInTheDocument() ); }); test("a refused category is reported, and Escape closes the editor", async () => { route("GET /api/categories", () => []); route("POST /api/categories", () => fail("Slug already taken", 409)); const user = open(); await user.click( await screen.findByRole("button", { name: "Add category" }), ); await user.type(screen.getByRole("textbox", { name: "Name" }), "X"); await user.type( screen.getByRole("textbox", { name: "URL slug" }), "x{Enter}", ); expect(await screen.findByRole("alert")).toHaveTextContent( "Slug already taken", ); await user.type(screen.getByRole("textbox", { name: "Name" }), "{Escape}"); expect(screen.getByRole("button", { name: "Add category" })) .toBeInTheDocument(); }); test("an admin trusts, blocks and removes other instances", async () => { route("GET /api/categories", () => []); const music = { issuer: "https://music.example", host: "music.example", name: "Music", status: "trusted", createdAt: new Date().toISOString(), }; let peers: (typeof music)[] = []; route("GET /api/federation/peers", () => peers); route("PUT /api/federation/peers", (req) => { const { status } = req.body as { status: string }; peers = [{ ...music, status }]; return peers[0]; }); route("DELETE /api/federation/peers/:host", () => { peers = []; return null; }); const user = open(); await user.type( await screen.findByPlaceholderText( "An instance's URL", ), "music.example", ); await user.click(screen.getByRole("button", { name: "Trust" })); expect(await screen.findByText("Music")).toBeInTheDocument(); expect(calls("PUT /api/federation/peers")[0].body).toEqual({ instance: "music.example", status: "trusted", }); await user.click(screen.getByRole("button", { name: "Blocked" })); await waitFor(() => expect(calls("PUT /api/federation/peers")[1]?.body).toEqual({ instance: "https://music.example", status: "blocked", }) ); await user.click( screen.getByRole("button", { name: "Remove music.example" }), ); await waitFor(() => expect(screen.queryByText("Music")).not.toBeInTheDocument() ); }); test("where users of other instances don't sign in, an admin only blocks instances", async () => { instance.federationIncoming = false; route("GET /api/categories", () => []); const peer = (name: string, host: string, status: string) => ({ issuer: `https://${host}`, host, name, status, createdAt: new Date().toISOString(), }); route("GET /api/federation/peers", () => [ peer("Films", "films.example", "trusted"), peer("Spam", "spam.example", "blocked"), ]); route( "PUT /api/federation/peers", () => peer("Music", "music.example", "blocked"), ); const user = open(); expect(await screen.findByText("Spam")).toBeInTheDocument(); expect(screen.queryByText("Films")).not.toBeInTheDocument(); expect(screen.queryByRole("button", { name: "Trust" })).not .toBeInTheDocument(); expect(screen.queryByRole("button", { name: "Blocked" })).not .toBeInTheDocument(); await user.type( screen.getByPlaceholderText("An instance's URL"), "music.example{Enter}", ); await waitFor(() => expect(calls("PUT /api/federation/peers")[0]?.body).toEqual({ instance: "music.example", status: "blocked", }) ); }); test("without federated sign-in either way, there are no instances to manage", async () => { instance.federationIncoming = false; instance.federationOutgoing = false; route("GET /api/categories", () => []); open(); expect(await screen.findByText("Categories")).toBeInTheDocument(); expect(screen.queryByText("Instances")).not.toBeInTheDocument(); }); test("without category:manage, the page isn't found", async () => { open(makeSession({ id: "mod", role: "moderator" })); expect(await screen.findByText("This page does not exist.")) .toBeInTheDocument(); }); });