236 lines
6.8 KiB
TypeScript
236 lines
6.8 KiB
TypeScript
import { fireEvent, render, screen, waitFor } from "@testing-library/react";
|
|
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
|
|
import { App } from "../src/client/App.js";
|
|
import type {
|
|
CollectionSummaryResponse,
|
|
DocumentComparisonResponse,
|
|
ModifierStageResponse,
|
|
} from "../src/shared/api.js";
|
|
|
|
vi.mock("../src/client/DiffView.js", () => ({
|
|
DiffView: ({ beforeLabel, afterLabel }: { beforeLabel: string; afterLabel: string }) => (
|
|
<div data-testid="diff-view">
|
|
{beforeLabel} / {afterLabel}
|
|
</div>
|
|
),
|
|
}));
|
|
|
|
const modifiers = [
|
|
{
|
|
modifier_position: 0,
|
|
modifier_id: "paper.rule",
|
|
modifier_version: "1.0.0",
|
|
parameters: [],
|
|
applicability: "替换测试单词。",
|
|
change_count: 1,
|
|
stage_available: true,
|
|
},
|
|
{
|
|
modifier_position: 1,
|
|
modifier_id: "paper.zero",
|
|
modifier_version: "1.0.0",
|
|
parameters: [],
|
|
applicability: "不修改当前测试文档。",
|
|
change_count: 0,
|
|
stage_available: true,
|
|
},
|
|
];
|
|
|
|
const documentSummary = {
|
|
document_id: "paper",
|
|
source_label: "paper.md",
|
|
status: "success" as const,
|
|
current_kind: "success_output" as const,
|
|
input_sha256: "1".repeat(64),
|
|
current_sha256: "2".repeat(64),
|
|
modifier_count: 2,
|
|
completed_stage_count: 2,
|
|
change_count: 1,
|
|
error_count: 0,
|
|
residual_proposal_count: 0,
|
|
};
|
|
|
|
const collectionResponse: CollectionSummaryResponse = {
|
|
schema_version: 1,
|
|
collection: { label: "合成评审", status: "success" },
|
|
documents: [documentSummary],
|
|
summary: {
|
|
document_count: 1,
|
|
success_count: 1,
|
|
failed_count: 0,
|
|
unstable_count: 0,
|
|
change_count: 1,
|
|
},
|
|
};
|
|
|
|
const change = {
|
|
modifier_position: 0,
|
|
modifier_id: "paper.rule",
|
|
modifier_version: "1.0.0",
|
|
proposal_index: 0,
|
|
edit_index: 0,
|
|
reason: "替换测试单词",
|
|
location: { line: 1, column: 1 },
|
|
span: { start: 0, end: 3 },
|
|
editor_range: { start: 0, end: 3 },
|
|
before: "old",
|
|
after: "new",
|
|
before_sha256: "1".repeat(64),
|
|
after_sha256: "2".repeat(64),
|
|
};
|
|
|
|
const documentResponse: DocumentComparisonResponse = {
|
|
schema_version: 1,
|
|
document: documentSummary,
|
|
modifiers,
|
|
input_markdown: "old",
|
|
current_markdown: "new",
|
|
changes: [change],
|
|
errors: [],
|
|
residual_proposals: [],
|
|
};
|
|
|
|
const stageResponse: ModifierStageResponse = {
|
|
schema_version: 1,
|
|
document_id: "paper",
|
|
modifier: modifiers[0]!,
|
|
before_sha256: "1".repeat(64),
|
|
after_sha256: "2".repeat(64),
|
|
before_markdown: "old",
|
|
after_markdown: "new",
|
|
changes: [change],
|
|
};
|
|
|
|
function response(payload: unknown, status = 200): Response {
|
|
return new Response(JSON.stringify(payload), {
|
|
status,
|
|
headers: { "Content-Type": "application/json" },
|
|
});
|
|
}
|
|
|
|
describe("App", () => {
|
|
beforeEach(() => {
|
|
vi.restoreAllMocks();
|
|
});
|
|
|
|
it("shows documents, Modifier order and the selected stage", async () => {
|
|
const fetchMock = vi.fn((input: string | URL | Request) => {
|
|
const pathname =
|
|
typeof input === "string" ? input : input instanceof URL ? input.toString() : input.url;
|
|
if (pathname === "/api/v1/collection") {
|
|
return Promise.resolve(response(collectionResponse));
|
|
}
|
|
if (pathname.endsWith("/modifiers/0")) {
|
|
return Promise.resolve(response(stageResponse));
|
|
}
|
|
return Promise.resolve(response(documentResponse));
|
|
});
|
|
vi.stubGlobal("fetch", fetchMock);
|
|
|
|
render(<App />);
|
|
|
|
expect(await screen.findByRole("heading", { name: "合成评审" })).toBeInTheDocument();
|
|
expect(await screen.findByTestId("diff-view")).toHaveTextContent("清洗前 / 清洗后");
|
|
expect(screen.getByText("paper.rule")).toBeInTheDocument();
|
|
expect(screen.getByText("paper.zero")).toBeInTheDocument();
|
|
|
|
fireEvent.click(screen.getByRole("button", { name: /paper\.rule/ }));
|
|
await waitFor(() => {
|
|
expect(fetchMock).toHaveBeenCalledWith(expect.stringContaining("modifiers/0"), expect.anything());
|
|
});
|
|
expect(await screen.findByTestId("diff-view")).toHaveTextContent(
|
|
"Modifier 1 执行前 / Modifier 1 执行后",
|
|
);
|
|
});
|
|
|
|
it("clicks a change through its Modifier stage", async () => {
|
|
vi.stubGlobal(
|
|
"fetch",
|
|
vi.fn((input: string | URL | Request) => {
|
|
const pathname =
|
|
typeof input === "string" ? input : input instanceof URL ? input.toString() : input.url;
|
|
if (pathname === "/api/v1/collection") {
|
|
return Promise.resolve(response(collectionResponse));
|
|
}
|
|
if (pathname.endsWith("/modifiers/0")) {
|
|
return Promise.resolve(response(stageResponse));
|
|
}
|
|
return Promise.resolve(response(documentResponse));
|
|
}),
|
|
);
|
|
|
|
render(<App />);
|
|
|
|
fireEvent.click(await screen.findByRole("button", { name: /替换测试单词/ }));
|
|
expect(await screen.findByTestId("diff-view")).toHaveTextContent(
|
|
"Modifier 1 执行前 / Modifier 1 执行后",
|
|
);
|
|
});
|
|
|
|
it("shows failed diagnostics without a cleaned comparison", async () => {
|
|
const failedSummary = {
|
|
...documentSummary,
|
|
status: "failed" as const,
|
|
current_kind: "partial_output" as const,
|
|
current_sha256: documentSummary.input_sha256,
|
|
completed_stage_count: 0,
|
|
change_count: 0,
|
|
error_count: 1,
|
|
};
|
|
const failedCollection: CollectionSummaryResponse = {
|
|
...collectionResponse,
|
|
collection: { ...collectionResponse.collection, status: "failed" },
|
|
documents: [failedSummary],
|
|
summary: {
|
|
...collectionResponse.summary,
|
|
success_count: 0,
|
|
failed_count: 1,
|
|
change_count: 0,
|
|
},
|
|
};
|
|
const failedDocument: DocumentComparisonResponse = {
|
|
schema_version: 1,
|
|
document: failedSummary,
|
|
modifiers: modifiers.map((modifier) => ({
|
|
...modifier,
|
|
change_count: 0,
|
|
stage_available: false,
|
|
})),
|
|
input_markdown: "原文",
|
|
current_markdown: null,
|
|
changes: [],
|
|
errors: [
|
|
{
|
|
code: "run.transform_failed",
|
|
stage: "transform",
|
|
modifier_position: 0,
|
|
modifier_id: "paper.rule",
|
|
modifier_version: "1.0.0",
|
|
diagnostic_type: "SyntheticError",
|
|
message: "测试 Modifier 失败。",
|
|
},
|
|
],
|
|
residual_proposals: [],
|
|
};
|
|
vi.stubGlobal(
|
|
"fetch",
|
|
vi.fn((input: string | URL | Request) => {
|
|
const pathname =
|
|
typeof input === "string" ? input : input instanceof URL ? input.toString() : input.url;
|
|
return Promise.resolve(
|
|
response(
|
|
pathname === "/api/v1/collection" ? failedCollection : failedDocument,
|
|
),
|
|
);
|
|
}),
|
|
);
|
|
|
|
render(<App />);
|
|
|
|
expect(await screen.findByRole("heading", { name: "失败文档只展示审计证据" })).toBeInTheDocument();
|
|
expect(screen.getByText("测试 Modifier 失败。")).toBeInTheDocument();
|
|
expect(screen.queryByTestId("diff-view")).not.toBeInTheDocument();
|
|
});
|
|
});
|