roymondchen 1c67b5e77b feat(editor): 注册编辑器字段内置 typeMatch 校验规则并补充文档
- 新增 typeMatchRules.ts,为编辑器自定义字段注册内置 typeMatch 校验规则
- 抽取 event.ts 事件工具函数
- form typeMatch 适配 TDesign 校验器签名,错误消息补充实际值
- rules.md 新增「Editor 字段内置规则」章节,16 个字段文档补充校验说明
2026-07-14 17:15:28 +08:00

108 lines
4.4 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 {
collectEventNameOptionValues,
getCompActionAllowedValues,
getCompActionOptions,
getEventNameAllowedValues,
getEventNameOptions,
normalizeCompActionValue,
} from '@editor/utils/event';
const { editorService, dataSourceService, eventsService } = vi.hoisted(() => ({
editorService: {
get: vi.fn(),
getNodeById: vi.fn(),
},
dataSourceService: {
getDataSourceById: vi.fn(),
getFormEvent: vi.fn(() => []),
},
eventsService: {
getEvent: vi.fn(() => [{ label: 'click', value: 'click' }]),
getMethod: vi.fn(() => [{ label: 'open', value: 'open' }]),
},
}));
vi.mock('@editor/services/editor', () => ({ default: editorService }));
vi.mock('@editor/services/dataSource', () => ({ default: dataSourceService }));
vi.mock('@editor/services/events', () => ({ default: eventsService }));
vi.mock('@editor/utils/data-source', async () => {
const actual = await vi.importActual<any>('@editor/utils/data-source');
return { ...actual, getCascaderOptionsFromFields: vi.fn(() => [{ label: 'f1', value: 'f1' }]) };
});
describe('event utils', () => {
beforeEach(() => {
vi.clearAllMocks();
eventsService.getEvent.mockReturnValue([{ label: 'click', value: 'click' }]);
eventsService.getMethod.mockReturnValue([{ label: 'open', value: 'open' }]);
});
test('normalizeCompActionValue 支持字符串与数组', () => {
expect(normalizeCompActionValue('open')).toBe('open');
expect(normalizeCompActionValue(['c1', 'open'])).toBe('c1.open');
expect(normalizeCompActionValue('')).toBe('');
expect(normalizeCompActionValue(undefined)).toBe('');
});
test('getCompActionOptions 普通组件返回 select options', () => {
editorService.getNodeById.mockReturnValue({ type: 'btn', id: '1' });
expect(getCompActionOptions('1')).toEqual([{ text: 'open', value: 'open' }]);
});
test('getCompActionOptions 无节点返回空', () => {
editorService.getNodeById.mockReturnValue(null);
expect(getCompActionOptions('missing')).toEqual([]);
expect(getCompActionOptions(undefined)).toEqual([]);
});
test('getCompActionOptions 页面片容器返回 cascader options', () => {
editorService.getNodeById.mockReturnValue({ type: 'page-fragment-container', id: '1', pageFragmentId: 'pf1' });
editorService.get.mockReturnValue({ items: [{ id: 'pf1', items: [{ id: 'c1', type: 'btn', name: 'b' }] }] });
expect(getCompActionOptions('1')).toEqual([
{
label: 'b_c1',
value: 'c1',
children: [{ label: 'open', value: 'open' }],
},
]);
});
test('getCompActionAllowedValues 枚举合法动作', () => {
editorService.getNodeById.mockReturnValue({ type: 'btn', id: '1' });
expect(getCompActionAllowedValues({ src: 'component' }, { to: '1' })).toEqual(new Set(['open']));
});
test('getCompActionAllowedValues 自定义 options 返回 null', () => {
expect(
getCompActionAllowedValues({ src: 'component', compActionConfig: { options: () => [] } }, { to: '1' }),
).toBeNull();
});
test('getCompActionAllowedValues 页面片路径用 . 拼接', () => {
editorService.getNodeById.mockReturnValue({ type: 'page-fragment-container', id: '1', pageFragmentId: 'pf1' });
editorService.get.mockReturnValue({ items: [{ id: 'pf1', items: [{ id: 'c1', type: 'btn', name: 'b' }] }] });
expect(getCompActionAllowedValues({ src: 'component' }, { to: '1' })).toEqual(new Set(['c1.open']));
});
test('getEventNameAllowedValues / collectEventNameOptionValues', () => {
expect(getEventNameAllowedValues({ src: 'component' }, { type: 'btn' })).toEqual(new Set(['click']));
expect(
getEventNameAllowedValues({ src: 'component', eventNameConfig: { options: [] } }, { type: 'btn' }),
).toBeNull();
expect(collectEventNameOptionValues([{ value: 'a', children: [{ value: 'b' }] }], false)).toEqual(new Set(['a.b']));
});
test('getEventNameOptions datasource 追加数据变化', () => {
dataSourceService.getDataSourceById.mockReturnValue({ fields: [{ name: 'f1' }] });
const opts = getEventNameOptions('datasource', { type: 'ds', id: 'd1' });
expect(opts.some((item: any) => item.value === 'ds-field-changed' || item.label === '数据变化')).toBe(true);
});
});