410 lines
14 KiB
TypeScript
410 lines
14 KiB
TypeScript
import {
|
|
collectEmployeeOrganizationPaths,
|
|
mergeDepartmentAndInheritPacks,
|
|
collectOccupiedOrganizationPaths,
|
|
countPeopleOnOrganizationPaths,
|
|
countUniquePeopleOnOrganizationPaths,
|
|
describeDepartmentGroupStatus,
|
|
findNearestGroupMemberships,
|
|
findOrganizationPermissionStorageKey,
|
|
isUnderOrganizationPath,
|
|
normalizeOrganizationPath,
|
|
organizationPathChain,
|
|
parseOrganizationDepartmentIds,
|
|
personBelongsToOrganizationPath,
|
|
pruneOrganizationTree,
|
|
resolveEmployeeOrganizationPaths,
|
|
resolveOrganizationPermissionKey,
|
|
unionPermissionPacks,
|
|
} from '@/lib/organization-material-permissions'
|
|
|
|
describe('organization material permission keys', () => {
|
|
test('path chain goes from company to leaf', () => {
|
|
expect(
|
|
organizationPathChain('江苏简尚品牌管理有限公司/运营中心A/事业一部/品类十九部'),
|
|
).toEqual([
|
|
'江苏简尚品牌管理有限公司',
|
|
'江苏简尚品牌管理有限公司/运营中心A',
|
|
'江苏简尚品牌管理有限公司/运营中心A/事业一部',
|
|
'江苏简尚品牌管理有限公司/运营中心A/事业一部/品类十九部',
|
|
])
|
|
})
|
|
|
|
test('own path config wins over legacy short names', () => {
|
|
expect(
|
|
findOrganizationPermissionStorageKey('江苏简尚品牌管理有限公司/运营中心A', [
|
|
'运营中心',
|
|
'江苏简尚品牌管理有限公司/运营中心A',
|
|
]),
|
|
).toEqual({
|
|
key: '江苏简尚品牌管理有限公司/运营中心A',
|
|
source: 'own',
|
|
})
|
|
})
|
|
|
|
test('运营中心A can reuse the old 运营中心 pack', () => {
|
|
expect(
|
|
findOrganizationPermissionStorageKey('江苏简尚品牌管理有限公司/运营中心A', ['运营中心']),
|
|
).toEqual({
|
|
key: '运营中心',
|
|
source: 'legacy',
|
|
})
|
|
})
|
|
|
|
test('策划中心 and 摄影中心 use the old 视觉中心 pack even if they have their own rows', () => {
|
|
expect(
|
|
findOrganizationPermissionStorageKey('江苏简尚品牌管理有限公司/策划中心', [
|
|
'策划中心',
|
|
'摄影中心',
|
|
'视觉中心',
|
|
]),
|
|
).toEqual({
|
|
key: '视觉中心',
|
|
source: 'legacy',
|
|
})
|
|
expect(
|
|
findOrganizationPermissionStorageKey('江苏简尚品牌管理有限公司/摄影中心', [
|
|
'策划中心',
|
|
'摄影中心',
|
|
'视觉中心',
|
|
]),
|
|
).toEqual({
|
|
key: '视觉中心',
|
|
source: 'legacy',
|
|
})
|
|
})
|
|
|
|
test('卓禾运营 and 朴美运营 reuse the old 运营中心 pack', () => {
|
|
expect(
|
|
findOrganizationPermissionStorageKey('江苏简尚品牌管理有限公司/运营中心A/卓禾运营', ['运营中心']),
|
|
).toEqual({
|
|
key: '运营中心',
|
|
source: 'legacy',
|
|
})
|
|
expect(
|
|
findOrganizationPermissionStorageKey('江苏简尚品牌管理有限公司/运营中心B/朴美运营', ['运营中心']),
|
|
).toEqual({
|
|
key: '运营中心',
|
|
source: 'legacy',
|
|
})
|
|
})
|
|
|
|
test('品牌中心 subtree uses the old 视觉中心 pack', () => {
|
|
expect(
|
|
findOrganizationPermissionStorageKey(
|
|
'江苏简尚品牌管理有限公司/品牌中心/品牌创意A部',
|
|
['视觉中心'],
|
|
),
|
|
).toEqual({
|
|
key: '视觉中心',
|
|
source: 'legacy',
|
|
})
|
|
expect(
|
|
resolveOrganizationPermissionKey(
|
|
'江苏简尚品牌管理有限公司/品牌中心/商品企划部/桌类',
|
|
['视觉中心'],
|
|
),
|
|
).toEqual({
|
|
key: '视觉中心',
|
|
source: 'inherit',
|
|
inheritFrom: '江苏简尚品牌管理有限公司/品牌中心/商品企划部',
|
|
})
|
|
})
|
|
|
|
test('employee org paths follow roster primary, extra posts and department ids', () => {
|
|
const company = '江苏简尚品牌管理有限公司'
|
|
const brand = `${company}/品牌中心`
|
|
const ops = `${company}/运营中心G`
|
|
const lookup = new Map([
|
|
['2', brand],
|
|
['9', ops],
|
|
])
|
|
|
|
expect(
|
|
resolveEmployeeOrganizationPaths(
|
|
{
|
|
departmentPath: brand,
|
|
departmentMemberships: [{ sourceDeptId: '9', departmentPath: ops }],
|
|
sourcePayload: { syncedFields: { departmentIds: '[2,9]' } },
|
|
},
|
|
lookup,
|
|
),
|
|
).toEqual([brand, ops])
|
|
expect(resolveEmployeeOrganizationPaths(null, lookup)).toEqual([])
|
|
})
|
|
|
|
test('permission tree drops revoked empty branches but keeps occupied ancestors', () => {
|
|
const occupied = collectOccupiedOrganizationPaths([
|
|
'江苏简尚品牌管理有限公司/运营中心A/事业一部/品类十九部',
|
|
])
|
|
const pruned = pruneOrganizationTree(
|
|
[
|
|
{
|
|
path: '江苏简尚品牌管理有限公司',
|
|
children: [
|
|
{
|
|
path: '江苏简尚品牌管理有限公司/广州项目组',
|
|
children: [{ path: '江苏简尚品牌管理有限公司/广州项目组/A组', children: [] }],
|
|
},
|
|
{
|
|
path: '江苏简尚品牌管理有限公司/运营中心A',
|
|
children: [
|
|
{
|
|
path: '江苏简尚品牌管理有限公司/运营中心A/事业一部',
|
|
children: [
|
|
{ path: '江苏简尚品牌管理有限公司/运营中心A/事业一部/品类十九部', children: [] },
|
|
],
|
|
},
|
|
],
|
|
},
|
|
],
|
|
},
|
|
],
|
|
occupied,
|
|
)
|
|
|
|
expect(pruned).toHaveLength(1)
|
|
expect(pruned[0].children.map((node) => node.path)).toEqual([
|
|
'江苏简尚品牌管理有限公司/运营中心A',
|
|
])
|
|
expect(pruned[0].children[0].children[0].children.map((node) => node.path)).toEqual([
|
|
'江苏简尚品牌管理有限公司/运营中心A/事业一部/品类十九部',
|
|
])
|
|
})
|
|
|
|
test('leaf without own rows inherits the parent pack', () => {
|
|
expect(
|
|
resolveOrganizationPermissionKey(
|
|
'江苏简尚品牌管理有限公司/运营中心A/事业一部/品类十九部',
|
|
['运营中心'],
|
|
),
|
|
).toEqual({
|
|
key: '运营中心',
|
|
source: 'inherit',
|
|
inheritFrom: '江苏简尚品牌管理有限公司/运营中心A',
|
|
})
|
|
})
|
|
})
|
|
|
|
describe('organization people counts', () => {
|
|
const company = '江苏简尚品牌管理有限公司'
|
|
const planning = `${company}/品牌中心/商品企划部`
|
|
const shelf = `${planning}/架类`
|
|
const support = `${company}/业务支持`
|
|
const visual = `${support}/视觉支持`
|
|
|
|
test('normalize keeps the original path, it does not join the prefix chain', () => {
|
|
expect(normalizeOrganizationPath(` ${company}/ 总经理室 / `)).toBe(`${company}/总经理室`)
|
|
})
|
|
|
|
test('a person belongs to their own dept and every ancestor, not a sibling', () => {
|
|
expect(isUnderOrganizationPath(shelf, planning)).toBe(true)
|
|
expect(isUnderOrganizationPath(shelf, company)).toBe(true)
|
|
expect(isUnderOrganizationPath(planning, planning)).toBe(true)
|
|
expect(isUnderOrganizationPath(planning, shelf)).toBe(false)
|
|
expect(isUnderOrganizationPath(visual, planning)).toBe(false)
|
|
expect(isUnderOrganizationPath(null, planning)).toBe(false)
|
|
})
|
|
|
|
test('tree numbers count each person once per ancestor, never roll children up again', () => {
|
|
const counts = countPeopleOnOrganizationPaths([shelf, visual, `${company}/人力行政中心`])
|
|
|
|
expect(counts.get(company)).toBe(3)
|
|
expect(counts.get(`${company}/品牌中心`)).toBe(1)
|
|
expect(counts.get(planning)).toBe(1)
|
|
expect(counts.get(shelf)).toBe(1)
|
|
expect(counts.get(support)).toBe(1)
|
|
expect(counts.get(visual)).toBe(1)
|
|
expect(counts.get(`${company}/人力行政中心`)).toBe(1)
|
|
})
|
|
|
|
test('clicking 商品企划部 includes the person sitting in 架类', () => {
|
|
const people = [
|
|
{ name: '涂涂', departmentPath: shelf },
|
|
{ name: '香樟', departmentPath: `${company}/品牌中心/品牌设计部` },
|
|
]
|
|
expect(
|
|
people.filter((person) => isUnderOrganizationPath(person.departmentPath, planning)).map((p) => p.name),
|
|
).toEqual(['涂涂'])
|
|
})
|
|
|
|
test('一人兼多个部门时,每个部门名单都有他,公司人数仍只计一次', () => {
|
|
const office = `${company}/总经理室`
|
|
const brand = `${company}/品牌中心`
|
|
const ops = `${company}/运营中心G`
|
|
const jiangling = collectEmployeeOrganizationPaths([office, brand, ops, office])
|
|
|
|
expect(jiangling).toEqual([office, brand, ops])
|
|
expect(personBelongsToOrganizationPath(jiangling, brand)).toBe(true)
|
|
expect(personBelongsToOrganizationPath(jiangling, office)).toBe(true)
|
|
expect(personBelongsToOrganizationPath(jiangling, `${company}/运营中心A`)).toBe(false)
|
|
|
|
const counts = countUniquePeopleOnOrganizationPaths([
|
|
jiangling,
|
|
[`${company}/人力行政中心`],
|
|
])
|
|
expect(counts.get(company)).toBe(2)
|
|
expect(counts.get(brand)).toBe(1)
|
|
expect(counts.get(office)).toBe(1)
|
|
expect(counts.get(`${company}/人力行政中心`)).toBe(1)
|
|
})
|
|
|
|
test('只保留最深任职,上级部门不另算一个职位', () => {
|
|
expect(
|
|
collectEmployeeOrganizationPaths([
|
|
`${company}/品牌中心`,
|
|
`${company}/品牌中心/品牌创意A部`,
|
|
`${company}/品牌中心`,
|
|
]),
|
|
).toEqual([`${company}/品牌中心/品牌创意A部`])
|
|
})
|
|
|
|
test('从自己往上找最近的权限组,一个部门只进一组', () => {
|
|
const leaf = `${company}/运营中心A/事业一部/品类十九部`
|
|
const ops = `${company}/运营中心A`
|
|
const memberships = [
|
|
{ departmentPath: ops, slug: 'operations', name: '运营' },
|
|
{ departmentPath: company, slug: 'all', name: '全员' },
|
|
]
|
|
|
|
expect(findNearestGroupMemberships(leaf, memberships)).toEqual({
|
|
attachedPath: ops,
|
|
items: [{ departmentPath: ops, slug: 'operations', name: '运营' }],
|
|
})
|
|
expect(findNearestGroupMemberships(`${company}/财务中心`, memberships)).toEqual({
|
|
attachedPath: company,
|
|
items: [{ departmentPath: company, slug: 'all', name: '全员' }],
|
|
})
|
|
expect(findNearestGroupMemberships(`${company}/人力行政中心`, [])).toBeNull()
|
|
})
|
|
|
|
test('分配部门时标明已在本组、已在别组、随上级或未进组', () => {
|
|
const ops = `${company}/运营中心A`
|
|
const leaf = `${company}/运营中心A/事业一部/品类十九部`
|
|
const memberships = [{ departmentPath: ops, slug: 'operations', name: '运营' }]
|
|
|
|
expect(describeDepartmentGroupStatus(ops, 'operations', memberships)).toMatchObject({
|
|
kind: 'own',
|
|
label: '已在本组',
|
|
})
|
|
expect(describeDepartmentGroupStatus(ops, 'visual', memberships)).toMatchObject({
|
|
kind: 'other',
|
|
label: '已在「运营」',
|
|
})
|
|
expect(describeDepartmentGroupStatus(leaf, 'visual', memberships)).toMatchObject({
|
|
kind: 'inherit',
|
|
label: '随 运营中心A · 运营',
|
|
})
|
|
expect(describeDepartmentGroupStatus(`${company}/财务中心`, 'visual', memberships)).toMatchObject({
|
|
kind: 'none',
|
|
label: '未进组',
|
|
})
|
|
})
|
|
|
|
test('多组合并时允许的取并集,不允许的不会把别组已经给的拿掉', () => {
|
|
const merged = unionPermissionPacks(
|
|
{
|
|
'3D场景': { canView: true, canDownload: true, canDownloadSource: false, canShare: true, canEditAssets: false, canAccessPrivate: false },
|
|
},
|
|
{
|
|
'3D场景': { canView: false, canDownload: false, canDownloadSource: false, canShare: true, canEditAssets: false, canAccessPrivate: false },
|
|
详情模板: { canView: true, canDownload: true, canDownloadSource: true, canShare: true, canEditAssets: false, canAccessPrivate: false },
|
|
},
|
|
{
|
|
详情模板: { canView: true, canDownload: false, canDownloadSource: false, canShare: true, canEditAssets: false, canAccessPrivate: false },
|
|
},
|
|
)
|
|
|
|
expect(merged['3D场景']).toEqual({
|
|
canView: true,
|
|
canDownload: true,
|
|
canDownloadSource: false,
|
|
canShare: true,
|
|
canEditAssets: false,
|
|
canAccessPrivate: false,
|
|
})
|
|
expect(merged['详情模板']).toEqual({
|
|
canView: true,
|
|
canDownload: true,
|
|
canDownloadSource: true,
|
|
canShare: true,
|
|
canEditAssets: false,
|
|
canAccessPrivate: false,
|
|
})
|
|
})
|
|
|
|
test('用户继承叠在部门权限上,能给的加上,不能把部门已有的拿掉', () => {
|
|
const merged = mergeDepartmentAndInheritPacks(
|
|
{
|
|
'3D场景': { canView: true, canDownload: true, canDownloadSource: false, canShare: true, canEditAssets: false, canAccessPrivate: false },
|
|
'任务-详情': { canView: true, canDownload: false, canDownloadSource: false, canShare: true, canEditAssets: false, canAccessPrivate: false },
|
|
},
|
|
{
|
|
'3D场景': { canView: false, canDownload: false, canDownloadSource: false, canShare: true, canEditAssets: false, canAccessPrivate: false },
|
|
详情模板: { canView: true, canDownload: true, canDownloadSource: true, canShare: true, canEditAssets: false, canAccessPrivate: false },
|
|
},
|
|
)
|
|
|
|
expect(merged['3D场景']).toEqual({
|
|
canView: true,
|
|
canDownload: true,
|
|
canDownloadSource: false,
|
|
canShare: true,
|
|
canEditAssets: false,
|
|
canAccessPrivate: false,
|
|
})
|
|
expect(merged['任务-详情']?.canDownload).toBe(false)
|
|
expect(merged['详情模板']).toEqual({
|
|
canView: true,
|
|
canDownload: true,
|
|
canDownloadSource: true,
|
|
canShare: true,
|
|
canEditAssets: false,
|
|
canAccessPrivate: false,
|
|
})
|
|
})
|
|
|
|
test('任务详情私密权限同样按并集合并,别的组不能把已给的权限抹掉', () => {
|
|
const merged = unionPermissionPacks(
|
|
{
|
|
'任务-详情': {
|
|
canView: true,
|
|
canDownload: true,
|
|
canDownloadSource: false,
|
|
canShare: true,
|
|
canEditAssets: false,
|
|
canAccessPrivate: true,
|
|
},
|
|
},
|
|
{
|
|
'任务-详情': {
|
|
canView: false,
|
|
canDownload: false,
|
|
canDownloadSource: false,
|
|
canShare: true,
|
|
canEditAssets: false,
|
|
canAccessPrivate: false,
|
|
},
|
|
},
|
|
)
|
|
|
|
expect(merged['任务-详情']).toEqual({
|
|
canView: true,
|
|
canDownload: true,
|
|
canDownloadSource: false,
|
|
canShare: true,
|
|
canEditAssets: false,
|
|
canAccessPrivate: true,
|
|
})
|
|
})
|
|
|
|
test('花名册部门 ID 列表会拆出全部任职部门', () => {
|
|
expect(parseOrganizationDepartmentIds('[22787301,909676348]')).toEqual(['22787301', '909676348'])
|
|
expect(parseOrganizationDepartmentIds('22802296|911666246|32221130')).toEqual([
|
|
'22802296',
|
|
'911666246',
|
|
'32221130',
|
|
])
|
|
})
|
|
})
|