import type { VueWrapper } from '@vue/test-utils';
|
|
import type { TemplateAnnotationClient } from '../src/api/template-annotation';
|
import type { TemplateAnnotationContext, TemplateBinding } from '../src/domain/types';
|
import type { OnlyOfficeBridge } from '../src/onlyoffice/bridge';
|
import type { OfficeContentControl } from '../src/onlyoffice/types';
|
|
import { mount } from '@vue/test-utils';
|
|
import Antd, { message } from 'ant-design-vue';
|
import { createPinia } from 'pinia';
|
import { afterEach, beforeAll, describe, expect, it, vi } from 'vitest';
|
|
import App from '../src/App.vue';
|
import DocumentFieldList from '../src/components/DocumentFieldList.vue';
|
import FieldEditor from '../src/components/FieldEditor.vue';
|
|
beforeAll(() => {
|
window.matchMedia ??= () =>
|
({
|
addEventListener() {},
|
addListener() {},
|
dispatchEvent: () => false,
|
matches: false,
|
media: '',
|
onchange: null,
|
removeEventListener() {},
|
removeListener() {},
|
}) as MediaQueryList;
|
globalThis.ResizeObserver ??= class ResizeObserver {
|
disconnect() {}
|
observe() {}
|
unobserve() {}
|
};
|
});
|
|
const wrappers: VueWrapper[] = [];
|
afterEach(() => wrappers.splice(0).forEach((wrapper) => wrapper.unmount()));
|
|
const activeBinding: TemplateBinding = {
|
controlAlias: '结果',
|
controlConfig: { placeholder: '请输入' },
|
controlRole: 'PRIMARY',
|
controlTag: 'eln.field.result',
|
controlType: 'TEXT',
|
fieldCode: 'result',
|
fieldId: 'field',
|
fieldVersionId: 'version',
|
id: 'binding',
|
primaryBindingId: null,
|
status: 'ACTIVE',
|
templateId: 'template',
|
version: 1,
|
};
|
|
function context(bindings: TemplateBinding[] = []): TemplateAnnotationContext {
|
return {
|
bindings,
|
stepConfiguration: {
|
singleStep: true,
|
steps: [{ id: 'step-1', name: '实验步骤', order: 1 }],
|
},
|
templateCode: 'TMP',
|
templateId: 'template',
|
fields: [
|
{
|
beizhu: null,
|
bizEnabled: 'enabled',
|
dangqianZiduanBanbenId: 'version',
|
id: 'field',
|
mubanBianhao: null,
|
ziduanBianma: 'result',
|
zuoyongFanwei: 'GLOBAL',
|
versions: [
|
{
|
banbenHao: 1,
|
binding: null,
|
bizStatus: 'PUBLISHED',
|
chuangjianShijian: null,
|
danwei: null,
|
gengxinShijian: null,
|
id: 'version',
|
kongjianPeizhi: { placeholder: '请输入' },
|
piciMoshi: null,
|
shifouBitian: 0,
|
shujuLeixing: 'TEXT',
|
ziduanDingyiId: 'field',
|
ziduanMingcheng: '结果',
|
zuofeiYuanyin: null,
|
},
|
],
|
},
|
],
|
};
|
}
|
|
function createFixture(canAnnotate = true, initialContext = context()) {
|
let lifecycle: Parameters<OnlyOfficeBridge['bindLifecycle']>[0] | undefined;
|
const cleanup = vi.fn();
|
const bridge: OnlyOfficeBridge = {
|
applyControlPlan: vi.fn().mockResolvedValue(undefined),
|
bindLifecycle: vi.fn((callbacks) => {
|
lifecycle = callbacks;
|
return cleanup;
|
}),
|
close: vi.fn(),
|
deleteControl: vi.fn(),
|
disableTrackRevisions: vi.fn().mockResolvedValue(undefined),
|
getRuntimeOptions: vi.fn(() => ({
|
apiBaseUrl: 'http://localhost:30000',
|
documentId: 'file-1',
|
bizDataId: 'record-1',
|
bizModule: 'eln.template',
|
bizScene: 'eln.template.annotate',
|
pluginVersion: '0.1.0',
|
scope: 'onlyoffice:eln-template-annotate',
|
ticket: 'ticket',
|
canAnnotate,
|
})),
|
insertBinding: vi.fn().mockResolvedValue(undefined),
|
insertReadOnlyBinding: vi.fn().mockResolvedValue(undefined),
|
insertField: vi.fn(),
|
insertReference: vi.fn().mockResolvedValue(undefined),
|
isFormulaResultLocked: vi.fn().mockResolvedValue(false),
|
lockFormulaResult: vi.fn().mockResolvedValue({ primaryTag: 'eln.field.result', locks: [] }),
|
readSelectedText: vi.fn().mockResolvedValue(''),
|
restoreFormulaResultLocks: vi.fn().mockResolvedValue(undefined),
|
unlockFormulaResult: vi.fn().mockResolvedValue(undefined),
|
listControls: vi.fn().mockResolvedValue([]),
|
selectControl: vi.fn().mockResolvedValue(undefined),
|
snapshotControls: vi.fn((bindings: TemplateBinding[], controls: OfficeContentControl[]) =>
|
bindings
|
.filter((binding) => controls.some((control) => control.Tag === binding.controlTag))
|
.map((binding) => ({ bindingId: binding.id, controlTag: binding.controlTag })),
|
),
|
};
|
const client: TemplateAnnotationClient = {
|
activateBinding: vi.fn().mockResolvedValue(undefined),
|
cancelBinding: vi.fn().mockResolvedValue(undefined),
|
changeCheckboxGroup: vi.fn().mockImplementation(async (_context, _bindingId, form) => ({
|
...activeBinding,
|
checkboxGroupCode: form.checkboxGroupCode,
|
version: form.expectedVersion + 1,
|
})),
|
changeCheckboxGroupRule: vi.fn().mockImplementation(async (_context, groupCode, form) => ({
|
groupCode,
|
maxSelected: form.maxSelected,
|
minSelected: form.minSelected,
|
version: form.expectedVersion + 1,
|
})),
|
checkBindingRemoval: vi.fn().mockResolvedValue({ controls: [], dependencies: [], expectedVersions: { binding: 1 }, operationCode: 'remove' }),
|
checkBindingVersionUpgrade: vi.fn(),
|
checkReferencePromotion: vi.fn(),
|
confirmBindingRemoval: vi.fn().mockResolvedValue(undefined),
|
confirmBindingVersionUpgrade: vi.fn(),
|
confirmReferencePromotion: vi.fn(),
|
confirmRepair: vi.fn(),
|
confirmVersionCopy: vi.fn(),
|
fetchAnnotationContext: vi.fn().mockResolvedValue(initialContext),
|
fetchCrossTemplateScalarCandidates: vi.fn().mockResolvedValue([]),
|
fetchCrossTemplateSourceTemplates: vi.fn().mockResolvedValue([]),
|
fetchRepairContext: vi.fn(),
|
reservePrimaryBinding: vi.fn().mockResolvedValue({ ...activeBinding, status: 'PENDING', version: 0 }),
|
reserveCrossTemplateScalarBinding: vi.fn(),
|
reservePrivateField: vi.fn(),
|
reserveReferenceBinding: vi.fn(),
|
activateFormulaBinding: vi.fn().mockResolvedValue(undefined),
|
cancelFormulaBinding: vi.fn().mockResolvedValue(undefined),
|
checkFormulaBindingRemoval: vi.fn(),
|
checkFormulaBindingUpgrade: vi.fn(),
|
confirmFormulaBindingRemoval: vi.fn(),
|
confirmFormulaBindingUpgrade: vi.fn(),
|
confirmFormulaVersionCopy: vi.fn(),
|
fetchFormulaMappings: vi.fn().mockResolvedValue({ bindings: [], versions: [] }),
|
reserveFormulaBinding: vi.fn(),
|
suggestCode: vi.fn().mockResolvedValue('jianyan_jieguo'),
|
};
|
return { bridge, client, cleanup, ready: () => lifecycle?.onReady() };
|
}
|
|
function mountApp(fixture: null | ReturnType<typeof createFixture>) {
|
const wrapper = mount(App, {
|
global: {
|
plugins: [createPinia(), Antd],
|
provide: { onlyOfficeBridge: fixture?.bridge ?? null, ...(fixture ? { templateAnnotationClient: fixture.client } : {}) },
|
},
|
});
|
wrappers.push(wrapper);
|
return wrapper;
|
}
|
|
async function openDocumentTask(wrapper: VueWrapper): Promise<void> {
|
await wrapper
|
.findAll('.ant-tabs-tab')
|
.find((tab) => tab.text().includes('文档'))!
|
.trigger('click');
|
}
|
|
describe('app', () => {
|
it('沿用 Ant Design Vue 默认主题和组件尺寸', () => {
|
const wrapper = mountApp(createFixture());
|
const configProvider = wrapper.getComponent({ name: 'AConfigProvider' });
|
const tabs = wrapper.getComponent({ name: 'ATabs' });
|
|
expect(configProvider.props('theme')).toBeUndefined();
|
expect(tabs.props('size')).toBeUndefined();
|
expect(configProvider.props('locale')).toMatchObject({ locale: 'zh-cn' });
|
});
|
|
it('按标注、文档和公式三个顶层任务页签隔离内容', async () => {
|
const fixture = createFixture(true, context([activeBinding]));
|
vi.mocked(fixture.bridge.listControls).mockResolvedValue([{ Alias: '结果', InternalId: 'control', Tag: activeBinding.controlTag }]);
|
const wrapper = mountApp(fixture);
|
fixture.ready();
|
await vi.waitFor(() => expect(wrapper.get('[data-test="connection-status"]').text()).toBe('已连接'));
|
|
expect(wrapper.get('[data-test="primary-task-tabs"]').text()).toContain('标注');
|
expect(wrapper.get('[data-test="document-tab-count"]').text()).toBe('1');
|
expect(wrapper.find('[data-test="formula-tab-count"]').exists()).toBe(false);
|
expect(wrapper.findComponent(FieldEditor).exists()).toBe(true);
|
expect(wrapper.findComponent(DocumentFieldList).exists()).toBe(false);
|
|
await openDocumentTask(wrapper);
|
expect(wrapper.findComponent(FieldEditor).exists()).toBe(false);
|
expect(wrapper.findComponent(DocumentFieldList).exists()).toBe(true);
|
expect(wrapper.findComponent(DocumentFieldList).props('steps')).toEqual([{ id: 'step-1', name: '实验步骤', order: 1 }]);
|
});
|
|
it('初始显示版本和加载状态并禁用编辑', () => {
|
const wrapper = mountApp(createFixture());
|
expect(wrapper.get('[data-test="plugin-version"]').text()).toBe('v0.1.0');
|
expect(wrapper.get('[data-test="connection-status"]').text()).toBe('加载中');
|
expect(wrapper.get('[data-test="submit-field"]').attributes('disabled')).toBeDefined();
|
});
|
|
it('加载过程只使用一条可原位更新的全局 loading message', async () => {
|
vi.useFakeTimers();
|
const loadingSpy = vi.spyOn(message, 'loading');
|
const wrapper = mountApp(createFixture());
|
|
try {
|
await vi.advanceTimersByTimeAsync(400);
|
expect(loadingSpy).toHaveBeenCalledWith({ content: '正在连接 ONLYOFFICE...', duration: 0, key: 'eln-template-operation' });
|
expect(wrapper.find('[data-test="task-progress"]').exists()).toBe(false);
|
expect(wrapper.find('[data-test="runtime-message"]').exists()).toBe(false);
|
} finally {
|
message.destroy('eln-template-operation');
|
loadingSpy.mockRestore();
|
vi.useRealTimers();
|
}
|
});
|
|
it('sDK ready 拉取 Ticket context 并装配字段选择器', async () => {
|
const fixture = createFixture();
|
const wrapper = mountApp(fixture);
|
fixture.ready();
|
await vi.waitFor(() => expect(wrapper.get('[data-test="connection-status"]').text()).toBe('已连接'));
|
expect(fixture.client.fetchAnnotationContext).toHaveBeenCalledOnce();
|
expect(wrapper.findComponent(FieldEditor).props('fields')).toHaveLength(1);
|
});
|
|
it('单步骤模板隐藏当前步骤选择器', async () => {
|
const fixture = createFixture();
|
const wrapper = mountApp(fixture);
|
fixture.ready();
|
await vi.waitFor(() => expect(wrapper.get('[data-test="connection-status"]').text()).toBe('已连接'));
|
|
expect(wrapper.find('[data-test="current-step"]').exists()).toBe(false);
|
});
|
|
it('多步骤模板按全局顺序展示可搜索选择器,选择前禁用标注', async () => {
|
const multiStepContext = context();
|
multiStepContext.stepConfiguration = {
|
singleStep: false,
|
steps: [
|
{ id: 'step-3', name: '复核', order: 3 },
|
{ id: 'step-2', name: '溶解', order: 2 },
|
{ id: 'step-1', name: '称量', order: 1 },
|
{ id: 'step-4', name: '归档', order: 4 },
|
],
|
};
|
const fixture = createFixture(true, multiStepContext);
|
const wrapper = mountApp(fixture);
|
fixture.ready();
|
await vi.waitFor(() => expect(wrapper.get('[data-test="connection-status"]').text()).toBe('已连接'));
|
|
const selector = wrapper.getComponent({ name: 'ASelect' });
|
expect(selector.attributes('data-test')).toBe('current-step');
|
expect(selector.props('showSearch')).toBe(true);
|
expect(selector.props('optionFilterProp')).toBe('label');
|
expect(selector.props('options')).toEqual([
|
{ label: '称量', value: 'step-1' },
|
{ label: '溶解', value: 'step-2' },
|
{ label: '复核', value: 'step-3' },
|
{ label: '归档', value: 'step-4' },
|
]);
|
expect(wrapper.findComponent(FieldEditor).props('disabled')).toBe(true);
|
|
selector.vm.$emit('update:value', 'step-2');
|
await wrapper.vm.$nextTick();
|
|
expect(wrapper.findComponent(FieldEditor).props('disabled')).toBe(false);
|
});
|
|
it('只读上下文禁用写动作但允许刷新', async () => {
|
const fixture = createFixture(false);
|
const wrapper = mountApp(fixture);
|
fixture.ready();
|
await vi.waitFor(() => expect(wrapper.get('[data-test="connection-status"]').text()).toBe('已连接'));
|
expect(wrapper.get('[data-test="submit-field"]').attributes('disabled')).toBeDefined();
|
await openDocumentTask(wrapper);
|
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);
|
});
|
|
it('修复模式隐藏字段编辑器,等待用户选择位置后才允许恢复目标控件', async () => {
|
const fixture = createFixture(true, context([activeBinding]));
|
vi.mocked(fixture.bridge.getRuntimeOptions).mockReturnValue({
|
apiBaseUrl: 'http://localhost:30000',
|
documentId: 'file-1',
|
bizDataId: 'record-1',
|
bizModule: 'eln.template',
|
bizScene: 'eln.template.annotate',
|
pluginVersion: '0.1.0',
|
scope: 'onlyoffice:eln-template-annotate',
|
ticket: 'ticket',
|
canAnnotate: true,
|
repairBindingId: 'binding',
|
});
|
vi.mocked(fixture.client.fetchRepairContext).mockResolvedValue({
|
binding: activeBinding,
|
controls: [{ bindingId: 'binding', controlTag: activeBinding.controlTag }],
|
dependencies: [],
|
expectedVersions: { binding: 1 },
|
operationCode: 'repair',
|
});
|
vi.mocked(fixture.bridge.listControls)
|
.mockResolvedValueOnce([])
|
.mockResolvedValueOnce([])
|
.mockResolvedValueOnce([{ Alias: '结果', InternalId: 'restored', Tag: activeBinding.controlTag }]);
|
const wrapper = mountApp(fixture);
|
fixture.ready();
|
await vi.waitFor(() => expect(wrapper.find('[data-test="repair-binding"]').exists()).toBe(true));
|
|
expect(wrapper.findComponent(FieldEditor).exists()).toBe(false);
|
expect(fixture.bridge.insertBinding).not.toHaveBeenCalled();
|
await wrapper.get('[data-test="repair-binding"]').trigger('click');
|
await vi.waitFor(() => expect(fixture.client.confirmRepair).toHaveBeenCalledOnce());
|
expect(fixture.bridge.insertBinding).toHaveBeenCalledWith(expect.objectContaining({ id: 'binding' }), expect.any(AbortSignal));
|
await vi.waitFor(() => expect(wrapper.find('[data-test="missing-binding"]').exists()).toBe(false));
|
expect(wrapper.get('[data-test="control-item"]').text()).toContain('结果');
|
expect(wrapper.find('.document-filter-badge').exists()).toBe(false);
|
});
|
|
it('字段列表升级事件连接到版本升级工作流', async () => {
|
const upgradeContext = context([activeBinding]);
|
upgradeContext.fields[0]!.versions.push({
|
...upgradeContext.fields[0]!.versions[0]!,
|
bizStatus: 'PRE_RELEASE',
|
id: 'pre-release-v2',
|
ziduanMingcheng: '结果 v2',
|
});
|
const fixture = createFixture(true, upgradeContext);
|
vi.mocked(fixture.client.checkBindingVersionUpgrade).mockResolvedValue({
|
controls: [],
|
dependencies: [],
|
expectedVersions: { binding: 1 },
|
operationCode: 'upgrade',
|
});
|
const wrapper = mountApp(fixture);
|
fixture.ready();
|
await vi.waitFor(() => expect(wrapper.get('[data-test="connection-status"]').text()).toBe('已连接'));
|
await openDocumentTask(wrapper);
|
expect(wrapper.findComponent(DocumentFieldList).props('fields')).toHaveLength(2);
|
|
wrapper.findComponent(DocumentFieldList).vm.$emit('upgrade-binding', 'binding', 'pre-release-v2');
|
await vi.waitFor(() => expect(fixture.client.confirmBindingVersionUpgrade).toHaveBeenCalledOnce());
|
});
|
|
it('fieldEditor 透传选择组规则并在标注成功后保留组件实例', async () => {
|
const fixture = createFixture();
|
vi.mocked(fixture.bridge.listControls)
|
.mockResolvedValueOnce([])
|
.mockResolvedValueOnce([])
|
.mockResolvedValueOnce([{ Alias: '结果', InternalId: 'new', Tag: activeBinding.controlTag }]);
|
const wrapper = mountApp(fixture);
|
fixture.ready();
|
await vi.waitFor(() => expect(wrapper.findComponent(FieldEditor).props('fields')).toHaveLength(1));
|
const fieldEditor = wrapper.findComponent(FieldEditor);
|
const fieldEditorId = fieldEditor.vm.$.uid;
|
const checkboxField = { ...fieldEditor.props('fields')[0], type: 'CHECKBOX' as const };
|
|
fieldEditor.vm.$emit('submit', checkboxField, { groupCode: 'inspection_result', maxSelected: 2, minSelected: 1 });
|
await vi.waitFor(() => expect(fixture.client.activateBinding).toHaveBeenCalledOnce());
|
expect(fixture.client.reservePrimaryBinding).toHaveBeenCalledWith(
|
expect.anything(),
|
expect.objectContaining({ checkboxGroupCode: 'inspection_result', checkboxGroupMaxSelected: 2, checkboxGroupMinSelected: 1 }),
|
);
|
expect(fixture.bridge.insertBinding).toHaveBeenCalledOnce();
|
expect(wrapper.findComponent(FieldEditor).vm.$.uid).toBe(fieldEditorId);
|
expect(wrapper.findComponent(FieldEditor).props('resetToken')).toBe(1);
|
await openDocumentTask(wrapper);
|
expect(wrapper.get('[data-test="control-item"]').text()).toContain('结果');
|
});
|
|
it('fieldEditor 使用当前 annotator 的编码建议方法', async () => {
|
const fixture = createFixture();
|
const wrapper = mountApp(fixture);
|
fixture.ready();
|
await vi.waitFor(() => expect(wrapper.findComponent(FieldEditor).props('fields')).toHaveLength(1));
|
|
const suggestCode = wrapper.findComponent(FieldEditor).props('suggestCode');
|
await expect(suggestCode?.('检验结果')).resolves.toBe('jianyan_jieguo');
|
expect(fixture.client.suggestCode).toHaveBeenCalledWith(expect.objectContaining({ ticket: 'ticket' }), { text: '检验结果' });
|
});
|
|
it('fieldEditor 使用编辑器当前选中文本预填字段名称', async () => {
|
const fixture = createFixture();
|
vi.mocked(fixture.bridge.readSelectedText).mockResolvedValue('是否合格');
|
const wrapper = mountApp(fixture);
|
fixture.ready();
|
await vi.waitFor(() => expect(wrapper.findComponent(FieldEditor).props('fields')).toHaveLength(1));
|
|
await wrapper.get('[data-test="open-private-field"]').trigger('click');
|
await vi.waitFor(() => expect(wrapper.find('[data-test="private-field-name"]').exists()).toBe(true));
|
|
expect(fixture.bridge.readSelectedText).toHaveBeenCalledOnce();
|
expect(wrapper.get('[data-test="private-field-name"]').attributes('value')).toBe('是否合格');
|
});
|
|
it('文档列表刷新、定位和正式删除事件均连接到工作流', async () => {
|
const initialContext = context([activeBinding]);
|
initialContext.checkboxGroups = [{ groupCode: 'inspection_result', maxSelected: 2, minSelected: 1, version: 2 }];
|
const fixture = createFixture(true, initialContext);
|
vi.mocked(fixture.bridge.listControls).mockResolvedValue([{ Alias: '结果', InternalId: 'control', Tag: activeBinding.controlTag }]);
|
const wrapper = mountApp(fixture);
|
fixture.ready();
|
await vi.waitFor(() => expect(wrapper.get('[data-test="connection-status"]').text()).toBe('已连接'));
|
await openDocumentTask(wrapper);
|
expect(wrapper.find('[data-test="control-item"]').exists()).toBe(true);
|
wrapper.findComponent(DocumentFieldList).vm.$emit('select', 'control');
|
await vi.waitFor(() => expect(fixture.bridge.selectControl).toHaveBeenCalledWith('control', expect.any(AbortSignal)));
|
wrapper.findComponent(DocumentFieldList).vm.$emit(
|
'change-checkbox-group',
|
'binding',
|
{ groupCode: 'inspection_result', maxSelected: 2, minSelected: 1 },
|
1,
|
);
|
await vi.waitFor(() =>
|
expect(fixture.client.changeCheckboxGroup).toHaveBeenCalledWith(expect.anything(), 'binding', {
|
checkboxGroupCode: 'inspection_result',
|
checkboxGroupMaxSelected: 2,
|
checkboxGroupMinSelected: 1,
|
expectedVersion: 1,
|
operationCode: expect.any(String),
|
}),
|
);
|
await vi.waitFor(() =>
|
expect((wrapper.findComponent(DocumentFieldList).props('bindings') ?? [])[0]!).toMatchObject({
|
checkboxGroupCode: 'inspection_result',
|
version: 2,
|
}),
|
);
|
wrapper.findComponent(DocumentFieldList).vm.$emit('change-checkbox-group-rule', 'inspection_result', 0, null, 2);
|
await vi.waitFor(() =>
|
expect(fixture.client.changeCheckboxGroupRule).toHaveBeenCalledWith(expect.anything(), 'inspection_result', {
|
expectedVersion: 2,
|
maxSelected: null,
|
minSelected: 0,
|
operationCode: expect.any(String),
|
}),
|
);
|
await vi.waitFor(() =>
|
expect(wrapper.findComponent(DocumentFieldList).props('checkboxGroups')).toContainEqual({
|
groupCode: 'inspection_result',
|
maxSelected: null,
|
minSelected: 0,
|
version: 3,
|
}),
|
);
|
wrapper.findComponent(DocumentFieldList).vm.$emit('refresh');
|
await vi.waitFor(() => expect(fixture.client.fetchAnnotationContext).toHaveBeenCalledTimes(2));
|
vi.mocked(fixture.bridge.listControls).mockResolvedValue([]);
|
wrapper.findComponent(DocumentFieldList).vm.$emit('remove-binding', 'binding', 'control');
|
await vi.waitFor(() => expect(fixture.client.confirmBindingRemoval).toHaveBeenCalledOnce());
|
});
|
|
it('初始化错误显示 fatal,事件处理不产生未处理拒绝', async () => {
|
const fixture = createFixture();
|
vi.mocked(fixture.client.fetchAnnotationContext).mockRejectedValue(new Error('context 失败'));
|
const wrapper = mountApp(fixture);
|
fixture.ready();
|
await vi.waitFor(() => expect(wrapper.get('[data-test="connection-status"]').text()).toBe('连接异常'));
|
expect(wrapper.text()).toContain('读取模板标注失败:context 失败');
|
expect(wrapper.find('[data-test="task-error"]').exists()).toBe(false);
|
await wrapper.get('[data-test="retry-initialization"]').trigger('click');
|
await vi.waitFor(() => expect(fixture.client.fetchAnnotationContext).toHaveBeenCalledTimes(2));
|
});
|
|
it('卸载时清理 lifecycle', () => {
|
const fixture = createFixture();
|
const wrapper = mountApp(fixture);
|
wrapper.unmount();
|
expect(fixture.cleanup).toHaveBeenCalledOnce();
|
});
|
|
it('sDK 缺失时显示 fatal 并禁用操作', async () => {
|
const wrapper = mountApp(null);
|
expect(wrapper.text()).toContain('ONLYOFFICE SDK 未加载,无法初始化插件');
|
expect(wrapper.get('[data-test="submit-field"]').attributes('disabled')).toBeDefined();
|
await openDocumentTask(wrapper);
|
expect(wrapper.get('[data-test="refresh-controls"]').attributes('disabled')).toBeDefined();
|
});
|
});
|