2026-05-14 15:26:22 +08:00

162 lines
6.0 KiB
TypeScript

/*
* Tencent is pleased to support the open source community by making TMagicEditor available.
*
* Copyright (C) 2025 Tencent.
*/
import { beforeEach, describe, expect, test, vi } from 'vitest';
import { defineComponent, h, nextTick } from 'vue';
import { mount } from '@vue/test-utils';
import Editor from '@editor/Editor.vue';
const { initServiceEventsMock, initServiceStateMock } = vi.hoisted(() => ({
initServiceEventsMock: vi.fn(),
initServiceStateMock: vi.fn(),
}));
vi.mock('@editor/initService', () => ({
initServiceEvents: initServiceEventsMock,
initServiceState: initServiceStateMock,
}));
vi.mock('@editor/services/codeBlock', () => ({ default: {} }));
vi.mock('@editor/services/componentList', () => ({ default: {} }));
vi.mock('@editor/services/dataSource', () => ({ default: {} }));
vi.mock('@editor/services/dep', () => ({ default: {} }));
vi.mock('@editor/services/editor', () => ({ default: {} }));
vi.mock('@editor/services/events', () => ({ default: {} }));
vi.mock('@editor/services/history', () => ({ default: {} }));
vi.mock('@editor/services/keybinding', () => ({
default: { register: vi.fn(), registerEl: vi.fn() },
}));
vi.mock('@editor/services/props', () => ({ default: {} }));
vi.mock('@editor/services/stageOverlay', () => ({
default: { set: vi.fn() },
}));
vi.mock('@editor/services/storage', () => ({ default: {}, Protocol: {} }));
vi.mock('@editor/services/ui', () => ({ default: {} }));
vi.mock('@editor/utils/keybinding-config', () => ({ default: {}, KeyBindingContainerKey: { STAGE: 'stage' } }));
vi.mock('@editor/layouts/Framework.vue', () => ({
default: defineComponent({
name: 'FakeFramework',
props: ['disabledPageFragment', 'pageBarSortOptions', 'pageFilterFunction'],
setup(_p, { slots }) {
return () =>
h('div', { class: 'fake-framework' }, [
slots.header?.(),
slots.nav?.({ editorService: {} }),
slots.sidebar?.({ editorService: {} }),
slots.workspace?.({ editorService: {} }),
slots['props-panel']?.(),
slots.footer?.(),
]);
},
}),
}));
vi.mock('@editor/layouts/NavMenu.vue', () => ({
default: defineComponent({
name: 'TMagicNavMenu',
props: ['data'],
setup() {
return () => h('div', { class: 'fake-nav-menu' });
},
}),
}));
vi.mock('@editor/layouts/sidebar/Sidebar.vue', () => ({
default: defineComponent({
name: 'FakeSidebar',
emits: ['layer-node-dblclick'],
setup(_p, { emit }) {
return () =>
h('div', {
class: 'fake-sidebar',
onClick: () => emit('layer-node-dblclick', new MouseEvent('dblclick'), { id: 'a' }),
});
},
}),
}));
vi.mock('@editor/layouts/workspace/Workspace.vue', () => ({
default: defineComponent({ name: 'FakeWorkspace', setup: () => () => h('div', { class: 'fake-workspace' }) }),
}));
vi.mock('@editor/layouts/props-panel/PropsPanel.vue', () => ({
default: defineComponent({
name: 'PropsPanel',
emits: ['mounted', 'unmounted', 'submit-error', 'form-error'],
setup(_p, { emit }) {
return () =>
h('div', { class: 'fake-props-panel' }, [
h('button', { class: 'mounted-btn', onClick: () => emit('mounted', { proxy: true }) }),
h('button', { class: 'unmounted-btn', onClick: () => emit('unmounted') }),
h('button', { class: 'submit-err', onClick: () => emit('submit-error', new Error('e')) }),
h('button', { class: 'form-err', onClick: () => emit('form-error', new Error('e')) }),
]);
},
}),
}));
vi.mock('@editor/layouts/props-panel/FormPanel.vue', () => ({
default: defineComponent({ name: 'FormPanel', setup: () => () => h('div') }),
}));
beforeEach(() => {
vi.clearAllMocks();
});
describe('Editor', () => {
test('挂载时初始化 services', () => {
mount(Editor, { props: {} as any });
expect(initServiceEventsMock).toHaveBeenCalled();
expect(initServiceStateMock).toHaveBeenCalled();
});
test('canDropIn 转发到 stage 含 stage-add/stage-drag 类型', async () => {
const canDropIn = vi.fn(() => true);
const stageOverlayMod = (await import('@editor/services/stageOverlay')) as any;
mount(Editor, { props: { canDropIn } as any });
await nextTick();
const stageOptions = stageOverlayMod.default.set.mock.calls.find((c: any[]) => c[0] === 'stageOptions')?.[1];
expect(stageOptions.canDropIn).toBeDefined();
stageOptions.canDropIn([], 't1');
expect(canDropIn).toHaveBeenCalledWith([], 't1', 'stage-add');
stageOptions.canDropIn(['s1'], 't1');
expect(canDropIn).toHaveBeenLastCalledWith(['s1'], 't1', 'stage-drag');
});
test('未传 canDropIn 时 stageOptions.canDropIn 为 undefined', async () => {
const stageOverlayMod = (await import('@editor/services/stageOverlay')) as any;
mount(Editor, { props: {} as any });
await nextTick();
const stageOptions = stageOverlayMod.default.set.mock.calls.find((c: any[]) => c[0] === 'stageOptions')?.[1];
expect(stageOptions.canDropIn).toBeUndefined();
});
test('PropsPanel 事件转发', async () => {
const wrapper = mount(Editor, { props: {} as any });
await wrapper.find('.mounted-btn').trigger('click');
expect(wrapper.emitted('props-panel-mounted')).toBeTruthy();
await wrapper.find('.unmounted-btn').trigger('click');
expect(wrapper.emitted('props-panel-unmounted')).toBeTruthy();
await wrapper.find('.submit-err').trigger('click');
expect(wrapper.emitted('props-submit-error')).toBeTruthy();
await wrapper.find('.form-err').trigger('click');
expect(wrapper.emitted('props-form-error')).toBeTruthy();
});
test('Sidebar layer-node-dblclick 事件转发', async () => {
const wrapper = mount(Editor, { props: {} as any });
await wrapper.find('.fake-sidebar').trigger('click');
expect(wrapper.emitted('layer-node-dblclick')).toBeTruthy();
});
test('expose services', () => {
const wrapper = mount(Editor, { props: {} as any });
expect((wrapper.vm as any).editorService).toBeDefined();
expect((wrapper.vm as any).propsService).toBeDefined();
});
});