/* @vitest-environment happy-dom */
|
|
import type { DmsPermissionResource } from '#/api/x/dms/permission';
|
|
import { flushPromises, mount } from '@vue/test-utils';
|
import { defineComponent, h } from 'vue';
|
|
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
import GrantDrawer from '../GrantDrawer.vue';
|
|
const apiMocks = vi.hoisted(() => ({ createPermissionGrant: vi.fn() }));
|
const formMocks = vi.hoisted(() => ({ clearValidate: vi.fn(), config: undefined as any, resetFields: vi.fn(), setFieldsValue: vi.fn(), validate: vi.fn() }));
|
const drawerMocks = vi.hoisted(() => ({ changeOkLoading: vi.fn(), closeDrawer: vi.fn(), init: undefined as any }));
|
const messageMocks = vi.hoisted(() => ({ success: vi.fn(), warning: vi.fn() }));
|
const signMock = vi.hoisted(() => vi.fn());
|
|
vi.mock('#/api/x/dms/permission', () => ({ createPermissionGrant: apiMocks.createPermissionGrant }));
|
vi.mock('#/utils/jnpf', () => ({ onlineUtils: { sign: signMock } }));
|
vi.mock('@jnpf/hooks', () => ({ useMessage: () => ({ createMessage: messageMocks }) }));
|
vi.mock('@jnpf/ui/form', async () => {
|
const { defineComponent, h } = await import('vue');
|
return {
|
BasicForm: defineComponent({
|
name: 'BasicForm',
|
setup: (_, { slots }) => () => h('div', slots.principalIds?.({ field: 'principalIds', model: { principalIds: [], principalType: 'USER' } })),
|
}),
|
useForm: (config: any) => {
|
formMocks.config = config;
|
return [vi.fn(), { clearValidate: formMocks.clearValidate, resetFields: formMocks.resetFields, setFieldsValue: formMocks.setFieldsValue, validate: formMocks.validate }];
|
},
|
};
|
});
|
vi.mock('@jnpf/ui/drawer', async () => {
|
const { defineComponent, h } = await import('vue');
|
return {
|
BasicDrawer: defineComponent({
|
name: 'BasicDrawer',
|
emits: ['ok'],
|
setup: (_, { slots }) => () => h('section', slots.default?.()),
|
}),
|
useDrawerInner: (init: any) => {
|
drawerMocks.init = init;
|
return [vi.fn(), { changeOkLoading: drawerMocks.changeOkLoading, closeDrawer: drawerMocks.closeDrawer }];
|
},
|
};
|
});
|
|
const folder: DmsPermissionResource = {
|
createdAt: '2026-09-01T00:00:00Z',
|
id: 'folder-1',
|
name: '质量文件',
|
resourceType: 'FOLDER',
|
sourceId: 'folder-1',
|
sourceType: 'MANUAL',
|
};
|
|
const SelectorStub = defineComponent({
|
name: 'JnpfUserSelect',
|
props: { multiple: Boolean, value: Array },
|
setup: () => () => h('div'),
|
});
|
|
function mountDrawer() {
|
return mount(GrantDrawer, {
|
global: {
|
components: {
|
JnpfOrganizeSelect: SelectorStub,
|
JnpfPosSelect: SelectorStub,
|
JnpfRoleSelect: SelectorStub,
|
JnpfUserSelect: SelectorStub,
|
},
|
stubs: { 'a-alert': true },
|
},
|
});
|
}
|
|
async function submit(wrapper: ReturnType<typeof mountDrawer>) {
|
wrapper.findComponent({ name: 'BasicDrawer' }).vm.$emit('ok');
|
await flushPromises();
|
}
|
|
describe('permission grant drawer', () => {
|
beforeEach(() => {
|
vi.clearAllMocks();
|
formMocks.resetFields.mockResolvedValue(undefined);
|
formMocks.setFieldsValue.mockResolvedValue(undefined);
|
formMocks.clearValidate.mockResolvedValue(undefined);
|
formMocks.validate.mockResolvedValue({
|
actions: ['READ', 'DOWNLOAD'],
|
grantReason: ' 项目协作 ',
|
includeChildOrgs: false,
|
principalIds: ['user-1', 'user-2'],
|
principalType: 'USER',
|
resourceScope: 'SELF',
|
});
|
apiMocks.createPermissionGrant.mockResolvedValue({ data: [{}, {}, {}, {}] });
|
});
|
|
it('creates one signed batch for every selected user and action', async () => {
|
const wrapper = mountDrawer();
|
await drawerMocks.init({ resource: folder });
|
expect(formMocks.setFieldsValue).toHaveBeenCalledWith(expect.objectContaining({ principalIds: [] }));
|
expect(formMocks.config.schemas.find((schema: any) => schema.field === 'principalIds').rules).toMatchObject([{ type: 'array', min: 1 }]);
|
expect(wrapper.getComponent({ name: 'JnpfUserSelect' }).props('multiple')).toBe(true);
|
|
await submit(wrapper);
|
expect(signMock).toHaveBeenCalledOnce();
|
const signed = signMock.mock.calls[0]![0];
|
const batch = signed.metaData.biz_data[0];
|
expect(batch.sourceId).toMatch(/^manual-/);
|
expect(batch.items).toHaveLength(4);
|
expect(batch.items.map(({ actionCode, principalId }: any) => [principalId, actionCode])).toEqual([
|
['user-1', 'READ'],
|
['user-1', 'DOWNLOAD'],
|
['user-2', 'READ'],
|
['user-2', 'DOWNLOAD'],
|
]);
|
expect(batch.items.every((item: any) => item.grantReason === '项目协作' && item.resourceId === folder.id)).toBe(true);
|
|
await signed.onSubmit({ biz_sign: 'signed-batch' });
|
expect(apiMocks.createPermissionGrant).toHaveBeenCalledOnce();
|
expect(apiMocks.createPermissionGrant).toHaveBeenCalledWith(batch, 'signed-batch');
|
expect(drawerMocks.closeDrawer).toHaveBeenCalledOnce();
|
expect(wrapper.emitted('reload')).toHaveLength(1);
|
wrapper.unmount();
|
});
|
|
it('clears prior selections on principal type change and removes duplicate normalized IDs', async () => {
|
const wrapper = mountDrawer();
|
await drawerMocks.init({ resource: folder });
|
await formMocks.config.schemas[0].componentProps.onChange();
|
expect(formMocks.setFieldsValue).toHaveBeenLastCalledWith({ includeChildOrgs: false, maxUseCount: undefined, principalIds: [] });
|
expect(formMocks.clearValidate).toHaveBeenCalledWith(['principalIds']);
|
|
formMocks.validate.mockResolvedValueOnce({
|
actions: ['READ'],
|
grantReason: '',
|
includeChildOrgs: true,
|
principalIds: ['org-1--org', 'org-1--subOrg', 'org-2--org'],
|
principalType: 'ORG',
|
resourceScope: 'SELF',
|
});
|
await submit(wrapper);
|
expect(signMock.mock.calls[0]![0].metaData.biz_data[0].items.map((item: any) => item.principalId)).toEqual(['org-1', 'org-2']);
|
wrapper.unmount();
|
});
|
|
it('stops oversized batches before requesting a signature', async () => {
|
formMocks.validate.mockResolvedValueOnce({
|
actions: ['READ', 'DOWNLOAD'],
|
grantReason: '',
|
includeChildOrgs: false,
|
principalIds: Array.from({ length: 251 }, (_, index) => `user-${index}`),
|
principalType: 'USER',
|
resourceScope: 'SELF',
|
});
|
const wrapper = mountDrawer();
|
await drawerMocks.init({ resource: folder });
|
await submit(wrapper);
|
|
expect(messageMocks.warning).toHaveBeenCalledWith('一次最多创建 500 条授权,请减少主体或操作');
|
expect(signMock).not.toHaveBeenCalled();
|
expect(apiMocks.createPermissionGrant).not.toHaveBeenCalled();
|
wrapper.unmount();
|
});
|
});
|