Files
2026-07-20 16:42:33 +08:00

131 lines
5.2 KiB
TypeScript

import { renderToStaticMarkup } from "react-dom/server";
import { describe, expect, it } from "vitest";
import { UsersPage } from "./UsersPage";
import usersPageSource from "./UsersPage.tsx?raw";
import type { UserResponse } from "../api/types";
import type { PageComponentProps } from "../contracts/page";
import { capabilitiesForRoles, type CurrentUserView } from "../contracts/workspace";
import type { OperationTracker } from "../stores/operations";
const adminUser: CurrentUserView = {
id: "user-admin",
displayName: "Operator",
status: "active",
roles: ["platformAdmin"],
capabilities: capabilitiesForRoles(["platformAdmin"]),
profile: {},
source: "local"
};
const serverUser: CurrentUserView = {
...adminUser,
id: "user-server",
roles: ["serverAdmin"],
capabilities: capabilitiesForRoles(["serverAdmin"])
};
const managedUser: UserResponse = {
id: "user-reviewer",
displayName: "Plugin Reviewer",
email: "reviewer@example.test",
status: "pending",
roles: ["server-admin"],
profile: { phone: "13800000000", qq: "10001", contactNote: "needs approval" },
createdAt: "2026-07-03T00:00:00Z",
updatedAt: "2026-07-03T00:00:00Z"
};
const noopOperations: OperationTracker = {
operations: [],
begin: () => "op-test",
update: () => undefined,
succeed: () => undefined,
fail: () => undefined,
isPending: () => false
};
function pageProps(session = adminUser): PageComponentProps {
return {
session,
params: {},
operations: noopOperations,
onNavigate: () => undefined,
onLogout: async () => undefined,
onProfileSave: async () => session,
onThemePreferenceSave: async () => ({ userId: session.id, paletteId: "mecha-black", backgroundPresetId: "mecha-grid", persistence: "api", updatedAt: "2026-07-03T00:00:00Z" })
};
}
describe("UsersPage", () => {
it("renders an empty API-backed state without local users", () => {
const html = renderToStaticMarkup(<UsersPage {...pageProps()} initialState={{ users: [], loading: false, source: "api" }} />);
expect(html).toContain("暂无用户");
expect(html).toContain("page-summary-chip");
expect(html).not.toContain("metric-card");
expect(html).not.toContain("Plugin Reviewer");
});
it("renders API failure without substituting actionable sample users", () => {
const html = renderToStaticMarkup(<UsersPage {...pageProps()} initialState={{ users: [], loading: false, source: "api", loadError: "backend unavailable" }} />);
expect(html).toContain("用户 API 暂不可用");
expect(html).toContain("backend unavailable");
expect(html).toContain("重试");
expect(html).not.toContain("local.example.test");
expect(usersPageSource).toContain("setUsers([])");
expect(usersPageSource).not.toContain("fallbackUsers");
});
it("renders explicit local-development fixture state with a single status flow", () => {
const html = renderToStaticMarkup(<UsersPage {...pageProps()} initialState={{ users: [managedUser], loading: false, source: "local-development" }} />);
expect(html).toContain("Plugin Reviewer");
expect(html).toContain("本地开发样例 / 仅查看");
expect(html).toContain("编辑");
expect(html).toContain("状态");
expect(html).toContain("应用状态");
expect(html).toContain("停用");
expect(html).toContain("邀请用户");
expect(html).toContain("审核申请");
expect(html).toContain("绑定服务器范围");
expect(html).toContain("角色影响");
expect(html).toContain("needs approval");
});
it("does not render create or edit forms inline on the list page", () => {
const html = renderToStaticMarkup(<UsersPage {...pageProps()} initialState={{ users: [managedUser], loading: false, source: "api" }} />);
expect(html).toContain("访问列表");
expect(html).toContain("邀请用户");
expect(html).not.toContain('role="dialog"');
expect(html).not.toContain('aria-label="编辑用户"');
});
it("renders full-width user search and role/status filters", () => {
const html = renderToStaticMarkup(<UsersPage {...pageProps()} initialState={{ users: [managedUser], loading: false, source: "api" }} />);
expect(html).toContain("搜索名称、邮箱或用户 ID");
expect(html).toContain("按角色筛选");
expect(html).toContain("按状态筛选");
expect(html).toContain("平台管理员");
expect(html).toContain("服务器管理员");
});
it("prevents duplicate create and edit submissions while session operations are pending", () => {
expect(usersPageSource).toContain('operations.isPending("users", "邀请用户")');
expect(usersPageSource).toContain('operations.isPending(editingUserId, "编辑用户")');
expect(usersPageSource).toContain("createPending ? \"发送中…\"");
expect(usersPageSource).toContain("editPending ? \"保存中…\"");
});
it("does not render account maintenance controls for non-admins", () => {
const html = renderToStaticMarkup(<UsersPage {...pageProps(serverUser)} initialState={{ users: [managedUser], loading: false, source: "api" }} />);
expect(html).toContain("当前账号不能管理用户");
expect(html).not.toContain("邀请用户");
expect(html).not.toContain("停用");
});
});