import type { TemplateBinding, TemplateFieldOption } from '../../src/domain/types';
|
import type { OfficeContentControl } from '../../src/onlyoffice/types';
|
|
import { mount } from '@vue/test-utils';
|
import { defineComponent, h, nextTick } from 'vue';
|
|
import Antd, { Modal } from 'ant-design-vue';
|
import { describe, expect, it, vi } from 'vitest';
|
|
import DocumentFieldList from '../../src/components/DocumentFieldList.vue';
|
|
const controls: OfficeContentControl[] = [
|
{ Alias: '结果', ControlType: 'text', InternalId: 'primary-control', Tag: 'eln.field.result', Text: '12.3' },
|
{ Alias: '结果', ControlType: 'text', InternalId: 'reference-control', Tag: 'eln.ref.result.abc', Text: '12.3' },
|
{ Alias: '旧重复', ControlType: 'text', InternalId: 'repeat-control', Tag: 'eln.repeat.group.row.value' },
|
];
|
const bindings: TemplateBinding[] = [
|
{
|
controlAlias: '结果',
|
controlConfig: {},
|
controlRole: 'PRIMARY',
|
controlTag: 'eln.field.result',
|
controlType: 'TEXT',
|
fieldCode: 'result',
|
fieldId: 'field',
|
fieldVersionId: 'version',
|
id: 'primary',
|
primaryBindingId: null,
|
status: 'ACTIVE',
|
templateId: 'template',
|
version: 2,
|
},
|
{
|
controlAlias: '结果',
|
controlConfig: {},
|
controlRole: 'REFERENCE',
|
controlTag: 'eln.ref.result.abc',
|
controlType: 'TEXT',
|
fieldCode: 'result',
|
fieldId: 'field',
|
fieldVersionId: 'version',
|
id: 'reference',
|
primaryBindingId: 'primary',
|
status: 'ACTIVE',
|
templateId: 'template',
|
version: 1,
|
},
|
];
|
const groupedControls: OfficeContentControl[] = [
|
{ Alias: '合格', ControlType: 'checkbox', InternalId: 'passed-control', Tag: 'eln.field.passed' },
|
{ Alias: '不合格', ControlType: 'checkbox', InternalId: 'failed-control', Tag: 'eln.field.failed' },
|
{ Alias: '需复核', ControlType: 'checkbox', InternalId: 'review-control', Tag: 'eln.field.review' },
|
];
|
const groupedBindings: TemplateBinding[] = [
|
{
|
checkboxGroupCode: 'inspection_result',
|
controlAlias: '合格',
|
controlConfig: { defaultChecked: false },
|
controlRole: 'PRIMARY',
|
controlTag: 'eln.field.passed',
|
controlType: 'CHECKBOX',
|
fieldCode: 'passed',
|
fieldId: 'passed-field',
|
fieldVersionId: 'passed-version',
|
id: 'passed',
|
primaryBindingId: null,
|
status: 'ACTIVE',
|
stepId: 'step-1',
|
templateId: 'template',
|
version: 2,
|
},
|
{
|
checkboxGroupCode: 'inspection_result',
|
controlAlias: '不合格',
|
controlConfig: { defaultChecked: false },
|
controlRole: 'PRIMARY',
|
controlTag: 'eln.field.failed',
|
controlType: 'CHECKBOX',
|
fieldCode: 'failed',
|
fieldId: 'failed-field',
|
fieldVersionId: 'failed-version',
|
id: 'failed',
|
primaryBindingId: null,
|
status: 'ACTIVE',
|
stepId: 'step-1',
|
templateId: 'template',
|
version: 3,
|
},
|
{
|
checkboxGroupCode: 'review_result',
|
controlAlias: '需复核',
|
controlConfig: { defaultChecked: false },
|
controlRole: 'PRIMARY',
|
controlTag: 'eln.field.review',
|
controlType: 'CHECKBOX',
|
fieldCode: 'review',
|
fieldId: 'review-field',
|
fieldVersionId: 'review-version',
|
id: 'review',
|
primaryBindingId: null,
|
status: 'ACTIVE',
|
stepId: 'step-1',
|
templateId: 'template',
|
version: 1,
|
},
|
];
|
const fields: TemplateFieldOption[] = [
|
{
|
alias: '结果',
|
controlConfig: {},
|
fieldCode: 'result',
|
fieldId: 'field',
|
fieldVersionId: 'version',
|
isCurrent: false,
|
label: '结果 v1',
|
required: false,
|
scope: 'GLOBAL',
|
source: 'NONE',
|
type: 'TEXT',
|
versionStatus: 'PUBLISHED',
|
},
|
{
|
alias: '结果',
|
controlConfig: {},
|
fieldCode: 'result',
|
fieldId: 'field',
|
fieldVersionId: 'published-v2',
|
isCurrent: true,
|
label: '结果 v2',
|
required: false,
|
scope: 'GLOBAL',
|
source: 'NONE',
|
type: 'TEXT',
|
versionStatus: 'PUBLISHED',
|
},
|
{
|
alias: '结果',
|
controlConfig: {},
|
fieldCode: 'result',
|
fieldId: 'field',
|
fieldVersionId: 'pre-release-v3',
|
isCurrent: false,
|
label: '结果 v3',
|
required: false,
|
scope: 'GLOBAL',
|
source: 'NONE',
|
type: 'TEXT',
|
versionStatus: 'PRE_RELEASE',
|
},
|
{
|
alias: '结果',
|
controlConfig: {},
|
fieldCode: 'result',
|
fieldId: 'field',
|
fieldVersionId: 'draft-v4',
|
isCurrent: false,
|
label: '结果 v4',
|
required: false,
|
scope: 'GLOBAL',
|
source: 'NONE',
|
type: 'TEXT',
|
versionStatus: 'DRAFT',
|
},
|
{
|
alias: '其他',
|
controlConfig: {},
|
fieldCode: 'other',
|
fieldId: 'other-field',
|
fieldVersionId: 'other-version',
|
isCurrent: true,
|
label: '其他',
|
required: false,
|
scope: 'GLOBAL',
|
source: 'NONE',
|
type: 'TEXT',
|
versionStatus: 'PUBLISHED',
|
},
|
];
|
const DropdownStub = defineComponent({
|
name: 'ADropdown',
|
setup(_, { slots }) {
|
return () => h('div', [slots.default?.(), slots.overlay?.()]);
|
},
|
});
|
const TooltipStub = defineComponent({
|
name: 'ATooltip',
|
props: {
|
open: { default: undefined, type: Boolean },
|
placement: { default: undefined, type: String },
|
title: { default: undefined, type: String },
|
},
|
setup(_, { slots }) {
|
return () => h('span', slots.default?.());
|
},
|
});
|
function mountList(extra: Record<string, unknown> = {}) {
|
return mount(DocumentFieldList, {
|
props: { bindings, controls, fields, status: 'ready', ...extra },
|
global: {
|
plugins: [Antd],
|
stubs: {
|
'a-dropdown': DropdownStub,
|
'a-tooltip': TooltipStub,
|
},
|
},
|
});
|
}
|
|
describe('documentFieldList', () => {
|
it('输入停止 300ms 后自动搜索且不显示搜索按钮', async () => {
|
vi.useFakeTimers();
|
const wrapper = mountList();
|
|
try {
|
const input = wrapper.getComponent({ name: 'AInput' });
|
expect(wrapper.findComponent({ name: 'AInputSearch' }).exists()).toBe(false);
|
|
input.vm.$emit('update:value', '旧重复');
|
await nextTick();
|
expect(wrapper.findAll('[data-test="control-item"]')).toHaveLength(3);
|
|
vi.advanceTimersByTime(299);
|
await nextTick();
|
expect(wrapper.findAll('[data-test="control-item"]')).toHaveLength(3);
|
|
vi.advanceTimersByTime(1);
|
await nextTick();
|
expect(wrapper.findAll('[data-test="control-item"]')).toHaveLength(1);
|
expect(wrapper.get('[data-test="control-item"]').text()).toContain('旧重复');
|
|
input.vm.$emit('update:value', '不存在的字段');
|
await nextTick();
|
vi.advanceTimersByTime(300);
|
await nextTick();
|
expect(wrapper.getComponent({ name: 'AEmpty' }).props('description')).toBe('没有匹配的文档字段');
|
} finally {
|
wrapper.unmount();
|
vi.useRealTimers();
|
}
|
});
|
|
it('提供全部、选择组和异常分段,并为异常显示数量', async () => {
|
const wrapper = mountList({ controls: controls.slice(1) });
|
const segmented = wrapper.getComponent({ name: 'ASegmented' });
|
|
expect(segmented.props('options').map((option: { value: string }) => option.value)).toEqual(['all', 'group', 'anomaly']);
|
expect(wrapper.findAll('.document-filter-badge')).toHaveLength(1);
|
expect(wrapper.get('.document-filter .ant-badge-count').text()).toBe('2');
|
expect(wrapper.get('.document-filter .ant-badge-count').attributes('style')).toContain('background-color: #ff4d4f');
|
expect(wrapper.get('.document-filter').text()).toContain('全部');
|
expect(wrapper.find('[data-test="control-summary"]').exists()).toBe(false);
|
segmented.vm.$emit('update:value', 'anomaly');
|
await nextTick();
|
expect(wrapper.findAll('[data-test="control-item"]')).toHaveLength(1);
|
expect(wrapper.get('[data-test="control-item"]').text()).toContain('旧重复');
|
expect(wrapper.findAll('[data-test="missing-binding"]')).toHaveLength(1);
|
});
|
|
it('全部、选择组和异常分段复用同一位置的 Empty 组件', async () => {
|
const wrapper = mountList({ bindings: [], controls: [] });
|
const segmented = wrapper.getComponent({ name: 'ASegmented' });
|
const assertEmpty = (description: string) => {
|
const empty = wrapper.get('[data-test="document-empty"]');
|
expect(empty.classes()).toContain('empty-controls');
|
expect(wrapper.findAllComponents({ name: 'AEmpty' })).toHaveLength(1);
|
expect(wrapper.getComponent({ name: 'AEmpty' }).props('description')).toBe(description);
|
};
|
|
assertEmpty('当前文档没有内容控件');
|
segmented.vm.$emit('update:value', 'group');
|
await nextTick();
|
assertEmpty('没有匹配的选择组');
|
segmented.vm.$emit('update:value', 'anomaly');
|
await nextTick();
|
assertEmpty('没有异常字段');
|
});
|
|
it('行内标签关联同组字段,搜索同时匹配组编码', async () => {
|
vi.useFakeTimers();
|
const wrapper = mountList({ bindings: groupedBindings, controls: groupedControls });
|
|
try {
|
const labels = wrapper.findAll('[data-test="checkbox-group-label"]');
|
expect(labels).toHaveLength(3);
|
expect(labels[0]!.text()).toContain('选择组 · inspection_result');
|
expect(labels[0]!.attributes('style')).toContain('color:');
|
expect(labels[0]!.attributes('style')).not.toContain('border');
|
|
wrapper.getComponent({ name: 'AInput' }).vm.$emit('update:value', 'inspection_result');
|
vi.advanceTimersByTime(300);
|
await nextTick();
|
expect(wrapper.findAll('[data-test="control-item"]')).toHaveLength(2);
|
} finally {
|
wrapper.unmount();
|
vi.useRealTimers();
|
}
|
});
|
|
it('选择组视图显示成员、作用域和单成员异常,并可定位字段', async () => {
|
const wrapper = mountList({
|
bindings: groupedBindings,
|
controls: groupedControls,
|
steps: [{ id: 'step-1', name: '样品制备', order: 1 }],
|
});
|
const segmented = wrapper.getComponent({ name: 'ASegmented' });
|
|
segmented.vm.$emit('update:value', 'group');
|
await nextTick();
|
|
const groups = wrapper.findAll('[data-test="checkbox-group"]');
|
expect(groups).toHaveLength(2);
|
expect(groups[0]!.text()).toContain('inspection_result');
|
expect(groups[0]!.text()).toContain('2 个成员');
|
expect(groups[0]!.text()).toContain('步骤:样品制备');
|
expect(groups[0]!.text()).not.toContain('步骤:step-1');
|
expect(wrapper.get('[data-test="checkbox-group-anomaly"]').text()).toContain('只有一个成员');
|
await groups[0]!.find('[data-test="checkbox-group-member-locate"]').trigger('click');
|
expect(wrapper.emitted('select')).toEqual([['passed-control']]);
|
});
|
|
it('重复复选框按重复组展示并可修改选择组', async () => {
|
const repeatBindings: TemplateBinding[] = ['qualified', 'unqualified'].map((fieldCode, index) => ({
|
checkboxGroupCode: 'row_result',
|
controlAlias: index === 0 ? '合格' : '不合格',
|
controlConfig: { defaultChecked: false },
|
controlRole: 'REPEAT',
|
controlTag: `eln.repeat.results.prototype.${fieldCode}`,
|
controlType: 'CHECKBOX',
|
fieldCode,
|
fieldId: `${fieldCode}-field`,
|
fieldVersionId: `${fieldCode}-version`,
|
id: fieldCode,
|
primaryBindingId: null,
|
repeatGroupCode: 'results',
|
status: 'ACTIVE',
|
templateId: 'template',
|
version: index + 1,
|
}));
|
const repeatControls: OfficeContentControl[] = repeatBindings.map((binding) => ({
|
Alias: binding.controlAlias || '',
|
ControlType: 'checkbox',
|
InternalId: `${binding.id}-control`,
|
Tag: binding.controlTag,
|
}));
|
const wrapper = mountList({ bindings: repeatBindings, controls: repeatControls });
|
const segmented = wrapper.getComponent({ name: 'ASegmented' });
|
|
segmented.vm.$emit('update:value', 'group');
|
await nextTick();
|
expect(wrapper.get('[data-test="checkbox-group"]').text()).toContain('重复组:results');
|
expect(wrapper.get('[data-test="checkbox-group"]').text()).toContain('2 个成员');
|
|
segmented.vm.$emit('update:value', 'all');
|
await nextTick();
|
await wrapper.findAll('[data-test="change-checkbox-group"]')[0]!.trigger('click');
|
wrapper.getComponent({ name: 'AAutoComplete' }).vm.$emit('update:value', 'row_decision');
|
await nextTick();
|
await wrapper.get('[data-test="save-checkbox-group"]').trigger('click');
|
expect(wrapper.emitted('change-checkbox-group')).toEqual([
|
['qualified', { groupCode: 'row_decision', maxSelected: 1, minSelected: 0 }, 1],
|
]);
|
});
|
|
it('同一选择组跨作用域时在组视图和异常视图显示冲突上下文', async () => {
|
const conflictingBindings = groupedBindings.map((binding) => (binding.id === 'failed' ? { ...binding, stepId: 'step-2' } : binding));
|
const wrapper = mountList({ bindings: conflictingBindings, controls: groupedControls });
|
const segmented = wrapper.getComponent({ name: 'ASegmented' });
|
|
segmented.vm.$emit('update:value', 'group');
|
await nextTick();
|
const conflicted = wrapper.findAll('[data-test="checkbox-group"]').find((group) => group.text().includes('inspection_result'))!;
|
expect(conflicted.get('[data-test="checkbox-group-anomaly"]').text()).toContain('跨越多个作用域');
|
expect(conflicted.text()).toContain('步骤:step-1');
|
expect(conflicted.text()).toContain('步骤:step-2');
|
|
segmented.vm.$emit('update:value', 'anomaly');
|
await nextTick();
|
expect(wrapper.get('[data-test="checkbox-group"]').text()).toContain('inspection_result');
|
});
|
|
it('默认值越界和非法成员都进入选择组异常视图', async () => {
|
const anomalyBindings: TemplateBinding[] = [
|
{ ...groupedBindings[0]!, controlConfig: { defaultChecked: true } },
|
{ ...groupedBindings[1]!, controlConfig: { defaultChecked: true } },
|
{
|
...groupedBindings[2]!,
|
checkboxGroupCode: 'invalid_members',
|
controlRole: 'REFERENCE',
|
},
|
{
|
...groupedBindings[2]!,
|
checkboxGroupCode: 'invalid_members',
|
controlAlias: '文本字段',
|
controlType: 'TEXT',
|
id: 'invalid-text',
|
},
|
];
|
const wrapper = mountList({
|
bindings: anomalyBindings,
|
checkboxGroups: [{ groupCode: 'inspection_result', maxSelected: 1, minSelected: 0, version: 1 }],
|
controls: groupedControls,
|
});
|
const segmented = wrapper.getComponent({ name: 'ASegmented' });
|
|
segmented.vm.$emit('update:value', 'anomaly');
|
await nextTick();
|
|
const groups = wrapper.findAll('[data-test="checkbox-group"]');
|
expect(groups).toHaveLength(2);
|
expect(groups.find((group) => group.text().includes('inspection_result'))!.text()).toContain('默认勾选数不符合数量规则');
|
expect(groups.find((group) => group.text().includes('invalid_members'))!.text()).toContain('包含不允许入组的字段');
|
});
|
|
it('可设置、修改或移出选择组,并携带完整规则和绑定当前版本', async () => {
|
const wrapper = mountList({
|
bindings: groupedBindings,
|
checkboxGroups: [
|
{ groupCode: 'inspection_result', maxSelected: 1, minSelected: 0, version: 1 },
|
{ groupCode: 'review_result', maxSelected: 3, minSelected: 1, version: 2 },
|
],
|
controls: groupedControls,
|
});
|
|
await wrapper.findAll('[data-test="change-checkbox-group"]')[0]!.trigger('click');
|
expect(wrapper.find('[data-test="document-main"]').exists()).toBe(false);
|
const input = wrapper.getComponent({ name: 'AAutoComplete' });
|
input.vm.$emit('update:value', 'review_result');
|
await nextTick();
|
await wrapper.get('[data-test="save-checkbox-group"]').trigger('click');
|
expect((wrapper.get('[data-test="checkbox-group-edit-min"]').element as HTMLInputElement).value).toBe('1');
|
expect(wrapper.get('[data-test="checkbox-group-edit-min"]').attributes('disabled')).toBeDefined();
|
expect(wrapper.emitted('change-checkbox-group')).toEqual([
|
['passed', { groupCode: 'review_result', maxSelected: 3, minSelected: 1 }, 2],
|
]);
|
|
const removeWrapper = mountList({ bindings: groupedBindings, controls: groupedControls });
|
await removeWrapper.findAll('[data-test="remove-checkbox-group"]')[0]!.trigger('click');
|
expect(removeWrapper.emitted('change-checkbox-group')).toEqual([['passed', null, 2]]);
|
});
|
|
it('选择组视图展示权威规则并可修改共享上下限', async () => {
|
const wrapper = mountList({
|
bindings: groupedBindings,
|
checkboxGroups: [{ groupCode: 'inspection_result', maxSelected: 2, minSelected: 1, version: 4 }],
|
controls: groupedControls,
|
});
|
const segmented = wrapper.getComponent({ name: 'ASegmented' });
|
segmented.vm.$emit('update:value', 'group');
|
await nextTick();
|
|
const group = wrapper.findAll('[data-test="checkbox-group"]').find((item) => item.text().includes('inspection_result'))!;
|
expect(group.text()).toContain('至少 1 项,最多 2 项');
|
await group.get('[data-test="edit-checkbox-group-rule"]').trigger('click');
|
await wrapper.get('[data-test="checkbox-group-rule-min"]').setValue(0);
|
await wrapper.get('[data-test="checkbox-group-rule-max"]').setValue(3);
|
await wrapper.get('[data-test="save-checkbox-group-rule"]').trigger('click');
|
|
expect(wrapper.emitted('change-checkbox-group-rule')).toEqual([['inspection_result', 0, 3, 4]]);
|
});
|
|
it('区分主字段、字段引用和重复控件,并显示缺失绑定异常', () => {
|
const wrapper = mountList({ controls: controls.slice(1) });
|
const referenceRow = wrapper.findAll('[data-test="control-item"]').find((item) => item.text().includes('引用'))!;
|
const roles = wrapper.findAll('[data-test="control-role"]');
|
|
expect(referenceRow.get('.control-metadata').text()).toBe('result');
|
expect(referenceRow.text()).not.toContain('字段编码:');
|
expect(wrapper.findAll('.control-metadata').some((item) => item.text() === 'value')).toBe(true);
|
expect(wrapper.findComponent({ name: 'ATag' }).exists()).toBe(false);
|
expect(roles.map((item) => item.attributes('data-role'))).toEqual(['repeat', 'reference']);
|
expect(roles[0]!.text()).toBe('·重复行');
|
expect(wrapper.findAll('[data-test="control-type"]').map((item) => item.text())).toEqual(['单行文本', '单行文本']);
|
expect(wrapper.get('[data-test="missing-binding"]').text()).toContain('主字段控件缺失');
|
});
|
|
it('已绑定数值字段按字段语义类型显示,不降级为 Word 单行文本控件类型', () => {
|
const wrapper = mountList({
|
bindings: bindings.slice(0, 1),
|
controls: controls.slice(0, 1),
|
fields: [{ ...fields[0]!, type: 'DECIMAL' }],
|
});
|
|
expect(wrapper.get('[data-test="control-type"]').text()).toBe('数值');
|
});
|
|
it('绑定上下文缺失时仍按控件类型显示主字段角色', () => {
|
const wrapper = mountList({ bindings: [], controls: controls.slice(0, 1) });
|
const role = wrapper.get('[data-test="control-role"]');
|
|
expect(role.attributes('data-role')).toBe('primary');
|
expect(role.text()).toBe('·主字段');
|
expect(wrapper.get('[data-test="anomaly-reason"]').text()).toBe('文档控件没有有效或待确认绑定');
|
});
|
|
it('缺失绑定异常显示 DOCX 控件已缺失的原因', () => {
|
const wrapper = mountList({ controls: controls.slice(1) });
|
|
expect(wrapper.get('[data-test="missing-binding"] [data-test="anomaly-reason"]').text()).toBe('绑定存在但 DOCX 控件已缺失');
|
});
|
|
it('孤立控件提供删除操作并携带控件内部 ID', async () => {
|
const wrapper = mountList({ bindings: [], controls: controls.slice(0, 1) });
|
const confirmSpy = vi.spyOn(Modal, 'confirm').mockReturnValue({ destroy: vi.fn(), update: vi.fn() } as ReturnType<typeof Modal.confirm>);
|
|
try {
|
const removeButton = wrapper.findAllComponents({ name: 'AButton' }).find((button) => button.attributes('data-test') === 'remove-orphan-control')!;
|
|
expect(removeButton.props('shape')).toBe('circle');
|
expect(removeButton.attributes('aria-label')).toBe('删除异常控件');
|
await removeButton.trigger('click');
|
await (confirmSpy.mock.calls[0]![0].onOk as () => void)();
|
|
expect(wrapper.emitted('remove-orphan-control')).toEqual([['primary-control']]);
|
} finally {
|
confirmSpy.mockRestore();
|
}
|
});
|
|
it('全部视图将异常控件排在普通控件之前', () => {
|
const wrapper = mountList();
|
const rows = wrapper.findAll('[data-test="control-item"]');
|
|
expect(rows[0]!.text()).toContain('旧重复');
|
expect(rows[1]!.text()).toContain('结果');
|
});
|
|
it('重复主字段不直接插入,向上发出引用创建动作', async () => {
|
const wrapper = mountList();
|
await wrapper.get('[data-test="insert-reference-primary"]').trigger('click');
|
expect(wrapper.emitted('insert-reference')).toEqual([['primary']]);
|
});
|
|
it('主字段删除和引用提升使用绑定 ID,普通非 ELN 控件没有业务动作', async () => {
|
const wrapper = mountList({ controls: [...controls, { InternalId: 'other', Tag: 'custom.tag' }] });
|
const confirmSpy = vi.spyOn(Modal, 'confirm').mockReturnValue({ destroy: vi.fn(), update: vi.fn() } as ReturnType<typeof Modal.confirm>);
|
|
try {
|
expect(wrapper.get('[data-test="remove-binding"]').element.closest('.ant-menu-item')).not.toBeNull();
|
expect(wrapper.find('.row-action-overlay').exists()).toBe(false);
|
await wrapper.get('[data-test="remove-binding"]').trigger('click');
|
expect(confirmSpy).toHaveBeenCalledWith(expect.objectContaining({ okText: '删除', okType: 'danger', title: '删除该字段控件?' }));
|
await (confirmSpy.mock.calls[0]![0].onOk as () => void)();
|
await wrapper.get('[data-test="promote-reference"]').trigger('click');
|
expect(wrapper.emitted('remove-binding')).toEqual([['primary', 'primary-control']]);
|
expect(wrapper.emitted('promote-reference')).toEqual([['reference']]);
|
expect(wrapper.findAll('[data-test="remove-binding"]')).toHaveLength(2);
|
} finally {
|
confirmSpy.mockRestore();
|
}
|
});
|
|
it('repair 仅针对缺失绑定并复用原 binding ID', async () => {
|
const wrapper = mountList({ controls: controls.slice(1) });
|
await wrapper.get('[data-test="repair-binding"]').trigger('click');
|
expect(wrapper.emitted('repair')).toEqual([['primary']]);
|
});
|
|
it('刷新和定位仍可用,working 时禁用写动作', async () => {
|
const wrapper = mountList();
|
const refreshButton = wrapper.findAllComponents({ name: 'AButton' }).find((button) => button.attributes('data-test') === 'refresh-controls')!;
|
const refreshTooltip = wrapper.findAllComponents(TooltipStub).find((tooltip) => tooltip.props('title') === '刷新文档字段')!;
|
const actionTooltip = wrapper.findAllComponents(TooltipStub).find((tooltip) => tooltip.props('title') === '更多操作')!;
|
const actionDropdown = wrapper.findAllComponents(DropdownStub)[0]!;
|
|
expect(refreshTooltip.props('placement')).toBe('topRight');
|
expect(actionTooltip.props('open')).toBeUndefined();
|
actionDropdown.vm.$emit('openChange', true);
|
await nextTick();
|
expect(actionTooltip.props('open')).toBe(false);
|
expect(refreshButton.props('loading')).toBe(false);
|
await wrapper.get('[data-test="refresh-controls"]').trigger('click');
|
await wrapper
|
.findAll('[data-test="control-item"]')
|
.find((item) => item.text().includes('主字段'))!
|
.trigger('click');
|
expect(wrapper.emitted('refresh')).toHaveLength(1);
|
expect(wrapper.emitted('select')).toEqual([['primary-control']]);
|
await wrapper.setProps({ status: 'working' });
|
expect(refreshButton.props('loading')).toBe(false);
|
expect(refreshButton.props('disabled')).toBe(true);
|
expect(wrapper.findAll('[data-write-action="true"]').every((item) => item.attributes('disabled') !== undefined)).toBe(true);
|
});
|
|
it('aCTIVE 主字段只允许升级到同字段的发布或预发布版本,并标记预发布风险', async () => {
|
const wrapper = mountList();
|
expect(wrapper.find('[data-test="upgrade-version"]').exists()).toBe(false);
|
await wrapper.get('[data-test="open-upgrade-binding"]').trigger('click');
|
expect(wrapper.find('[data-test="document-main"]').exists()).toBe(false);
|
const upgradeSelect = wrapper.getComponent({ name: 'ASelect' });
|
const optionValues = upgradeSelect.props('options').map((item: { value: string }) => item.value);
|
expect(optionValues).toEqual(['published-v2', 'pre-release-v3']);
|
expect(upgradeSelect.props('options')[1].label).toContain('预发布');
|
expect(optionValues).not.toContain('draft-v4');
|
expect(optionValues).not.toContain('other-version');
|
|
(upgradeSelect.props()['onUpdate:value'] as (value: string) => void)('pre-release-v3');
|
await nextTick();
|
await wrapper.get('[data-test="upgrade-binding"]').trigger('click');
|
expect(wrapper.emitted('upgrade-binding')).toEqual([['primary', 'pre-release-v3']]);
|
expect(wrapper.text()).toContain('预发布版本仅供模板验证');
|
});
|
|
it('只读时保留刷新和定位,但包括 repair 在内的全部写动作均不 emit', async () => {
|
const wrapper = mountList({ controls: controls.slice(1), readOnly: true });
|
await wrapper
|
.findAll('[data-test="control-item"]')
|
.find((item) => item.text().includes('引用'))!
|
.trigger('click');
|
await wrapper.get('[data-test="repair-binding"]').trigger('click');
|
expect(wrapper.emitted('select')).toEqual([['reference-control']]);
|
expect(wrapper.get('[data-test="refresh-controls"]').attributes('disabled')).toBeUndefined();
|
expect(wrapper.findAll('[data-write-action="true"]').every((item) => item.attributes('disabled') !== undefined)).toBe(true);
|
expect(wrapper.emitted('repair')).toBeUndefined();
|
});
|
|
it('修复模式只保留目标恢复、刷新和定位动作', () => {
|
const wrapper = mountList({ controls: controls.slice(1), repairBindingId: 'primary' });
|
expect(wrapper.find('[data-test="insert-reference-primary"]').exists()).toBe(false);
|
expect(wrapper.find('[data-test="promote-reference"]').exists()).toBe(false);
|
expect(wrapper.find('[data-test="remove-binding"]').exists()).toBe(false);
|
expect(wrapper.find('[data-test="upgrade-binding"]').exists()).toBe(false);
|
expect(wrapper.get('[data-test="repair-binding"]').text()).toContain('在文档选择位置后恢复');
|
expect(wrapper.get('[data-test="refresh-controls"]').attributes('disabled')).toBeUndefined();
|
});
|
});
|