整理测试代码并清理文档引用
This commit is contained in:
@@ -1,22 +0,0 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { readFileSync } from "node:fs";
|
||||
import { resolve } from "node:path";
|
||||
|
||||
const readForm = () => readFileSync(resolve(__dirname, "./KickoffForm.vue"), "utf8");
|
||||
const readDetail = () => readFileSync(resolve(__dirname, "./KickoffDetail.vue"), "utf8");
|
||||
|
||||
describe("Kickoff member permissions", () => {
|
||||
it("does not load project members in the form unless the role can read project members", () => {
|
||||
const source = readForm();
|
||||
|
||||
expect(source).toContain('can("project.members.list")');
|
||||
expect(source).toContain("if (canReadMembers.value)");
|
||||
});
|
||||
|
||||
it("does not load project members in the detail page unless the role can read project members", () => {
|
||||
const source = readDetail();
|
||||
|
||||
expect(source).toContain('can("project.members.list")');
|
||||
expect(source).toContain("if (canReadMembers.value)");
|
||||
});
|
||||
});
|
||||
@@ -1,76 +0,0 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { readFileSync } from "node:fs";
|
||||
import { resolve } from "node:path";
|
||||
|
||||
const read = (relativePath: string) => readFileSync(resolve(__dirname, relativePath), "utf8");
|
||||
|
||||
describe("startup auth editor drawer attachments", () => {
|
||||
it("uses upload cards for kickoff attachments instead of full attachment tables", () => {
|
||||
const source = read("./KickoffEditorDrawer.vue");
|
||||
|
||||
expect(source).toContain("AttachmentList");
|
||||
expect(source).toContain('ref="attachmentPanelRef"');
|
||||
expect(source).toContain(':mode="\'upload\'"');
|
||||
expect(source).toContain(":entity-groups=\"kickoffAttachmentGroups\"");
|
||||
expect(source).toContain("attachments: attachmentPanelRef.value?.pendingSnapshot() || []");
|
||||
expect(source).toContain("await attachmentPanelRef.value?.uploadPending(props.meetingId)");
|
||||
expect(source).toContain('entityType: "startup_kickoff_minutes"');
|
||||
expect(source).toContain('entityType: "startup_kickoff_signin"');
|
||||
expect(source).toContain('entityType: "startup_kickoff_ppt"');
|
||||
expect(source).toContain('entityType: "startup_kickoff"');
|
||||
expect(source).not.toContain("PendingAttachmentUpload");
|
||||
expect(source).not.toContain("attendees");
|
||||
expect(source).not.toContain("parseAttendees");
|
||||
expect(source).not.toContain("attendeesPlaceholder");
|
||||
});
|
||||
|
||||
it("does not expose attachments in the training authorization editor", () => {
|
||||
const source = read("./TrainingEditorDrawer.vue");
|
||||
|
||||
expect(source).not.toContain("PendingAttachmentUpload");
|
||||
expect(source).not.toContain("AttachmentList");
|
||||
expect(source).not.toContain("training_authorization");
|
||||
expect(source).not.toContain("attachments:");
|
||||
expect(source).not.toContain("uploadPending");
|
||||
});
|
||||
|
||||
it("supports batch creating and single-row updating training authorizations in the same drawer", () => {
|
||||
const source = read("./TrainingEditorDrawer.vue");
|
||||
|
||||
expect(source).toContain("recordId?: string");
|
||||
expect(source).toContain('editorTitle');
|
||||
expect(source).toContain("createTrainingAuthorization");
|
||||
expect(source).toContain("updateTrainingAuthorization");
|
||||
expect(source).toContain("const isCreateMode = computed(() => !props.recordId)");
|
||||
expect(source).toContain("const canCreateAuth = computed(() => can(\"startup.auth.create\"))");
|
||||
expect(source).toContain("const canSaveAuth = computed(() => (isCreateMode.value ? canCreateAuth.value : canUpdateAuth.value))");
|
||||
expect(source).toContain("batchRows");
|
||||
expect(source).toContain("addBatchRow");
|
||||
expect(source).toContain("removeBatchRow");
|
||||
expect(source).toContain("for (const row of batchRows.value)");
|
||||
});
|
||||
|
||||
it("keeps training authorization editor compact and hides site input while preserving site context", () => {
|
||||
const source = read("./TrainingEditorDrawer.vue");
|
||||
|
||||
expect(source).not.toContain(':label="TEXT.common.fields.site"');
|
||||
expect(source).not.toContain('prop="site_name"');
|
||||
expect(source).toContain("site_name: props.initialSiteName || form.site_name || null");
|
||||
expect(source).toContain('class="compact-row-grid"');
|
||||
expect(source).toContain('class="compact-field compact-field--name"');
|
||||
expect(source).toContain('class="compact-field compact-field--status"');
|
||||
expect(source).toContain('class="compact-field compact-field--remark"');
|
||||
});
|
||||
|
||||
it("uses a single-layer batch editor layout for training authorization creation", () => {
|
||||
const source = read("./TrainingEditorDrawer.vue");
|
||||
|
||||
expect(source).toContain('class="form-section training-batch-editor"');
|
||||
expect(source).toContain('class="batch-editor"');
|
||||
expect(source).toContain('class="batch-entry"');
|
||||
expect(source).toContain('class="status-inline"');
|
||||
expect(source).toContain('class="status-date"');
|
||||
expect(source).not.toContain('class="batch-row"');
|
||||
expect(source).not.toContain(".batch-row {");
|
||||
});
|
||||
});
|
||||
@@ -1,125 +0,0 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { readFileSync } from "node:fs";
|
||||
import { resolve } from "node:path";
|
||||
|
||||
const read = (relativePath: string) => readFileSync(resolve(__dirname, relativePath), "utf8");
|
||||
|
||||
describe("startup auth form/detail permissions", () => {
|
||||
it("maps startup auth actions to backend operation permissions", () => {
|
||||
const source = read("../../utils/permission.ts");
|
||||
|
||||
expect(source).toContain('"startup.auth.create": "startup_auth:create"');
|
||||
expect(source).toContain('"startup.auth.update": "startup_auth:update"');
|
||||
expect(source).toContain('"startup.auth.delete": "startup_auth:delete"');
|
||||
});
|
||||
|
||||
it("uses create/update permissions in kickoff and training forms", () => {
|
||||
const kickoffForm = read("./KickoffForm.vue");
|
||||
const trainingForm = read("./TrainingForm.vue");
|
||||
|
||||
expect(kickoffForm).toContain('const canCreateAuth = computed(() => can("startup.auth.create"))');
|
||||
expect(kickoffForm).toContain('const canUpdateAuth = computed(() => can("startup.auth.update"))');
|
||||
expect(kickoffForm).toContain("const canSaveAuth = computed(() => (isEdit.value ? canUpdateAuth.value : canCreateAuth.value))");
|
||||
expect(kickoffForm).toContain("if (!canSaveAuth.value)");
|
||||
expect(trainingForm).toContain('const canCreateAuth = computed(() => can("startup.auth.create"))');
|
||||
expect(trainingForm).toContain('const canUpdateAuth = computed(() => can("startup.auth.update"))');
|
||||
expect(trainingForm).toContain("const canSaveAuth = computed(() => (isEdit.value ? canUpdateAuth.value : canCreateAuth.value))");
|
||||
expect(trainingForm).toContain("if (!canSaveAuth.value)");
|
||||
});
|
||||
|
||||
it("opens kickoff detail edits in a drawer and keeps training actions split by backend operations", () => {
|
||||
const source = read("./KickoffDetail.vue");
|
||||
|
||||
expect(source).toContain("<KickoffEditorDrawer");
|
||||
expect(source).toContain("<TrainingEditorDrawer");
|
||||
expect(source).toContain("editorVisible.value = true");
|
||||
expect(source).toContain("trainingEditorVisible.value = true");
|
||||
expect(source).toContain("const handleEditorSaved = () =>");
|
||||
expect(source).toContain(":refresh-key=\"attachmentRefreshKey\"");
|
||||
expect(source).toContain("attachmentRefreshKey.value += 1");
|
||||
expect(source).toContain("const handleTrainingEditorSaved = () =>");
|
||||
expect(source).toContain('const canCreateAuth = computed(() => can("startup.auth.create"))');
|
||||
expect(source).toContain('const canUpdateAuth = computed(() => can("startup.auth.update"))');
|
||||
expect(source).toContain('const canDeleteAuth = computed(() => can("startup.auth.delete"))');
|
||||
expect(source).toContain('v-if="canUpdateAuth"');
|
||||
expect(source).toContain('v-if="canCreateAuth || canUpdateAuth || canDeleteAuth"');
|
||||
expect(source).toContain("if (!canCreateAuth.value)");
|
||||
expect(source).toContain("if (!canUpdateAuth.value)");
|
||||
expect(source).toContain("if (!canDeleteAuth.value)");
|
||||
expect(source).not.toContain("trainingRowDraft");
|
||||
expect(source).not.toContain("isTrainingEditing");
|
||||
expect(source).not.toContain("saveTraining");
|
||||
expect(source).not.toContain("cancelTrainingEdit");
|
||||
expect(source).not.toContain("editMode");
|
||||
expect(source).not.toContain("saveEdit");
|
||||
});
|
||||
|
||||
it("opens training detail edits in a drawer without navigating to the old edit page", () => {
|
||||
const source = read("./TrainingDetail.vue");
|
||||
|
||||
expect(source).toContain("<TrainingEditorDrawer");
|
||||
expect(source).toContain("editorVisible.value = true");
|
||||
expect(source).toContain("const handleEditorSaved = () =>");
|
||||
expect(source).toContain('const canUpdateAuth = computed(() => can("startup.auth.update"))');
|
||||
expect(source).toContain('v-if="canUpdateAuth"');
|
||||
expect(source).toContain("if (!canUpdateAuth.value)");
|
||||
expect(source).not.toContain("AttachmentList");
|
||||
expect(source).not.toContain("training_authorization");
|
||||
expect(source).not.toContain("attachmentRefreshKey");
|
||||
expect(source).not.toContain("router.push(`/startup/training/${recordId}/edit`)");
|
||||
});
|
||||
|
||||
it("renders kickoff attachments as one aggregated table with attachment type labels", () => {
|
||||
const source = read("./KickoffDetail.vue");
|
||||
|
||||
expect(source).toContain(":entity-groups=\"kickoffAttachmentGroups\"");
|
||||
expect(source).not.toContain("v-for=\"group in kickoffAttachmentGroups\"");
|
||||
expect(source).toContain('label: TEXT.modules.startupMeetingAuth.kickoffMinutesLabel');
|
||||
expect(source).toContain('label: TEXT.modules.startupMeetingAuth.kickoffSignInLabel');
|
||||
expect(source).toContain('label: TEXT.modules.startupMeetingAuth.kickoffPptLabel');
|
||||
expect(source).toContain('label: TEXT.modules.startupMeetingAuth.kickoffOtherLabel');
|
||||
});
|
||||
|
||||
it("allows legacy kickoff and training forms to queue attachments before saving", () => {
|
||||
const kickoffForm = read("./KickoffForm.vue");
|
||||
const trainingForm = read("./TrainingForm.vue");
|
||||
|
||||
expect(kickoffForm).toContain("<AttachmentList");
|
||||
expect(kickoffForm).toContain('ref="attachmentPanelRef"');
|
||||
expect(kickoffForm).toContain(":entity-groups=\"kickoffAttachmentGroups\"");
|
||||
expect(kickoffForm).toContain(':entity-id="meetingId || \'\'"');
|
||||
expect(kickoffForm).toContain(':mode="\'upload\'"');
|
||||
expect(kickoffForm).toContain("await attachmentPanelRef.value?.uploadPending(savedId)");
|
||||
expect(kickoffForm).not.toContain('v-for="group in kickoffAttachmentGroups"');
|
||||
expect(kickoffForm).not.toContain("StateEmpty");
|
||||
|
||||
expect(trainingForm).toContain("<AttachmentList");
|
||||
expect(trainingForm).toContain('ref="attachmentPanelRef"');
|
||||
expect(trainingForm).toContain('entity-type="training_authorization"');
|
||||
expect(trainingForm).toContain(':entity-id="recordId || \'\'"');
|
||||
expect(trainingForm).toContain(':mode="\'upload\'"');
|
||||
expect(trainingForm).toContain("await attachmentPanelRef.value?.uploadPending(savedId)");
|
||||
expect(trainingForm).not.toContain('v-if="isEdit && recordId"');
|
||||
expect(trainingForm).not.toContain("StateEmpty");
|
||||
});
|
||||
|
||||
it("shows training and authorization dates under positive status in the training table", () => {
|
||||
const source = read("./KickoffDetail.vue");
|
||||
|
||||
expect(source).toContain("status-dot--yes");
|
||||
expect(source).toContain("status-dot--no");
|
||||
expect(source).toContain("scope.row.trained && scope.row.trained_date");
|
||||
expect(source).toContain("scope.row.authorized && scope.row.authorized_date");
|
||||
expect(source).toContain('class="status-date"');
|
||||
expect(source).toContain("scope.row.trained ? TEXT.common.boolean.yes : TEXT.common.boolean.no");
|
||||
expect(source).toContain("scope.row.authorized ? TEXT.common.boolean.yes : TEXT.common.boolean.no");
|
||||
});
|
||||
|
||||
it("loads training authorizations for the current kickoff site only", () => {
|
||||
const source = read("./KickoffDetail.vue");
|
||||
|
||||
expect(source).toContain("if (!studyId || !siteInfo.name) return;");
|
||||
expect(source).toContain("listTrainingAuthorizations(studyId, { site_name: siteInfo.name })");
|
||||
expect(source).not.toContain("listTrainingAuthorizations(studyId);");
|
||||
});
|
||||
});
|
||||
@@ -1,84 +0,0 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { readFileSync } from "node:fs";
|
||||
import { resolve } from "node:path";
|
||||
|
||||
const read = (relativePath: string) => readFileSync(resolve(__dirname, relativePath), "utf8");
|
||||
|
||||
describe("startup initiation and ethics form/detail permissions", () => {
|
||||
it("uses startup initiation create/update permissions in feasibility form and detail", () => {
|
||||
const formSource = read("./FeasibilityForm.vue");
|
||||
const detailSource = read("./FeasibilityDetail.vue");
|
||||
|
||||
expect(formSource).toContain('const canCreateInitiation = computed(() => can("startup.initiation.create"))');
|
||||
expect(formSource).toContain('const canUpdateInitiation = computed(() => can("startup.initiation.update"))');
|
||||
expect(formSource).toContain("const canSaveInitiation = computed(() => (isEdit.value ? canUpdateInitiation.value : canCreateInitiation.value))");
|
||||
expect(formSource).toContain("if (!canSaveInitiation.value)");
|
||||
expect(detailSource).toContain('const canUpdateInitiation = computed(() => can("startup.initiation.update"))');
|
||||
expect(detailSource).toContain('v-if="canUpdateInitiation"');
|
||||
expect(detailSource).toContain("if (!canUpdateInitiation.value)");
|
||||
});
|
||||
|
||||
it("uses startup ethics create/update permissions in ethics form and detail", () => {
|
||||
const formSource = read("./EthicsForm.vue");
|
||||
const detailSource = read("./EthicsDetail.vue");
|
||||
|
||||
expect(formSource).toContain('const canCreateEthics = computed(() => can("startup.ethics.create"))');
|
||||
expect(formSource).toContain('const canUpdateEthics = computed(() => can("startup.ethics.update"))');
|
||||
expect(formSource).toContain("const canSaveEthics = computed(() => (isEdit.value ? canUpdateEthics.value : canCreateEthics.value))");
|
||||
expect(formSource).toContain("if (!canSaveEthics.value)");
|
||||
expect(detailSource).toContain('const canUpdateEthics = computed(() => can("startup.ethics.update"))');
|
||||
expect(detailSource).toContain('v-if="canUpdateEthics"');
|
||||
expect(detailSource).toContain("if (!canUpdateEthics.value)");
|
||||
});
|
||||
|
||||
it("keeps detail attachment areas display-only and moves upload into editor drawers", () => {
|
||||
const feasibilityDetail = read("./FeasibilityDetail.vue");
|
||||
const ethicsDetail = read("./EthicsDetail.vue");
|
||||
const feasibilityDrawer = read("./FeasibilityEditorDrawer.vue");
|
||||
const ethicsDrawer = read("./EthicsEditorDrawer.vue");
|
||||
|
||||
expect(feasibilityDetail).toContain(":hide-uploader=\"true\"");
|
||||
expect(ethicsDetail).toContain(":hide-uploader=\"true\"");
|
||||
expect(feasibilityDrawer).toContain("AttachmentList");
|
||||
expect(feasibilityDrawer).toContain('ref="attachmentPanelRef"');
|
||||
expect(feasibilityDrawer).toContain('entity-type="startup_feasibility"');
|
||||
expect(feasibilityDrawer).toContain(":entity-id=\"recordId || ''\"");
|
||||
expect(feasibilityDrawer).toContain(':mode="\'upload\'"');
|
||||
expect(feasibilityDrawer).toContain("attachments: attachmentPanelRef.value?.pendingSnapshot() || []");
|
||||
expect(feasibilityDrawer).toContain("await attachmentPanelRef.value?.uploadPending(id)");
|
||||
expect(feasibilityDrawer).not.toContain("PendingAttachmentUpload");
|
||||
expect(ethicsDrawer).toContain("AttachmentList");
|
||||
expect(ethicsDrawer).toContain('ref="attachmentPanelRef"');
|
||||
expect(ethicsDrawer).toContain('entity-type="startup_ethics"');
|
||||
expect(ethicsDrawer).toContain(":entity-id=\"recordId || ''\"");
|
||||
expect(ethicsDrawer).toContain(':mode="\'upload\'"');
|
||||
expect(ethicsDrawer).toContain("attachments: attachmentPanelRef.value?.pendingSnapshot() || []");
|
||||
expect(ethicsDrawer).toContain("await attachmentPanelRef.value?.uploadPending(id)");
|
||||
expect(ethicsDrawer).not.toContain("PendingAttachmentUpload");
|
||||
expect(feasibilityDrawer).not.toContain('class="attachment-group"');
|
||||
expect(ethicsDrawer).not.toContain('class="attachment-group"');
|
||||
});
|
||||
|
||||
it("allows legacy feasibility and ethics forms to queue attachments before the record exists", () => {
|
||||
const feasibilityForm = read("./FeasibilityForm.vue");
|
||||
const ethicsForm = read("./EthicsForm.vue");
|
||||
|
||||
expect(feasibilityForm).toContain("<AttachmentList");
|
||||
expect(feasibilityForm).toContain('ref="attachmentPanelRef"');
|
||||
expect(feasibilityForm).toContain('entity-type="startup_feasibility"');
|
||||
expect(feasibilityForm).toContain(':entity-id="recordId || \'\'"');
|
||||
expect(feasibilityForm).toContain(':mode="\'upload\'"');
|
||||
expect(feasibilityForm).toContain("await attachmentPanelRef.value?.uploadPending(savedId)");
|
||||
expect(feasibilityForm).not.toContain('v-if="isEdit && recordId"');
|
||||
expect(feasibilityForm).not.toContain("StateEmpty");
|
||||
|
||||
expect(ethicsForm).toContain("<AttachmentList");
|
||||
expect(ethicsForm).toContain('ref="attachmentPanelRef"');
|
||||
expect(ethicsForm).toContain('entity-type="startup_ethics"');
|
||||
expect(ethicsForm).toContain(':entity-id="recordId || \'\'"');
|
||||
expect(ethicsForm).toContain(':mode="\'upload\'"');
|
||||
expect(ethicsForm).toContain("await attachmentPanelRef.value?.uploadPending(savedId)");
|
||||
expect(ethicsForm).not.toContain('v-if="isEdit && recordId"');
|
||||
expect(ethicsForm).not.toContain("StateEmpty");
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user