mirror of
https://github.com/Tencent/tmagic-editor.git
synced 2026-10-28 13:30:46 +00:00
- 新增 packages/editor/src/sdk,通过 sdk.ts 对外导出 - 将 services 下的核心服务迁移至 sdk/services - 工具函数拆分至 sdk/utils,校验逻辑迁移至 sdk/validate,新增 sdk/wiring - services/sdk-instance.ts 负责在编辑器中实例化 SDK - 补充 SDK 相关单元测试,新增 docs/api/editor/sdk.md 文档
174 lines
6.4 KiB
TypeScript
174 lines
6.4 KiB
TypeScript
// @vitest-environment node
|
|
import { describe, expect, test, vi } from 'vitest';
|
|
|
|
import { DepTargetType } from '@tmagic/core';
|
|
|
|
import { createDslSdk } from '@editor/sdk';
|
|
import {
|
|
addDataSourceTargets,
|
|
getDataSourceRecollectTypes,
|
|
removeDataSourceTargets,
|
|
removeTargetDeps,
|
|
resetDepTargets,
|
|
writeTargetDeps,
|
|
} from '@editor/sdk/utils/dep';
|
|
|
|
import { createDsl, createSdk } from './helpers';
|
|
|
|
const record = (propPath?: string) => ({ propPath, value: undefined }) as any;
|
|
|
|
describe('getDataSourceRecollectTypes', () => {
|
|
test('字段变化:重建数据源与条件依赖', () => {
|
|
for (const path of ['fields', 'fields.0.name', 'fields.1.defaultValue', 'fields.2']) {
|
|
expect(getDataSourceRecollectTypes([record(path)])).toEqual([
|
|
DepTargetType.DATA_SOURCE,
|
|
DepTargetType.DATA_SOURCE_COND,
|
|
]);
|
|
}
|
|
});
|
|
|
|
test('mocks 变化:只重建数据源依赖', () => {
|
|
expect(getDataSourceRecollectTypes([record('mocks')])).toEqual([DepTargetType.DATA_SOURCE]);
|
|
});
|
|
|
|
test('方法变化:只重建方法依赖', () => {
|
|
for (const path of ['methods', 'methods.0.name', 'methods.3']) {
|
|
expect(getDataSourceRecollectTypes([record(path)])).toEqual([DepTargetType.DATA_SOURCE_METHOD]);
|
|
}
|
|
});
|
|
|
|
test('取第一条命中规则的记录,忽略没有 propPath 的记录', () => {
|
|
expect(
|
|
getDataSourceRecollectTypes([record(undefined), record('title'), record('methods'), record('fields')]),
|
|
).toEqual([DepTargetType.DATA_SOURCE_METHOD]);
|
|
});
|
|
|
|
test('不影响依赖的变化返回 null', () => {
|
|
expect(getDataSourceRecollectTypes([record('title'), record(undefined)])).toBeNull();
|
|
expect(getDataSourceRecollectTypes([])).toBeNull();
|
|
});
|
|
});
|
|
|
|
describe('依赖目标与 root 写回', () => {
|
|
const ds = { id: 'ds_1', type: 'base', fields: [], methods: [] } as any;
|
|
|
|
test('addDataSourceTargets / removeDataSourceTargets 默认处理三类,可指定类型', () => {
|
|
const dep = { addTarget: vi.fn(), removeTarget: vi.fn() };
|
|
|
|
addDataSourceTargets(dep as any, ds, (v) => v);
|
|
expect(dep.addTarget.mock.calls.map(([target]) => target.type)).toEqual([
|
|
DepTargetType.DATA_SOURCE,
|
|
DepTargetType.DATA_SOURCE_COND,
|
|
DepTargetType.DATA_SOURCE_METHOD,
|
|
]);
|
|
|
|
dep.addTarget.mockClear();
|
|
addDataSourceTargets(dep as any, ds, (v) => v, [DepTargetType.DATA_SOURCE_METHOD]);
|
|
expect(dep.addTarget).toHaveBeenCalledTimes(1);
|
|
|
|
removeDataSourceTargets(dep as any, 'ds_1');
|
|
expect(dep.removeTarget).toHaveBeenCalledTimes(3);
|
|
removeDataSourceTargets(dep as any, 'ds_1', [DepTargetType.DATA_SOURCE]);
|
|
expect(dep.removeTarget).toHaveBeenLastCalledWith('ds_1', DepTargetType.DATA_SOURCE);
|
|
});
|
|
|
|
test('resetDepTargets 先清空再建代码块与数据源目标', () => {
|
|
const dep = { clearTargets: vi.fn(), addTarget: vi.fn() };
|
|
|
|
resetDepTargets(dep as any, { code_1: { name: 'c', content: '', params: [] } } as any, [ds], (v) => v);
|
|
|
|
expect(dep.clearTargets).toHaveBeenCalledTimes(1);
|
|
expect(dep.addTarget.mock.calls.map(([target]) => target.type)).toEqual([
|
|
DepTargetType.CODE_BLOCK,
|
|
DepTargetType.DATA_SOURCE,
|
|
DepTargetType.DATA_SOURCE_COND,
|
|
DepTargetType.DATA_SOURCE_METHOD,
|
|
]);
|
|
});
|
|
|
|
test('writeTargetDeps / removeTargetDeps 只处理数据源三类依赖', () => {
|
|
const root: any = {};
|
|
const deps = { n1: { name: 'n1', keys: ['text'] } };
|
|
|
|
writeTargetDeps(root, { id: 'ds_1', type: DepTargetType.DATA_SOURCE, deps } as any);
|
|
writeTargetDeps(root, { id: 'ds_1', type: DepTargetType.DATA_SOURCE_COND, deps } as any);
|
|
writeTargetDeps(root, { id: 'ds_1', type: DepTargetType.DATA_SOURCE_METHOD, deps } as any);
|
|
writeTargetDeps(root, { id: 'code_1', type: DepTargetType.CODE_BLOCK, deps } as any);
|
|
writeTargetDeps(null, { id: 'ds_1', type: DepTargetType.DATA_SOURCE, deps } as any);
|
|
|
|
expect(root.dataSourceDeps.ds_1).toBe(deps);
|
|
expect(root.dataSourceCondDeps.ds_1).toBe(deps);
|
|
expect(root.dataSourceMethodDeps.ds_1).toBe(deps);
|
|
expect(Object.keys(root)).toHaveLength(3);
|
|
|
|
removeTargetDeps(root, 'ds_1', DepTargetType.DATA_SOURCE);
|
|
removeTargetDeps(root, 'ds_1', DepTargetType.CODE_BLOCK);
|
|
removeTargetDeps(root, 'ds_1', 'unknown');
|
|
removeTargetDeps(undefined, 'ds_1', DepTargetType.DATA_SOURCE);
|
|
removeTargetDeps({} as any, 'ds_1', DepTargetType.DATA_SOURCE);
|
|
|
|
expect(root.dataSourceDeps.ds_1).toBeUndefined();
|
|
expect(root.dataSourceCondDeps.ds_1).toBe(deps);
|
|
});
|
|
});
|
|
|
|
describe('延迟绑定连线', () => {
|
|
test('autoWiring 为 false 时,之后调用 bindWiring 会补收集已有 DSL 的依赖', async () => {
|
|
const sdk = createSdk({ autoWiring: false });
|
|
await sdk.ready;
|
|
expect(sdk.getDsl()!.dataSourceDeps).toBeUndefined();
|
|
|
|
const unbind = sdk.bindWiring();
|
|
await sdk.ready;
|
|
|
|
expect(sdk.getDsl()!.dataSourceDeps!.ds_1.text_1).toBeDefined();
|
|
|
|
const node = (await sdk.editor.add({ type: 'text', text: '${ds_1.name}' } as any)) as any;
|
|
expect(sdk.getDsl()!.dataSourceDeps!.ds_1[node.id]).toBeDefined();
|
|
|
|
unbind();
|
|
});
|
|
|
|
test('重复 bindWiring 返回同一个解绑函数且不会重复绑定', async () => {
|
|
const sdk = createDslSdk({ dsl: createDsl(), autoWiring: false });
|
|
await sdk.ready;
|
|
|
|
const first = sdk.bindWiring();
|
|
const second = sdk.bindWiring();
|
|
expect(second).toBe(first);
|
|
|
|
first();
|
|
expect(sdk.editor.listenerCount('add')).toBe(0);
|
|
});
|
|
});
|
|
|
|
describe('登记表', () => {
|
|
test('setPropsConfigs / setPropsValues 返回的 Promise 在异步配置处理完后 resolve', async () => {
|
|
const sdk = createDslSdk({});
|
|
|
|
await sdk.props.setPropsConfigs({ Text: [{ name: 'text', text: '文本' }] });
|
|
await sdk.props.setPropsValues({ Text: { text: 'x' } });
|
|
|
|
expect(sdk.props.getPropsConfigs()).toHaveProperty('text');
|
|
expect(sdk.props.getPropsValues()).toHaveProperty('text', { text: 'x' });
|
|
});
|
|
|
|
test('setPropsConfigs 在配置全部写入后才 emit props-configs-change', async () => {
|
|
const sdk = createDslSdk({});
|
|
let hasTextAtEmit: boolean | undefined;
|
|
sdk.props.on('props-configs-change', () => {
|
|
hasTextAtEmit = sdk.props.hasPropsConfig('Text');
|
|
});
|
|
|
|
await sdk.props.setPropsConfigs({ Text: [{ name: 'text', text: '文本' }] });
|
|
|
|
expect(hasTextAtEmit).toBe(true);
|
|
});
|
|
|
|
test('没有传 eventMethodList 时 events 保持为空', () => {
|
|
const sdk = createDslSdk({});
|
|
expect(sdk.events.getEvent('text')).toEqual([]);
|
|
expect(sdk.events.getMethod('text')).toEqual([]);
|
|
});
|
|
});
|