84 lines
4.3 KiB
TypeScript
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("提前终止不作为常规计划访视配置");
|
|
});
|
|
});
|