Add managed content and keyboard usage insights
Introduce versioned official content workflows and privacy-safe keyboard analytics, while preventing repeat DeviceCheck sign-ins from incorrectly restricting eligible accounts.
This commit is contained in:
@@ -0,0 +1,121 @@
|
||||
import { cleanup, render, screen, waitFor } from "@testing-library/react";
|
||||
import userEvent from "@testing-library/user-event";
|
||||
import { afterEach, describe, expect, it, vi } from "vitest";
|
||||
import { adminApi } from "../api/client";
|
||||
import type { AdminRole, OfficialSkillCatalog } from "../api/types";
|
||||
import { App } from "../app";
|
||||
|
||||
afterEach(() => {
|
||||
cleanup();
|
||||
vi.restoreAllMocks();
|
||||
window.location.hash = "";
|
||||
});
|
||||
|
||||
describe("内容管理", () => {
|
||||
it("SUPPORT 可查看但不能修改 Skill 与 Hint", async () => {
|
||||
mockSession("SUPPORT");
|
||||
mockContent();
|
||||
window.location.hash = "#/content";
|
||||
|
||||
render(<App />);
|
||||
|
||||
expect(await screen.findByRole("heading", { name: "内容管理" })).toBeTruthy();
|
||||
expect(screen.getByRole("link", { name: /内容管理/ })).toBeTruthy();
|
||||
expect(await screen.findByText("润色")).toBeTruthy();
|
||||
expect(screen.getByText("只读访问")).toBeTruthy();
|
||||
expect(screen.queryByRole("button", { name: "新增 Skill" })).toBeNull();
|
||||
const editor = await screen.findByLabelText("zh JSON");
|
||||
expect(editor).toHaveProperty("readOnly", true);
|
||||
expect(screen.queryByRole("button", { name: "保存并立即发布" })).toBeNull();
|
||||
});
|
||||
|
||||
it("SUPER_ADMIN 可启停官方 Skill", async () => {
|
||||
mockSession("SUPER_ADMIN");
|
||||
mockContent();
|
||||
const mutation = vi
|
||||
.spyOn(adminApi, "setContentSkillEnabled")
|
||||
.mockResolvedValue(undefined);
|
||||
window.location.hash = "#/content";
|
||||
|
||||
render(<App />);
|
||||
|
||||
await userEvent.click(await screen.findByRole("button", { name: "停用" }));
|
||||
|
||||
await waitFor(() => {
|
||||
expect(mutation).toHaveBeenCalledWith("official.polish", false);
|
||||
});
|
||||
expect(screen.getByRole("button", { name: "新增 Skill" })).toBeTruthy();
|
||||
});
|
||||
|
||||
it("Skill 编辑器约束与客户端边界一致", async () => {
|
||||
mockSession("SUPER_ADMIN");
|
||||
mockContent();
|
||||
window.location.hash = "#/content";
|
||||
render(<App />);
|
||||
|
||||
await userEvent.click(await screen.findByRole("button", { name: "新增 Skill" }));
|
||||
|
||||
expect(screen.getByLabelText("Skill ID").getAttribute("maxlength")).toBe("100");
|
||||
expect(screen.getByLabelText("SF Symbol").getAttribute("maxlength")).toBe("100");
|
||||
const sortOrder = screen.getByLabelText("排序");
|
||||
expect(sortOrder.getAttribute("min")).toBe("0");
|
||||
expect(sortOrder.getAttribute("max")).toBe("100000");
|
||||
screen.getAllByLabelText("名称").forEach((input) => {
|
||||
expect(input.getAttribute("maxlength")).toBe("40");
|
||||
});
|
||||
screen.getAllByLabelText("摘要").forEach((input) => {
|
||||
expect(input.getAttribute("maxlength")).toBe("200");
|
||||
});
|
||||
screen.getAllByLabelText("Prompt").forEach((input) => {
|
||||
expect(input.getAttribute("maxlength")).toBe("6000");
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
function mockSession(role: AdminRole) {
|
||||
vi.spyOn(adminApi, "session").mockResolvedValue({
|
||||
authenticated: true,
|
||||
operatorName: "owner",
|
||||
role,
|
||||
});
|
||||
}
|
||||
|
||||
function mockContent() {
|
||||
vi.spyOn(adminApi, "contentSkills").mockResolvedValue(catalog());
|
||||
vi.spyOn(adminApi, "contentHintPack").mockResolvedValue({
|
||||
locale: "zh",
|
||||
generatedAt: "2026-08-21T04:00:00Z",
|
||||
intervalHours: 12,
|
||||
version: 2,
|
||||
cards: [],
|
||||
});
|
||||
}
|
||||
|
||||
function catalog(): OfficialSkillCatalog {
|
||||
return {
|
||||
revision: 3,
|
||||
generatedAt: "2026-08-21T04:00:00Z",
|
||||
skills: [
|
||||
{
|
||||
id: "official.polish",
|
||||
systemImage: "wand.and.sparkles",
|
||||
sortOrder: 10,
|
||||
kind: "transform",
|
||||
thinkingEnabled: false,
|
||||
enabled: true,
|
||||
localizations: {
|
||||
"zh-Hans": {
|
||||
name: "润色",
|
||||
summary: "优化表达",
|
||||
prompt: "请润色",
|
||||
},
|
||||
en: {
|
||||
name: "Polish",
|
||||
summary: "Improve wording",
|
||||
prompt: "Please polish",
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user