Files
ctms/frontend/src/views/admin/ProjectDetail.test.ts
T
2026-05-11 11:03:06 +08:00

84 lines
4.3 KiB
TypeScript

import { describe, expect, it } from "vitest";
import { readFileSync } from "node:fs";
import { resolve } from "node:path";
const readProjectDetail = () => readFileSync(resolve(__dirname, "./ProjectDetail.vue"), "utf8");
describe("ProjectDetail setup draft publish workflow", () => {
it("marks the draft server-synced after a successful server draft save", () => {
const source = readProjectDetail();
const functionIndex = source.indexOf("const persistSetupDraft = async");
const successIndex = source.indexOf("applySetupResponseMeta(data);", functionIndex);
const syncedIndex = source.indexOf("markSetupServerSynced();", successIndex);
expect(functionIndex).toBeGreaterThan(-1);
expect(successIndex).toBeGreaterThan(-1);
expect(syncedIndex).toBeGreaterThan(successIndex);
});
it("reconciles stale dirty flags before opening the publish dialog", () => {
const source = readProjectDetail();
const functionIndex = source.indexOf("const publishConfigNow = async");
const reconcileIndex = source.indexOf("reconcileDraftSyncStateBeforePublish();", functionIndex);
const unsavedGuardIndex = source.indexOf("if (hasSetupDraftUnsavedChanges.value)", functionIndex);
expect(functionIndex).toBeGreaterThan(-1);
expect(reconcileIndex).toBeGreaterThan(functionIndex);
expect(unsavedGuardIndex).toBeGreaterThan(reconcileIndex);
});
it("uses an empty project snapshot as the first publish diff baseline", () => {
const source = readProjectDetail();
const computedIndex = source.indexOf("const projectPublishCompareBase = computed");
const emptyBaselineIndex = source.indexOf("return isFirstPublish.value ? emptyProjectInfoDraft() : null;", computedIndex);
expect(computedIndex).toBeGreaterThan(-1);
expect(emptyBaselineIndex).toBeGreaterThan(computedIndex);
});
it("clears the full setup draft including project info", () => {
const source = readProjectDetail();
const functionIndex = source.indexOf("const handleClearDraft = async");
const confirmTextIndex = source.indexOf("确认清空当前立项配置草稿的所有数据?该操作不会影响已发布版本。", functionIndex);
const applyServerDataIndex = source.indexOf("applySetupDraft(clone(data.data));", functionIndex);
const clearProjectDraftIndex = source.indexOf("clearLocalProjectDraft();", applyServerDataIndex);
expect(functionIndex).toBeGreaterThan(-1);
expect(confirmTextIndex).toBeGreaterThan(functionIndex);
expect(applyServerDataIndex).toBeGreaterThan(confirmTextIndex);
expect(clearProjectDraftIndex).toBeGreaterThan(applyServerDataIndex);
expect(source.indexOf("applySetupDraft(createDefaultSetupDraft(siteOptions.value));", functionIndex)).toBe(-1);
});
it("applies setup drafts atomically including empty project info", () => {
const source = readProjectDetail();
const functionIndex = source.indexOf("const applySetupDraft = (draft: SetupConfigDraft) => {");
const nextFunctionIndex = source.indexOf("const loadLocalSetupDraft = ", functionIndex);
const body = source.slice(functionIndex, nextFunctionIndex);
expect(functionIndex).toBeGreaterThan(-1);
expect(body).toContain("setupDraft.projectInfo = projectInfo;");
expect(body).toContain("applyProjectInfoDraft(projectInfo);");
expect(body).not.toContain("shouldApplyProjectInfo");
expect(body).not.toContain("isSetupDraftEffectivelyEmpty(setupDraft)");
});
it("requires projectInfo in setup draft shape", () => {
const source = readProjectDetail();
const functionIndex = source.indexOf("const isSetupDraftShape = (value: unknown): value is SetupConfigDraft => {");
const nextFunctionIndex = source.indexOf("const isProjectPublishSnapshotShape = ", functionIndex);
const body = source.slice(functionIndex, nextFunctionIndex);
expect(functionIndex).toBeGreaterThan(-1);
expect(body).toContain("isProjectPublishSnapshotShape(data.projectInfo)");
expect(body).not.toContain("!Object.prototype.hasOwnProperty.call(data, \"projectInfo\")");
});
it("keeps early termination out of regular visit schedule options", () => {
const source = readProjectDetail();
expect(source).toContain('const specialVisitOptions = ["筛选访视", "基线访视", "终止治疗"];');
expect(source).toContain("提前终止不作为常规计划访视配置");
});
});