chore: improve tooling, linting, testing, and CI
- Add LICENSE file (CC BY-NC-SA 4.0) - Add .nvmrc pinning Node 22 - Add Renovate config with daily schedule and Gitea Actions workflow - Reduce ESLint max-warnings from 1000 to 50 - Re-enable Biome a11y/security recommended rules - Fix Biome lint issues (a11y, hook deps, SVG labels, checkbox semantics) - Improve CI: run on pushes to feat/fix branches, add pnpm audit - Add Vitest coverage with v8 provider and thresholds - Add E2E tests (auth, admin, navigation specs) - Add admin-maintenance server action test - Install @vitest/coverage-v8 - Ignore coverage/ directory
This commit is contained in:
1 parent
0f5c190ab1
commit
423a33200e
28 files changed
+1016
-80
No files matched your search
@@ -0,0 +1,182 @@
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
const { mockUpsert, mockRequirePermission, mockReload, mockRevalidatePath } =
|
||||
vi.hoisted(() => ({
|
||||
mockUpsert: vi.fn(),
|
||||
mockRequirePermission: vi.fn(),
|
||||
mockReload: vi.fn(),
|
||||
mockRevalidatePath: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock("@/lib/permissions", () => ({
|
||||
PERMS: {
|
||||
ADMIN_DASHBOARD: "admin.dashboard",
|
||||
SETTINGS_VIEW: "admin.settings.view",
|
||||
SETTINGS_EDIT: "admin.settings.edit",
|
||||
},
|
||||
}));
|
||||
|
||||
vi.mock("@/lib/prisma", () => ({
|
||||
prisma: {
|
||||
websiteSetting: { upsert: mockUpsert },
|
||||
},
|
||||
}));
|
||||
|
||||
vi.mock("@/lib/admin/guard", () => ({
|
||||
requirePermission: mockRequirePermission,
|
||||
}));
|
||||
|
||||
vi.mock("@/lib/services/site-settings", () => ({
|
||||
siteSettings: { reload: mockReload },
|
||||
}));
|
||||
|
||||
vi.mock("next/cache", () => ({
|
||||
revalidatePath: mockRevalidatePath,
|
||||
}));
|
||||
|
||||
import { saveMaintenance } from "./admin-maintenance";
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
describe("saveMaintenance", () => {
|
||||
it("enables maintenance mode with message and min rank", async () => {
|
||||
mockRequirePermission.mockResolvedValue({
|
||||
id: 1,
|
||||
rank: 7,
|
||||
username: "admin",
|
||||
});
|
||||
|
||||
const fd = new FormData();
|
||||
fd.set("enabled", "on");
|
||||
fd.set("message", "We will be back soon!");
|
||||
fd.set("min_rank", "3");
|
||||
|
||||
await saveMaintenance(fd);
|
||||
|
||||
expect(mockRequirePermission).toHaveBeenCalled();
|
||||
|
||||
expect(mockUpsert).toHaveBeenCalledTimes(3);
|
||||
expect(mockUpsert).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
where: { key: "maintenance_enabled" },
|
||||
update: { value: "1" },
|
||||
create: expect.objectContaining({
|
||||
key: "maintenance_enabled",
|
||||
value: "1",
|
||||
}),
|
||||
}),
|
||||
);
|
||||
expect(mockUpsert).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
where: { key: "maintenance_message" },
|
||||
update: { value: "We will be back soon!" },
|
||||
create: expect.objectContaining({
|
||||
key: "maintenance_message",
|
||||
value: "We will be back soon!",
|
||||
}),
|
||||
}),
|
||||
);
|
||||
expect(mockUpsert).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
where: { key: "min_maintenance_login_rank" },
|
||||
update: { value: "3" },
|
||||
create: expect.objectContaining({
|
||||
key: "min_maintenance_login_rank",
|
||||
value: "3",
|
||||
}),
|
||||
}),
|
||||
);
|
||||
|
||||
expect(mockReload).toHaveBeenCalledOnce();
|
||||
expect(mockRevalidatePath).toHaveBeenCalledWith("/admin/maintenance");
|
||||
});
|
||||
|
||||
it("disables maintenance mode", async () => {
|
||||
mockRequirePermission.mockResolvedValue({
|
||||
id: 1,
|
||||
rank: 7,
|
||||
username: "admin",
|
||||
});
|
||||
|
||||
const fd = new FormData();
|
||||
fd.set("message", "");
|
||||
fd.set("min_rank", "");
|
||||
|
||||
await saveMaintenance(fd);
|
||||
|
||||
expect(mockUpsert).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
where: { key: "maintenance_enabled" },
|
||||
update: { value: "0" },
|
||||
}),
|
||||
);
|
||||
expect(mockUpsert).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
where: { key: "min_maintenance_login_rank" },
|
||||
update: { value: "5" },
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it("defaults min_rank to 5 when input is empty", async () => {
|
||||
mockRequirePermission.mockResolvedValue({
|
||||
id: 1,
|
||||
rank: 7,
|
||||
username: "admin",
|
||||
});
|
||||
|
||||
const fd = new FormData();
|
||||
fd.set("enabled", "on");
|
||||
fd.set("message", "");
|
||||
fd.set("min_rank", "");
|
||||
|
||||
await saveMaintenance(fd);
|
||||
|
||||
expect(mockUpsert).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
where: { key: "min_maintenance_login_rank" },
|
||||
update: { value: "5" },
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it("defaults min_rank to 5 when input is negative", async () => {
|
||||
mockRequirePermission.mockResolvedValue({
|
||||
id: 1,
|
||||
rank: 7,
|
||||
username: "admin",
|
||||
});
|
||||
|
||||
const fd = new FormData();
|
||||
fd.set("enabled", "on");
|
||||
fd.set("message", "");
|
||||
fd.set("min_rank", "-1");
|
||||
|
||||
await saveMaintenance(fd);
|
||||
|
||||
expect(mockUpsert).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
where: { key: "min_maintenance_login_rank" },
|
||||
update: { value: "5" },
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it("calls requirePermission with SETTINGS_EDIT", async () => {
|
||||
mockRequirePermission.mockResolvedValue({
|
||||
id: 1,
|
||||
rank: 7,
|
||||
username: "admin",
|
||||
});
|
||||
|
||||
const fd = new FormData();
|
||||
fd.set("message", "");
|
||||
fd.set("min_rank", "");
|
||||
|
||||
await saveMaintenance(fd);
|
||||
|
||||
expect(mockRequirePermission).toHaveBeenCalledWith("admin.settings.edit");
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user