- Update ResolverPlugin interface to include context in update and resolve methods - Modify CustomResolver to pass entity state and resolved values to plugins - Update built-in plugins to accept and use the new context parameter - Add comprehensive test for inter-plugin communication - Add documentation for the new view composition patterns This change enables plugins to access each other's states during both update and resolve phases, allowing for more powerful and flexible resolver compositions.
173 lines
4.2 KiB
TypeScript
173 lines
4.2 KiB
TypeScript
import { jsonToAst } from '../../../src/utils/json-ast';
|
|
import { JsonNode } from '../../../src/utils/json-ast/types';
|
|
|
|
describe('jsonToAst', () => {
|
|
it('should handle primitive values', () => {
|
|
expect(jsonToAst(42)).toMatchObject({
|
|
type: 'number',
|
|
value: 42
|
|
});
|
|
|
|
expect(jsonToAst('test')).toMatchObject({
|
|
type: 'string',
|
|
value: 'test'
|
|
});
|
|
|
|
expect(jsonToAst(true)).toMatchObject({
|
|
type: 'boolean',
|
|
value: true
|
|
});
|
|
|
|
expect(jsonToAst(null)).toMatchObject({
|
|
type: 'null',
|
|
value: null
|
|
});
|
|
});
|
|
|
|
it('should handle empty objects and arrays', () => {
|
|
const emptyObj = jsonToAst({});
|
|
expect(emptyObj).toMatchObject({
|
|
type: 'object',
|
|
children: []
|
|
});
|
|
|
|
const emptyArray = jsonToAst([]);
|
|
expect(emptyArray).toMatchObject({
|
|
type: 'array',
|
|
children: []
|
|
});
|
|
});
|
|
|
|
it('should handle nested objects', () => {
|
|
const ast = jsonToAst({
|
|
name: 'test',
|
|
nested: { value: 42 }
|
|
});
|
|
|
|
expect(ast.type).toBe('object');
|
|
expect(ast.children).toHaveLength(2);
|
|
|
|
const nameNode = ast.children?.[0];
|
|
const nestedNode = ast.children?.[1];
|
|
|
|
expect(nameNode).toMatchObject({
|
|
type: 'string',
|
|
key: 'name',
|
|
value: 'test'
|
|
});
|
|
|
|
expect(nestedNode).toMatchObject({
|
|
type: 'object',
|
|
key: 'nested'
|
|
});
|
|
|
|
expect(nestedNode?.children?.[0]).toMatchObject({
|
|
type: 'number',
|
|
key: 'value',
|
|
value: 42
|
|
});
|
|
});
|
|
|
|
it('should handle arrays', () => {
|
|
const ast = jsonToAst([1, 'two', true]);
|
|
|
|
expect(ast.type).toBe('array');
|
|
expect(ast.children).toHaveLength(3);
|
|
|
|
expect(ast.children?.[0]).toMatchObject({
|
|
type: 'number',
|
|
value: 1
|
|
});
|
|
|
|
expect(ast.children?.[1]).toMatchObject({
|
|
type: 'string',
|
|
value: 'two'
|
|
});
|
|
|
|
expect(ast.children?.[2]).toMatchObject({
|
|
type: 'boolean',
|
|
value: true
|
|
});
|
|
});
|
|
|
|
it('should include paths when includePath is true', () => {
|
|
const ast = jsonToAst({
|
|
user: {
|
|
name: 'test',
|
|
roles: ['admin', 'user']
|
|
}
|
|
}, { includePath: true });
|
|
|
|
const findNode = (node: JsonNode, key: string): JsonNode | undefined => {
|
|
if (node.key === key) return node;
|
|
if (!node.children) return undefined;
|
|
for (const child of node.children) {
|
|
const found = findNode(child, key);
|
|
if (found) return found;
|
|
}
|
|
return undefined;
|
|
};
|
|
|
|
const nameNode = findNode(ast, 'name');
|
|
const rolesNode = findNode(ast, 'roles');
|
|
|
|
expect(nameNode?.path).toBe('user.name');
|
|
expect(rolesNode?.path).toBe('user.roles');
|
|
expect(rolesNode?.children?.[0].path).toBe('user.roles[0]');
|
|
});
|
|
|
|
it('should respect maxDepth option', () => {
|
|
const deepObject = {
|
|
level1: {
|
|
level2: {
|
|
level3: {
|
|
value: 'too deep'
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
const ast = jsonToAst(deepObject, {
|
|
maxDepth: 2,
|
|
includePath: true
|
|
});
|
|
|
|
const level2 = ast.children?.[0].children?.[0];
|
|
expect(level2?.type).toBe('object');
|
|
// The implementation sets value to undefined when max depth is exceeded
|
|
expect(level2?.value).toBeUndefined();
|
|
expect(level2?.path).toBe('level1.level2');
|
|
});
|
|
|
|
it('should apply filter function when provided', () => {
|
|
const data = {
|
|
name: 'test',
|
|
age: 42,
|
|
active: true,
|
|
address: {
|
|
street: '123 Main St',
|
|
city: 'Anytown'
|
|
}
|
|
};
|
|
|
|
// Only include string and number values
|
|
const ast = jsonToAst(data, {
|
|
filter: (node: JsonNode) =>
|
|
node.type === 'string' ||
|
|
node.type === 'number' ||
|
|
node.type === 'object' // Keep objects to maintain structure
|
|
});
|
|
|
|
// Should have filtered out the boolean 'active' field
|
|
expect(ast.children).toHaveLength(3);
|
|
expect(ast.children?.some((c: any) => c.key === 'active')).toBe(false);
|
|
|
|
// Nested object should only have string properties
|
|
const addressNode = ast.children?.find((c: any) => c.key === 'address');
|
|
expect(addressNode?.children).toHaveLength(2);
|
|
expect(addressNode?.children?.every((c: any) =>
|
|
c.type === 'string' || c.key === 'city' || c.key === 'street'
|
|
)).toBe(true);
|
|
});
|
|
});
|