rhizome-node/__tests__/unit/core/json-ast.test.ts
Lentil Hoffman 5afd3232cb
feat: enable inter-plugin state sharing in CustomResolver
- 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.
2025-06-22 20:42:05 -05:00

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);
});
});