200 lines
14 KiB
JavaScript
200 lines
14 KiB
JavaScript
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<QualificationDraftItem\[\]>/)
|
|
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, /<el-select v-model="qualificationDraft\.type"/)
|
|
assert.match(profileSource, /class="mobile-qualification-list"/)
|
|
assert.match(profileSource, /import DataTable, \{ type DataTableColumn \} from '@\/components\/common\/DataTable\.vue'/)
|
|
assert.match(profileSource, /const qualificationColumns: DataTableColumn\[\]/)
|
|
assert.match(profileSource, /slotName: 'actions'/)
|
|
assert.match(profileSource, /<DataTable[\s\S]*v-model:page="qualificationPage"[\s\S]*v-model:page-size="qualificationPageSize"[\s\S]*:columns="qualificationColumns"/)
|
|
assert.doesNotMatch(profileSource, /<el-table class="desktop-qualification-table"/)
|
|
assert.doesNotMatch(profileSource, /class="profile-summary-card"/)
|
|
assert.match(profileSource, /class="info-panel-header"[\s\S]*auth\.hasPermission\('company:profile:edit'\)/)
|
|
assert.match(profileSource, /ref="profileFormRef" :model="draft" :rules="profileFormRules"/)
|
|
assert.match(profileSource, /prop="companyName" required><el-input v-model="draft\.companyName" placeholder="请输入公司名称"/)
|
|
assert.match(profileSource, /const valid = await profileFormRef\.value\?\.validate\(\)/)
|
|
assert.match(profileSource, /const requiredQualificationTypes = qualificationTypes\.filter\(\(type\) => type\.required\)/)
|
|
assert.match(profileSource, /货代资质证书'.*required: false/)
|
|
assert.match(profileSource, /银行开户许可证'.*required: false/)
|
|
assert.match(profileSource, /requiredQualificationTypes\.every/)
|
|
assert.doesNotMatch(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.doesNotMatch(profileSource, /企业类型|注册资本|contactWechat|businessAddress/)
|
|
})
|
|
|
|
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, /<QueryToolbar v-model="memberFilters"/)
|
|
assert.match(membersSource, /<DataTable v-model:page="memberPage" v-model:page-size="memberPageSize"/)
|
|
assert.match(membersSource, /<div class="desktop-member-table">\s*<DataTable/)
|
|
assert.doesNotMatch(membersSource, /<DataTable[^>]*class="desktop-member-table"/)
|
|
assert.match(membersSource, /\.desktop-member-table \{ display: none; \}/)
|
|
assert.doesNotMatch(membersSource, /\.desktop-member-table :deep\(\.el-table\)/)
|
|
assert.match(membersSource, /<section v-loading="loading" class="members-page" :aria-busy="loading">/)
|
|
assert.doesNotMatch(membersSource, /<DataTable[^>]*:loading="loading"/)
|
|
assert.match(membersSource, /fixed: 'left'/)
|
|
assert.match(membersSource, /class="table-actions"/)
|
|
assert.doesNotMatch(membersSource, /template #actions><el-button v-if="auth\.hasPermission\('company:member:write'\)/)
|
|
assert.doesNotMatch(membersSource, /member-metrics/)
|
|
})
|
|
|
|
test('protects built-in roles and exposes role permission assignment', () => {
|
|
assert.match(rolesSource, /auth\.hasPermission\('company:role:write'\).*\.builtin/)
|
|
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, /store\.loadRoles\(\)/)
|
|
assert.match(rolesSource, /auth\.hasPermission\('company:member:list'\).*store\.loadMembers\(\)/)
|
|
assert.match(rolesSource, /await Promise\.all\(requests\)/)
|
|
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, /<QueryToolbar v-model="roleFilters"/)
|
|
assert.match(rolesSource, /<DataTable v-model:page="rolePage" v-model:page-size="rolePageSize"/)
|
|
assert.match(rolesSource, /<section v-loading="loading" class="roles-page" :aria-busy="loading">/)
|
|
assert.doesNotMatch(rolesSource, /<DataTable[^>]*:loading="loading"/)
|
|
assert.match(rolesSource, /fixed: 'left'/)
|
|
assert.match(rolesSource, /class="table-actions"/)
|
|
assert.doesNotMatch(rolesSource, /template #actions><el-button v-if="auth\.hasPermission\('company:role:write'\)/)
|
|
assert.doesNotMatch(rolesSource, /role-guidance/)
|
|
})
|
|
|
|
test('shows company audit status and applies button permissions', () => {
|
|
assert.match(profileSource, /companyApproved/)
|
|
assert.match(profileSource, /企业资料正在等待平台审核/)
|
|
assert.match(profileSource, /auth\.hasPermission\('company:profile:edit'\)/)
|
|
assert.match(membersSource, /auth\.hasPermission\('company:member:write'\)/)
|
|
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, /<el-checkbox v-for="item in group\.items"[^>]*class="permission-item"/)
|
|
assert.doesNotMatch(permissionsSource, /<label v-for="item in group\.items"/)
|
|
assert.match(permissionsSource, /permission-group--single/)
|
|
assert.match(permissionsSource, /\.permission-item:last-child:nth-child\(odd\) \{ grid-column: 1 \/ -1; border-right: 0; \}/)
|
|
assert.match(permissionsSource, /\.permission-item :deep\(\.el-checkbox__label\) \{[^}]*white-space: normal/)
|
|
assert.match(permissionsSource, /\.permission-copy \{[^}]*flex-direction: column[^}]*gap: 5px/)
|
|
assert.doesNotMatch(permissionsSource, /permission-heading/)
|
|
assert.doesNotMatch(permissionsSource, /section-label/)
|
|
assert.doesNotMatch(permissionsSource, /当前只读展示权限资源/)
|
|
})
|
|
|
|
test('keeps tenant administration state scoped to the client store', () => {
|
|
assert.match(storeSource, /defineStore\('tenant-administration'/)
|
|
assert.match(storeSource, /function createMember/)
|
|
assert.match(storeSource, /function createRole/)
|
|
assert.match(storeSource, /function setRolePermissions/)
|
|
assert.doesNotMatch(storeSource, /tenantId\s*:/)
|
|
})
|