Files
gerbeur/src/test/pages/Manage.test.tsx

202 lines
6.7 KiB
TypeScript

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<typeof import("../../config/instance.ts")>(),
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(<Manage />, { 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();
});
});