import { describe, expect, it } from "vitest";
import { readFileSync } from "node:fs";
import { resolve } from "node:path";
const readSubjectDetail = () => readFileSync(resolve(__dirname, "./SubjectDetail.vue"), "utf8");
describe("SubjectDetail medication adherence", () => {
it("adds a medication adherence tab with editable actual count", () => {
const source = readSubjectDetail();
expect(source).toContain('name="medicationAdherence"');
expect(source).toContain("actual_medication_count");
expect(source).toContain("saveMedicationAdherence");
expect(source).toContain("
用药依从性
");
expect(source).toContain("实际用药次数为试验期间实际药物暴露次数");
expect(source).toContain("75%~125% 范围内(包含边界值)");
});
it("uses baseline and termination visit actual dates for adherence calculation", () => {
const source = readSubjectDetail();
expect(source).toContain("const medicationStartDate = computed(() => detail.baseline_date || \"\");");
expect(source).toContain('new Set(["终止治疗", "提前终止"])');
expect(source).toContain("return Math.floor((end.getTime() - start.getTime()) / 86400000) + 1;");
});
it("marks adherence outside 75 to 125 percent as abnormal", () => {
const source = readSubjectDetail();
expect(source).toContain("if (rate < 75 || rate > 125) return \"danger\";");
expect(source).not.toContain("if (rate < 80 || rate > 120) return \"danger\";");
});
it("provides a dedicated early termination workflow", () => {
const source = readSubjectDetail();
expect(source).toContain("openEarlyTerminationDialog");
expect(source).toContain("createEarlyTerminationVisit");
expect(source).toContain("return \"不适用\";");
expect(source).toContain("getVisitStatusLabel");
expect(source).toContain("ElMessage.success(TEXT.common.messages.saveSuccess);");
expect(source).toContain("提前终止已保存,刷新数据失败,请手动刷新页面");
expect(source).toContain("提前终止表示受试者在方案最后一个计划访视窗口期前停止治疗");
expect(source).toContain("validateEarlyTerminationDateInput");
expect(source).toContain("提前终止日期必须早于最后计划访视窗口期开始日");
expect(source).toContain("getVisitPlannedDateLabel");
expect(source).toContain("getVisitWindowRangeLabel");
expect(source).toContain("~");
expect(source).toContain("row.window_start === row.planned_date && row.window_end === row.planned_date");
expect(source).toContain("return \"不适用\";");
});
it("gates participant subresource mutations by concrete API permissions", () => {
const source = readSubjectDetail();
expect(source).toContain("isApiPermissionAllowed");
expect(source).toContain("getProjectRole");
expect(source).toContain("isSystemAdmin");
[
"subjects:update",
"subject_histories:read",
"subject_histories:create",
"subject_histories:update",
"subject_histories:delete",
"visits:list",
"visits:create",
"visits:update",
"visits:delete",
"subject_aes:create",
"subject_aes:read",
"subject_aes:update",
"subject_aes:delete",
"subject_pds:create",
"subject_pds:list",
"subject_pds:update",
"subject_pds:delete",
].forEach((operationKey) => {
expect(source).toContain(operationKey);
});
expect(source).toContain("v-if=\"currentTabAction.allowed\"");
expect(source).toContain("v-if=\"canReadHistory\"");
expect(source).toContain("v-if=\"canReadVisit\"");
expect(source).toContain("v-if=\"canListAe\"");
expect(source).toContain("v-if=\"canListPd\"");
expect(source).toContain("v-if=\"canUpdateHistory || canDeleteHistory\"");
expect(source).toContain("v-if=\"canUpdateVisit || canDeleteVisit\"");
expect(source).toContain("v-if=\"canUpdateAe || canDeleteAe\"");
expect(source).toContain("v-if=\"canUpdatePd || canDeletePd\"");
expect(source).toContain("const canSaveHistory = computed(() => (historyEditingId.value ? canUpdateHistory.value : canCreateHistory.value));");
expect(source).toContain(':can-create="canCreateVisit"');
expect(source).toContain(':can-update="canUpdateVisit"');
expect(source).toContain("const canSaveAe = computed(() => (aeEditingId.value ? canUpdateAe.value : canCreateAe.value));");
expect(source).toContain("const canSavePd = computed(() => (pdEditingId.value ? canUpdatePd.value : canCreatePd.value));");
expect(source).toContain("const canReadHistory = computed(() => canUseApiPermission(\"subject_histories:read\"));");
expect(source).toContain("const canReadVisit = computed(() => canUseApiPermission(\"visits:list\"));");
expect(source).toContain("if (!canReadHistory.value)");
expect(source).toContain("if (!canReadVisit.value)");
expect(source).toContain("const canCreateAe = computed(() => canUseApiPermission(\"subject_aes:create\"));");
expect(source).toContain("const canUpdateAe = computed(() => canUseApiPermission(\"subject_aes:update\"));");
expect(source).toContain("const canDeleteAe = computed(() => canUseApiPermission(\"subject_aes:delete\"));");
expect(source).toContain("const canListAe = computed(() => canUseApiPermission(\"subject_aes:read\"));");
expect(source).toContain("const canListPd = computed(() => canUseApiPermission(\"subject_pds:list\"));");
});
});
describe("SubjectDetail drawer editor", () => {
it("opens participant basic information edit in the shared drawer editor", () => {
const source = readSubjectDetail();
expect(source).toContain("SubjectEditorDrawer");
expect(source).toContain("subjectEditorVisible");
expect(source).toContain("@success=\"handleSubjectEditorSuccess\"");
expect(source).not.toContain("subjectEditing");
expect(source).not.toContain("saveSubjectEdit");
});
it("uses drawer editors for participant subresource create and edit flows", () => {
const source = readSubjectDetail();
expect(source).toContain("VisitEditorDrawer");
expect(source).toContain("adherenceDrawerVisible");
expect(source).toContain("aeDrawerVisible");
expect(source).toContain("pdDrawerVisible");
expect(source).toContain("openVisitDrawer");
expect(source).toContain("openAeDrawer");
expect(source).toContain("openPdDrawer");
expect(source).toContain("openMedicationAdherenceDrawer");
expect(source).not.toContain("visitDialogVisible");
expect(source).not.toContain("aeDialogVisible");
expect(source).not.toContain("pdDialogVisible");
expect(source).not.toContain("adherenceEditing");
expect(source).not.toContain(" {
const readVisitEditorDrawer = () => readFileSync(resolve(__dirname, "./VisitEditorDrawer.vue"), "utf8");
it("keeps create and edit visit flows on the same business fields", () => {
const source = readVisitEditorDrawer();
expect(source).not.toContain("");
expect(source).not.toContain("");
expect(source).toContain("v-model=\"form.actual_date\"");
expect(source).toContain("v-model=\"form.notes\"");
expect(source).toContain("actual_date: form.actual_date || null");
expect(source).toContain("notes: form.notes || null");
});
});