import assert from 'node:assert/strict' import fs from 'node:fs' import test from 'node:test' const routerSource = fs.readFileSync(new URL('../routes.ts', import.meta.url), 'utf8') const profileSource = fs.readFileSync(new URL('./CompanyProfileView.vue', import.meta.url), 'utf8') const membersSource = fs.readFileSync(new URL('./MembersView.vue', import.meta.url), 'utf8') const rolesSource = fs.readFileSync(new URL('./RolesView.vue', import.meta.url), 'utf8') const permissionsSource = fs.readFileSync(new URL('./PermissionsView.vue', import.meta.url), 'utf8') const storeSource = fs.readFileSync(new URL('../store/tenantAdministration.ts', import.meta.url), 'utf8') test('routes tenant administration modules to implemented views instead of the placeholder', () => { assert.match(routerSource, /path: 'company\/profile'[\s\S]*?CompanyProfileView\.vue/) assert.match(routerSource, /path: 'organization\/members'[\s\S]*?MembersView\.vue/) assert.match(routerSource, /path: 'organization\/roles'[\s\S]*?RolesView\.vue/) assert.match(routerSource, /path: 'organization\/permissions'[\s\S]*?PermissionsView\.vue/) assert.equal((routerSource.match(/release: 'active'/g) ?? []).length, 4) assert.doesNotMatch(routerSource, /ModuleView\.vue/) }) test('implements company details, contact information and qualification workflow', () => { assert.match(profileSource, /企业基本信息/) assert.match(profileSource, /联系信息/) assert.match(profileSource, /资质文件/) assert.match(profileSource, /async function saveProfile\(\)/) assert.match(profileSource, /function submitQualification\(\)/) assert.match(profileSource, /validateQualificationFile/) assert.match(profileSource, /function removeSelectedFile\(type: MerchantQualificationType\)/) assert.match(profileSource, /qualificationSubmitting/) assert.match(profileSource, /const qualificationDrafts = reactive/) assert.match(profileSource, /inputVersion: number/) assert.match(profileSource, /:key="`\$\{item\.value\}-\$\{item\.inputVersion\}`"/) assert.match(profileSource, /class="qualification-upload-grid"/) assert.match(profileSource, /v-for="item in qualificationDrafts"/) assert.match(profileSource, /保存全部资质/) assert.match(profileSource, /uploadMerchantRequiredQualificationPackage/) assert.match(profileSource, /const requiredPackageFilesSelected = computed/) assert.match(profileSource, /item\.uploadState = 'uploading'/) assert.match(profileSource, /for \(const item of failures\.length \? \[\] : optionalItems\)/) assert.match(profileSource, /item\.fileName = ''[\s\S]*item\.uploadState = 'uploaded'/) assert.match(profileSource, /const canSaveQualificationBatch = computed/) assert.match(profileSource, /const qualificationCreditCodeReady = computed/) assert.match(profileSource, /const qualificationDialogHint = computed/) assert.match(profileSource, /qualificationSaveDisabledReason\.value === qualificationCreditCodeError\.value/) assert.match(profileSource, /previewUrl: string/) assert.match(profileSource, /URL\.createObjectURL\(raw\)/) assert.match(profileSource, /URL\.revokeObjectURL\(item\.previewUrl\)/) assert.match(profileSource, /class="selected-file-preview"/) assert.match(profileSource, /待上传图片预览/) assert.match(profileSource, /:close-on-click-modal="!qualificationSubmitting"/) assert.match(profileSource, /:close-on-press-escape="!qualificationSubmitting"/) assert.match(profileSource, /:show-close="!qualificationSubmitting"/) assert.match(profileSource, /const requiredPackageComplete = computed/) assert.match(profileSource, /营业执照和18位统一社会信用代码需共同保存;法人身份证及其他资质可按需上传/) assert.match(profileSource, /已登记的统一社会信用代码不可直接修改/) assert.match(profileSource, /companyCreditCodeValid\.value/) assert.doesNotMatch(profileSource, / type\.required\)/) assert.match(profileSource, /货代资质证书'.*required: false/) assert.match(profileSource, /法人身份证'.*required: false/) assert.match(profileSource, /银行开户许可证'.*required: false/) assert.match(profileSource, /requiredQualificationTypes\.every/) assert.doesNotMatch(profileSource, /四类必填资质/) assert.doesNotMatch(profileSource, /法人身份证需与本次营业执照一起保存/) assert.match(profileSource, /const standaloneLegalPersonIdUnsupported = computed/) assert.match(profileSource, /当前文件服务尚未开放法人身份证单独上传或替换,该选填文件未提交/) assert.match(profileSource, /qualificationUploadErrorMessage\(error\)/) assert.match(profileSource, /submitTenantQualificationReview/) assert.match(profileSource, /提交企业审核/) assert.match(profileSource, /营业执照和统一社会信用代码已保存,尚未提交审核/) assert.match(profileSource, /资质文件已保存,但最新状态刷新失败/) assert.match(profileSource, /企业资料已提交审核,但最新状态刷新失败/) assert.match(profileSource, /qualificationStatusMeta/) assert.match(profileSource, /const canUploadQualification = computed/) assert.match(profileSource, /:disabled="!canUploadQualification"/) assert.doesNotMatch(profileSource, /error\.code === 'A0409'\) break/) assert.match(profileSource, /downloadQualificationImage/) assert.match(profileSource, /当前资质文件尚未迁移到安全预览地址/) assert.match(profileSource, /previewError/) assert.match(profileSource, /URL\.createObjectURL/) assert.match(profileSource, /URL\.revokeObjectURL/) assert.match(profileSource, /企业资料加载失败/) assert.match(profileSource, /@click="loadCompanyContext"/) assert.match(profileSource, /
企业类型<\/dt>
\{\{ store\.company\.enterpriseType/) assert.match(profileSource, /
注册地<\/dt>
\{\{ store\.company\.registeredAddress/) assert.match(profileSource, /
成立日期<\/dt>
\{\{ store\.company\.establishedDate/) assert.match(profileSource, /
注册资本<\/dt>
\{\{ store\.company\.registeredCapital/) assert.match(profileSource, /
联系微信<\/dt>
\{\{ store\.company\.contactWechat/) assert.match(profileSource, /
公司联系电话<\/dt>
\{\{ store\.company\.contactPhone/) assert.match(profileSource, /label="公司联系电话" prop="contactPhone" required/) assert.match(profileSource, /不会修改任何成员的登录手机号/) assert.match(profileSource, /pattern: \/\^\[0-9-\]\{4,32\}\$\//) assert.doesNotMatch(profileSource, /
联系电话<\/dt>
\{\{ store\.company\.contactPhone/) assert.match(storeSource, /expiresAt: item\.validTo \|\| '长期有效'/) }) test('implements member list, role assignment, password reset and status control', () => { assert.match(membersSource, /async function saveMember\(\)/) assert.match(membersSource, /function resetPassword\(member: TenantMember\)/) assert.match(membersSource, /function toggleStatus\(member: TenantMember\)/) assert.match(membersSource, /v-model="form\.roleIds" multiple/) assert.match(membersSource, /class="mobile-member-list"/) assert.match(membersSource, /class="mobile-member-empty"/) assert.doesNotMatch(membersSource, /class="mobile-member-empty client-empty"/) assert.match(membersSource, /\.mobile-member-list, \.mobile-member-empty \{ display: none; \}/) assert.match(membersSource, /\.mobile-member-empty \{ display: grid; min-height: 150px; place-items: center;/) assert.match(membersSource, /store\.loadMembers\(\)/) assert.match(membersSource, /await store\.resetMemberPassword/) assert.match(membersSource, /await store\.toggleMemberStatus/) assert.match(membersSource, /v-model="form\.password"/) assert.match(membersSource, /企业成员加载失败/) assert.match(membersSource, /@click="loadMembers"/) assert.match(membersSource, /const memberRules: FormRules/) assert.match(membersSource, /ref="formRef" :model="form" :rules="memberRules"/) assert.match(membersSource, /prop="roleIds" required/) assert.match(membersSource, /await formRef\.value\?\.validate\(\)/) assert.match(membersSource, /import QueryToolbar.*QueryToolbar\.vue/) assert.match(membersSource, /import DataTable.*DataTable\.vue/) assert.match(membersSource, /\s*]*class="desktop-member-table"/) assert.match(membersSource, /\.desktop-member-table \{ display: none; \}/) assert.doesNotMatch(membersSource, /\.desktop-member-table :deep\(\.el-table\)/) assert.match(membersSource, /
/) assert.doesNotMatch(membersSource, /]*:loading="loading"/) assert.match(membersSource, /fixed: 'left'/) assert.match(membersSource, /class="table-actions"/) assert.match(membersSource, /canCreateMember.*company:member:create/) assert.match(membersSource, /canEditMember.*company:member:edit/) assert.match(membersSource, /canGrantMemberRole.*company:member:grant-role/) assert.match(membersSource, /canChangeMemberStatus.*company:member:status/) assert.doesNotMatch(membersSource, /company:member:write/) assert.doesNotMatch(membersSource, /member-metrics/) assert.match(membersSource, /label="登录手机号" prop="phone"/) assert.match(membersSource, /登录手机号 \/ 邮箱/) assert.match(membersSource, /用于该成员手机号登录;修改后会同步认证账号并退出该成员现有会话/) assert.match(membersSource, /phone: \[\{ pattern: \/\^1\[3-9\]\\d\{9\}\$\//) assert.doesNotMatch(membersSource, /label="联系电话" prop="phone"/) }) test('protects built-in roles and exposes role permission assignment', () => { assert.match(rolesSource, /canCreateRole.*company:role:create/) assert.match(rolesSource, /canEditRole.*company:role:edit/) assert.match(rolesSource, /canChangeRoleStatus.*company:role:status/) assert.match(rolesSource, /canDeleteRole.*company:role:delete/) assert.doesNotMatch(rolesSource, /company:role:write/) assert.match(rolesSource, /function assignPermissions\(role: TenantRole\)/) assert.match(storeSource, /if \(role\.builtin\) throw new Error\('内置角色不能删除'\)/) assert.match(storeSource, /if \(members\.value\.some\(\(member\) => member\.roleIds\.includes\(id\)\)\)/) assert.match(rolesSource, /await store\.loadRoles\(\)/) assert.doesNotMatch(rolesSource, /store\.loadMembers\(\)/) assert.match(rolesSource, /await store\.createRole/) assert.match(rolesSource, /await store\.updateRole/) assert.match(rolesSource, /await store\.toggleRoleStatus/) assert.match(rolesSource, /await store\.deleteRole/) assert.match(rolesSource, /企业角色加载失败/) assert.match(rolesSource, /@click="loadRoles"/) assert.match(rolesSource, /const roleRules: FormRules/) assert.match(rolesSource, /ref="formRef" :model="form" :rules="roleRules"/) assert.match(rolesSource, /placeholder="例如 订单操作员"/) assert.match(rolesSource, /import QueryToolbar.*QueryToolbar\.vue/) assert.match(rolesSource, /import DataTable.*DataTable\.vue/) assert.match(rolesSource, //) assert.doesNotMatch(rolesSource, /]*:loading="loading"/) assert.match(rolesSource, /fixed: 'left'/) assert.match(rolesSource, /class="table-actions"/) assert.match(rolesSource, /canEditRole && !\(row as TenantRole\)\.builtin/) assert.doesNotMatch(rolesSource, /role-guidance/) }) test('shows company audit status and applies button permissions', () => { assert.match(profileSource, /companyApproved/) assert.match(profileSource, /ARCHIVED: \{ title: '企业已归档'/) assert.match(profileSource, /auditStatus !== 'ARCHIVED'/) assert.match(profileSource, /企业资料正在等待平台审核/) assert.match(profileSource, /auth\.hasPermission\('company:profile:edit'\)/) assert.match(membersSource, /company:member:create/) assert.match(membersSource, /company:member:edit/) assert.match(membersSource, /company:member:status/) assert.match(permissionsSource, /auth\.hasPermission\('company:permission:assign'\)/) assert.match(storeSource, /auditStatus: 'PROFILE_REQUIRED'/) assert.match(storeSource, /loadCompanyContext/) }) test('implements grouped menu and action permission editing', () => { assert.match(permissionsSource, /TENANT_PERMISSION_GROUPS/) assert.match(permissionsSource, /function toggleGroup\(groupKey: string, checked: boolean\)/) assert.match(permissionsSource, /function savePermissions\(\)/) assert.match(permissionsSource, /系统内置角色,权限由平台统一维护,不允许修改/) assert.match(permissionsSource, /全量替换/) assert.match(permissionsSource, /await store\.setRolePermissions/) assert.match(permissionsSource, /角色权限加载失败/) assert.match(permissionsSource, /当前企业暂无可用角色/) assert.match(permissionsSource, /const saving = ref\(false\)/) assert.match(permissionsSource, /:loading="saving"/) assert.match(permissionsSource, /button:focus-visible/) assert.match(permissionsSource, /class="permission-actions" aria-label="权限操作"/) assert.match(permissionsSource, /]*class="permission-item"/) assert.doesNotMatch(permissionsSource, /