import { flushPromises, mount } from '@vue/test-utils';
|
import { defineComponent, h } from 'vue';
|
|
import { describe, expect, it, vi } from 'vitest';
|
|
import FilePermissionSettings from './FilePermissionSettings.vue';
|
|
vi.mock('@jnpf/utils', async () => import('../../../../../../../../packages/jnpf/utils/src/helpers/uuid'));
|
|
const Modal = defineComponent({
|
props: ['open', 'okButtonProps'],
|
emits: ['ok', 'update:open'],
|
setup(props, { slots, emit }) {
|
return () =>
|
props.open
|
? h('section', [
|
slots.default?.(),
|
h('button', { class: 'confirm', disabled: props.okButtonProps?.disabled, onClick: () => emit('ok') }, '确定'),
|
h('button', { class: 'cancel', onClick: () => emit('update:open', false) }, '取消'),
|
])
|
: null;
|
},
|
});
|
const Button = defineComponent({
|
props: ['disabled'],
|
emits: ['click'],
|
setup(props, { slots, emit }) {
|
return () => h('button', { disabled: props.disabled, onClick: () => emit('click') }, slots.default?.());
|
},
|
});
|
const Select = defineComponent({
|
props: ['value', 'options'],
|
emits: ['change', 'update:value'],
|
setup(props, { emit }) {
|
return () =>
|
h(
|
'select',
|
{
|
value: props.value,
|
onChange: (event: Event) => {
|
const value = (event.target as HTMLSelectElement).value;
|
emit('update:value', value);
|
emit('change', value);
|
},
|
},
|
[h('option', { value: '' }, ''), ...props.options.map((o: any) => h('option', { value: o.value, disabled: o.disabled }, o.label))],
|
);
|
},
|
});
|
const Slot = defineComponent({
|
setup(_, { slots }) {
|
return () => h('div', [slots.extra?.(), slots.default?.()]);
|
},
|
});
|
function setup(modelValue?: any, realModal = false) {
|
const data: any = {
|
global: { allFormMap: { form_f: [{ id: 'rows-files', fullName: '记录文件', __config__: { jnpfKey: 'uploadFile' } }] } },
|
start: { type: 'start', formId: 'f' },
|
node: { type: 'approver' },
|
};
|
const wrapper = mount(FilePermissionSettings, {
|
attachTo: document.body,
|
props: { modelValue, flowType: 0, bpmn: { get: (name: string) => (name === 'jnpfData' ? { data } : { getAll: () => [{ id: 'start' }, { id: 'node' }] }) } },
|
global: {
|
stubs: realModal ? {} : { AModal: Modal, AEmpty: Slot },
|
components: {
|
AButton: Button,
|
ASelect: Select,
|
AForm: Slot,
|
AFormItem: Slot,
|
ACard: Slot,
|
AAlert: defineComponent({
|
props: ['message'],
|
setup(props) {
|
return () => h('p', props.message);
|
},
|
}),
|
},
|
},
|
});
|
return { wrapper, data };
|
}
|
function policy(): any {
|
return { version: 1, rules: [{ id: 'r', formId: 'f', field: 'rows-files', subject: 'CREATOR', trigger: 'COMPLETED', actions: { DOWNLOAD: 'DENY' } }] };
|
}
|
async function show(wrapper: ReturnType<typeof setup>['wrapper']) {
|
await wrapper.find('button').trigger('click');
|
}
|
|
describe('workflow file permission editor', () => {
|
it('opens the actual locally registered modal without global modal/empty registration', async () => {
|
const { wrapper } = setup(undefined, true);
|
await show(wrapper);
|
await flushPromises();
|
expect(document.body.querySelector('.ant-modal-title')?.textContent).toBe('流程文件权限');
|
expect(document.body.querySelector('.ant-empty-description')?.textContent).toBe('尚未配置文件权限规则,沿用原权限');
|
wrapper.unmount();
|
});
|
it('creates a rule without depending on browser secure-context UUID support', async () => {
|
const { wrapper } = setup();
|
await show(wrapper);
|
await wrapper
|
.findAll('button')
|
.find((b) => b.text() === '添加文件权限规则')!
|
.trigger('click');
|
await wrapper.findAll('select')[0]!.setValue(JSON.stringify(['f', 'rows-files']));
|
await wrapper.findAll('select')[2]!.setValue('COMPLETED');
|
await wrapper.findAll('select')[4]!.setValue('DENY');
|
await wrapper.get('.confirm').trigger('click');
|
const rule = (wrapper.emitted('update:modelValue')![0]![0] as any).rules[0];
|
expect(rule.id).toEqual(expect.any(String));
|
expect(rule.id.length).toBeGreaterThan(0);
|
expect(rule).toMatchObject({ formId: 'f', field: 'rows-files', actions: { DOWNLOAD: 'DENY' } });
|
wrapper.unmount();
|
});
|
it('does not activate legacy flows just by opening and confirming', async () => {
|
const { wrapper } = setup();
|
await show(wrapper);
|
await wrapper.get('.confirm').trigger('click');
|
expect(wrapper.emitted('update:modelValue')).toBeUndefined();
|
wrapper.unmount();
|
});
|
it('cancel keeps original restrictions and reopening restores them', async () => {
|
const original = policy();
|
const { wrapper } = setup(original);
|
await show(wrapper);
|
await wrapper.findAll('select')[4]!.setValue('ALLOW');
|
await wrapper.get('.cancel').trigger('click');
|
expect(original.rules[0].actions.DOWNLOAD).toBe('DENY');
|
expect(wrapper.emitted('update:modelValue')).toBeUndefined();
|
await show(wrapper);
|
expect(wrapper.findAll('select')[4]!.element.value).toBe('DENY');
|
wrapper.unmount();
|
});
|
it('confirms a changed rule using native form/child IDs without mutating original', async () => {
|
const original = policy();
|
const { wrapper } = setup(original);
|
await show(wrapper);
|
await wrapper.findAll('select')[4]!.setValue('ALLOW');
|
await wrapper.get('.confirm').trigger('click');
|
const saved = wrapper.emitted('update:modelValue')![0]![0] as any;
|
expect(saved.rules[0]).toMatchObject({ formId: 'f', field: 'rows-files', actions: { DOWNLOAD: 'ALLOW' } });
|
expect(original.rules[0].actions.DOWNLOAD).toBe('DENY');
|
wrapper.unmount();
|
});
|
it('revalidates live form fields on confirm after the dialog was opened', async () => {
|
const { wrapper, data } = setup(policy());
|
await show(wrapper);
|
data.global.allFormMap.form_f = [];
|
await wrapper.get('.confirm').trigger('click');
|
expect(wrapper.emitted('update:modelValue')).toBeUndefined();
|
expect(wrapper.text()).toContain('附件字段不存在');
|
wrapper.unmount();
|
});
|
it('keeps unknown configuration intact instead of clearing it', async () => {
|
const original = { version: 2, rules: [{ id: 'unknown', actions: { DOWNLOAD: 'DENY' } }] };
|
const { wrapper } = setup(original);
|
await show(wrapper);
|
expect(wrapper.get('.confirm').attributes('disabled')).toBeDefined();
|
await wrapper.get('.cancel').trigger('click');
|
expect(wrapper.emitted('update:modelValue')).toBeUndefined();
|
expect(original.version).toBe(2);
|
wrapper.unmount();
|
});
|
it('requires explicit return behavior for persistent rules', async () => {
|
const { wrapper } = setup(policy());
|
await show(wrapper);
|
await wrapper.findAll('select')[2]!.setValue('ENTERED');
|
await wrapper.findAll('select')[3]!.setValue('node');
|
await wrapper.get('.confirm').trigger('click');
|
expect(wrapper.text()).toContain('请选择退回后保留或解除规则');
|
expect(wrapper.emitted('update:modelValue')).toBeUndefined();
|
await wrapper.findAll('select')[4]!.setValue('KEEP');
|
await wrapper.get('.confirm').trigger('click');
|
expect((wrapper.emitted('update:modelValue')![0]![0] as any).rules[0]).toMatchObject({ trigger: 'ENTERED', triggerNodeId: 'node', onReturn: 'KEEP' });
|
wrapper.unmount();
|
});
|
it('allows persistent rules to release after returning before the trigger node', async () => {
|
const original = policy();
|
original.rules[0].trigger = 'ENTERED';
|
original.rules[0].triggerNodeId = 'node';
|
original.rules[0].onReturn = 'RELEASE';
|
const { wrapper } = setup(original);
|
await show(wrapper);
|
const returnSelect = wrapper.findAll('select')[4]!;
|
expect(returnSelect.element.value).toBe('RELEASE');
|
expect(returnSelect.find('option[value="RELEASE"]').attributes('disabled')).toBeUndefined();
|
await wrapper.get('.confirm').trigger('click');
|
expect((wrapper.emitted('update:modelValue')![0]![0] as any).rules[0]).toMatchObject({ onReturn: 'RELEASE' });
|
wrapper.unmount();
|
});
|
it('offers and persists the non-current-handler subject', async () => {
|
const { wrapper } = setup(policy());
|
await show(wrapper);
|
const subject = wrapper.findAll('select')[1]!;
|
expect(subject.find('option[value="NON_CURRENT_HANDLERS"]').text()).toBe('非当前办理人');
|
await subject.setValue('NON_CURRENT_HANDLERS');
|
await wrapper.get('.confirm').trigger('click');
|
expect((wrapper.emitted('update:modelValue')![0]![0] as any).rules[0].subject).toBe('NON_CURRENT_HANDLERS');
|
wrapper.unmount();
|
});
|
it('explicitly clearing existing rules saves an empty versioned policy', async () => {
|
const { wrapper } = setup(policy());
|
await show(wrapper);
|
await wrapper
|
.findAll('button')
|
.find((b) => b.text() === '删除规则')!
|
.trigger('click');
|
await wrapper.get('.confirm').trigger('click');
|
expect(wrapper.emitted('update:modelValue')![0]).toEqual([{ version: 1, rules: [] }]);
|
wrapper.unmount();
|
});
|
});
|