Files
OpenNoodl/packages/noodl-editor/tests/models/ElementConfigRegistry.test.ts
2026-02-18 15:59:52 +01:00

208 lines
7.4 KiB
TypeScript

/**
* STYLE-004: Unit tests for ElementConfigRegistry
*
* Covers: applyVariant, applySize, getSizeNames, resolveVariant,
* getVariantNames, applyDefaults.
*/
import { describe, it, expect } from '@jest/globals';
import { ElementConfigRegistry, NodeModelLike } from '../../src/editor/src/models/ElementConfigs/ElementConfigRegistry';
// ---------------------------------------------------------------------------
// Helpers
// ---------------------------------------------------------------------------
function makeNode(): NodeModelLike {
return { parameters: {} };
}
const BUTTON_TYPE = 'net.noodl.controls.button';
const TEXT_TYPE = 'net.noodl.visual.text';
const GROUP_TYPE = 'net.noodl.visual.group';
// ---------------------------------------------------------------------------
// applyVariant
// ---------------------------------------------------------------------------
describe('ElementConfigRegistry.applyVariant', () => {
it('stamps base styles onto node parameters', () => {
const node = makeNode();
ElementConfigRegistry.applyVariant(node, BUTTON_TYPE, 'primary');
expect(node.parameters['backgroundColor']).toBe('var(--primary)');
expect(node.parameters['color']).toBe('var(--primary-foreground)');
expect(node.parameters['_variant']).toBe('primary');
});
it('does not include the "states" key in stamped parameters', () => {
const node = makeNode();
ElementConfigRegistry.applyVariant(node, BUTTON_TYPE, 'primary');
expect(node.parameters['states']).toBeUndefined();
});
it('updates _variant marker when switching variants', () => {
const node = makeNode();
ElementConfigRegistry.applyVariant(node, BUTTON_TYPE, 'primary');
ElementConfigRegistry.applyVariant(node, BUTTON_TYPE, 'secondary');
expect(node.parameters['_variant']).toBe('secondary');
expect(node.parameters['backgroundColor']).toBe('var(--secondary)');
});
it('is a no-op for an unknown node type', () => {
const node = makeNode();
ElementConfigRegistry.applyVariant(node, 'unknown.type', 'primary');
expect(node.parameters).toEqual({});
});
it('is a no-op for an unknown variant name', () => {
const node = makeNode();
ElementConfigRegistry.applyVariant(node, BUTTON_TYPE, 'nonexistent');
expect(node.parameters).toEqual({});
});
});
// ---------------------------------------------------------------------------
// applySize (STYLE-004)
// ---------------------------------------------------------------------------
describe('ElementConfigRegistry.applySize', () => {
it('stamps size preset styles onto node parameters', () => {
const node = makeNode();
ElementConfigRegistry.applySize(node, BUTTON_TYPE, 'sm');
expect(node.parameters['fontSize']).toBe('var(--text-xs)');
expect(node.parameters['_size']).toBe('sm');
});
it('stamps lg preset correctly', () => {
const node = makeNode();
ElementConfigRegistry.applySize(node, BUTTON_TYPE, 'lg');
expect(node.parameters['fontSize']).toBe('var(--text-base)');
expect(node.parameters['paddingTop']).toBe('var(--space-3)');
expect(node.parameters['_size']).toBe('lg');
});
it('is a no-op for a node type with no sizes', () => {
const node = makeNode();
// Group has no sizes defined
ElementConfigRegistry.applySize(node, GROUP_TYPE, 'sm');
expect(node.parameters).toEqual({});
});
it('is a no-op for an unknown size name', () => {
const node = makeNode();
ElementConfigRegistry.applySize(node, BUTTON_TYPE, 'xxl');
expect(node.parameters).toEqual({});
});
it('is a no-op for an unknown type', () => {
const node = makeNode();
ElementConfigRegistry.applySize(node, 'unknown.type', 'md');
expect(node.parameters).toEqual({});
});
});
// ---------------------------------------------------------------------------
// getSizeNames (STYLE-004)
// ---------------------------------------------------------------------------
describe('ElementConfigRegistry.getSizeNames', () => {
it('returns size keys in definition order for Button', () => {
const sizes = ElementConfigRegistry.getSizeNames(BUTTON_TYPE);
expect(sizes).toEqual(['sm', 'md', 'lg', 'xl']);
});
it('returns empty array for node types with no sizes', () => {
expect(ElementConfigRegistry.getSizeNames(GROUP_TYPE)).toEqual([]);
expect(ElementConfigRegistry.getSizeNames(TEXT_TYPE)).toEqual([]);
});
it('returns empty array for unknown type', () => {
expect(ElementConfigRegistry.getSizeNames('unknown.type')).toEqual([]);
});
});
// ---------------------------------------------------------------------------
// getVariantNames
// ---------------------------------------------------------------------------
describe('ElementConfigRegistry.getVariantNames', () => {
it('returns all variant names for Button', () => {
const variants = ElementConfigRegistry.getVariantNames(BUTTON_TYPE);
expect(variants).toContain('primary');
expect(variants).toContain('secondary');
expect(variants).toContain('outline');
expect(variants).toContain('ghost');
expect(variants).toContain('destructive');
expect(variants).toContain('link');
});
it('returns empty array for unknown type', () => {
expect(ElementConfigRegistry.getVariantNames('unknown.type')).toEqual([]);
});
});
// ---------------------------------------------------------------------------
// resolveVariant
// ---------------------------------------------------------------------------
describe('ElementConfigRegistry.resolveVariant', () => {
it('returns baseStyles and states', () => {
const resolved = ElementConfigRegistry.resolveVariant(BUTTON_TYPE, 'primary');
expect(resolved).toBeDefined();
expect(resolved!.baseStyles['backgroundColor']).toBe('var(--primary)');
expect(resolved!.states.hover).toBeDefined();
});
it('does not include "states" key inside baseStyles', () => {
const resolved = ElementConfigRegistry.resolveVariant(BUTTON_TYPE, 'primary');
expect('states' in resolved!.baseStyles).toBe(false);
});
it('returns undefined for unknown type', () => {
expect(ElementConfigRegistry.resolveVariant('unknown.type', 'primary')).toBeUndefined();
});
it('returns undefined for unknown variant', () => {
expect(ElementConfigRegistry.resolveVariant(BUTTON_TYPE, 'nonexistent')).toBeUndefined();
});
});
// ---------------------------------------------------------------------------
// applyDefaults
// ---------------------------------------------------------------------------
describe('ElementConfigRegistry.applyDefaults', () => {
it('applies default styles and stamps the default variant', () => {
const node = makeNode();
ElementConfigRegistry.applyDefaults(node, BUTTON_TYPE);
// Default variant is 'primary', so its styles should be applied
expect(node.parameters['_variant']).toBe('primary');
expect(node.parameters['backgroundColor']).toBe('var(--primary)');
// Base defaults
expect(node.parameters['borderRadius']).toBe('var(--radius-md)');
expect(node.parameters['cursor']).toBe('pointer');
});
it('does not overwrite already-set parameters', () => {
const node = makeNode();
node.parameters['cursor'] = 'default';
ElementConfigRegistry.applyDefaults(node, BUTTON_TYPE);
// Pre-existing value should be preserved
expect(node.parameters['cursor']).toBe('default');
});
it('is a no-op for unknown type', () => {
const node = makeNode();
ElementConfigRegistry.applyDefaults(node, 'unknown.type');
expect(node.parameters).toEqual({});
});
});