Files
Termix/plugins/homepage/tests/backend/routes.test.ts
T

125 lines
3.9 KiB
TypeScript

import { afterEach, beforeEach, describe, expect, it } from "vitest";
import { startServer, type TestServer } from "./helpers";
let server: TestServer;
afterEach(async () => {
await server.close();
});
describe("homepage item routes", () => {
beforeEach(async () => {
server = await startServer();
});
it("creates, lists, updates and deletes an item", async () => {
const created = await server.request("POST", "/items", {
body: { typeId: "clock", title: "My Clock", config: { format: "24h" } },
});
expect(created.status).toBe(201);
expect(created.body).toMatchObject({ typeId: "clock", title: "My Clock" });
expect(created.body.syncId).toEqual(expect.any(String));
const list = await server.request("GET", "/items");
expect(list.body).toHaveLength(1);
const updated = await server.request("PUT", `/items/${created.body.id}`, {
body: { title: "Renamed" },
});
expect(updated.status).toBe(200);
expect(updated.body.title).toBe("Renamed");
const deleted = await server.request("DELETE", `/items/${created.body.id}`);
expect(deleted.status).toBe(200);
expect((await server.request("GET", "/items")).body).toHaveLength(0);
});
it("rejects a missing typeId", async () => {
const res = await server.request("POST", "/items", { body: {} });
expect(res.status).toBe(400);
});
it("404s updating or deleting another user's item", async () => {
const created = await server.request("POST", "/items", {
body: { typeId: "clock" },
});
const res = await server.request("DELETE", `/items/${created.body.id}`, {
user: "user-2",
});
expect(res.status).toBe(404);
});
});
describe("homepage layout routes", () => {
beforeEach(async () => {
server = await startServer();
});
it("returns null before any layout is saved, then round-trips one", async () => {
expect((await server.request("GET", "/layout")).body).toBeNull();
const saved = await server.request("PUT", "/layout", {
body: { entries: [], pan: { x: 0, y: 0 }, zoom: 1 },
});
expect(saved.status).toBe(200);
expect(saved.body.layout).toEqual({
entries: [],
pan: { x: 0, y: 0 },
zoom: 1,
});
const fetched = await server.request("GET", "/layout");
expect(fetched.body.layout.zoom).toBe(1);
});
});
describe("service link routes", () => {
beforeEach(async () => {
server = await startServer();
});
it("creates, lists, updates and deletes a service link", async () => {
const created = await server.request("POST", "/service-links", {
body: { label: "Grafana", url: "grafana.local" },
});
expect(created.status).toBe(201);
// A bare host gets normalized to http://.
expect(created.body.url).toBe("http://grafana.local");
const list = await server.request("GET", "/service-links");
expect(list.body).toHaveLength(1);
const updated = await server.request(
"PUT",
`/service-links/${created.body.id}`,
{ body: { label: "Grafana Dashboard" } },
);
expect(updated.body.label).toBe("Grafana Dashboard");
const deleted = await server.request(
"DELETE",
`/service-links/${created.body.id}`,
);
expect(deleted.status).toBe(200);
expect((await server.request("GET", "/service-links")).body).toHaveLength(
0,
);
});
it("rejects an invalid url", async () => {
const res = await server.request("POST", "/service-links", {
body: { label: "Bad", url: "javascript:alert(1)" },
});
expect(res.status).toBe(400);
});
});
describe("permission denial", () => {
it("answers 403 for every route without homepage.use", async () => {
server = await startServer({ permissions: [] });
expect((await server.request("GET", "/items")).status).toBe(403);
expect((await server.request("GET", "/layout")).status).toBe(403);
expect((await server.request("GET", "/service-links")).status).toBe(403);
});
});