mirror of
https://github.com/The-Low-Code-Foundation/OpenNoodl.git
synced 2026-01-12 07:12:54 +01:00
new code editor
This commit is contained in:
387
packages/noodl-editor/tests/utils/ParameterValueResolver.test.ts
Normal file
387
packages/noodl-editor/tests/utils/ParameterValueResolver.test.ts
Normal file
@@ -0,0 +1,387 @@
|
||||
/**
|
||||
* Unit tests for ParameterValueResolver
|
||||
*
|
||||
* Tests the resolution of parameter values from storage (primitives or expression objects)
|
||||
* to display/runtime values based on context.
|
||||
*
|
||||
* @module noodl-editor/tests/utils
|
||||
*/
|
||||
|
||||
import { describe, it, expect } from '@jest/globals';
|
||||
|
||||
import { createExpressionParameter, ExpressionParameter } from '../../src/editor/src/models/ExpressionParameter';
|
||||
import { ParameterValueResolver, ValueContext } from '../../src/editor/src/utils/ParameterValueResolver';
|
||||
|
||||
describe('ParameterValueResolver', () => {
|
||||
describe('resolve()', () => {
|
||||
describe('with primitive values', () => {
|
||||
it('should return string values as-is', () => {
|
||||
expect(ParameterValueResolver.resolve('hello', ValueContext.Display)).toBe('hello');
|
||||
expect(ParameterValueResolver.resolve('', ValueContext.Display)).toBe('');
|
||||
expect(ParameterValueResolver.resolve('123', ValueContext.Display)).toBe('123');
|
||||
});
|
||||
|
||||
it('should return number values as-is', () => {
|
||||
expect(ParameterValueResolver.resolve(42, ValueContext.Display)).toBe(42);
|
||||
expect(ParameterValueResolver.resolve(0, ValueContext.Display)).toBe(0);
|
||||
expect(ParameterValueResolver.resolve(-42.5, ValueContext.Display)).toBe(-42.5);
|
||||
});
|
||||
|
||||
it('should return boolean values as-is', () => {
|
||||
expect(ParameterValueResolver.resolve(true, ValueContext.Display)).toBe(true);
|
||||
expect(ParameterValueResolver.resolve(false, ValueContext.Display)).toBe(false);
|
||||
});
|
||||
|
||||
it('should return undefined as-is', () => {
|
||||
expect(ParameterValueResolver.resolve(undefined, ValueContext.Display)).toBe(undefined);
|
||||
});
|
||||
|
||||
it('should handle null', () => {
|
||||
expect(ParameterValueResolver.resolve(null, ValueContext.Display)).toBe(null);
|
||||
});
|
||||
});
|
||||
|
||||
describe('with expression parameters', () => {
|
||||
it('should extract fallback from expression parameter in Display context', () => {
|
||||
const exprParam = createExpressionParameter('Variables.x', 'default', 1);
|
||||
expect(ParameterValueResolver.resolve(exprParam, ValueContext.Display)).toBe('default');
|
||||
});
|
||||
|
||||
it('should extract fallback from expression parameter in Runtime context', () => {
|
||||
const exprParam = createExpressionParameter('Variables.x', 'default', 1);
|
||||
expect(ParameterValueResolver.resolve(exprParam, ValueContext.Runtime)).toBe('default');
|
||||
});
|
||||
|
||||
it('should return full object in Serialization context', () => {
|
||||
const exprParam = createExpressionParameter('Variables.x', 'default', 1);
|
||||
const result = ParameterValueResolver.resolve(exprParam, ValueContext.Serialization);
|
||||
expect(result).toBe(exprParam);
|
||||
expect((result as ExpressionParameter).mode).toBe('expression');
|
||||
});
|
||||
|
||||
it('should handle expression parameter with undefined fallback', () => {
|
||||
const exprParam = createExpressionParameter('Variables.x', undefined, 1);
|
||||
expect(ParameterValueResolver.resolve(exprParam, ValueContext.Display)).toBe('');
|
||||
});
|
||||
|
||||
it('should handle expression parameter with numeric fallback', () => {
|
||||
const exprParam = createExpressionParameter('Variables.count', 42, 1);
|
||||
expect(ParameterValueResolver.resolve(exprParam, ValueContext.Display)).toBe(42);
|
||||
});
|
||||
|
||||
it('should handle expression parameter with boolean fallback', () => {
|
||||
const exprParam = createExpressionParameter('Variables.flag', true, 1);
|
||||
expect(ParameterValueResolver.resolve(exprParam, ValueContext.Display)).toBe(true);
|
||||
});
|
||||
|
||||
it('should handle expression parameter with empty string fallback', () => {
|
||||
const exprParam = createExpressionParameter('Variables.x', '', 1);
|
||||
expect(ParameterValueResolver.resolve(exprParam, ValueContext.Display)).toBe('');
|
||||
});
|
||||
});
|
||||
|
||||
describe('edge cases', () => {
|
||||
it('should handle objects that are not expression parameters', () => {
|
||||
const regularObj = { foo: 'bar' };
|
||||
// Should return as-is since it's not an expression parameter
|
||||
expect(ParameterValueResolver.resolve(regularObj, ValueContext.Display)).toBe(regularObj);
|
||||
});
|
||||
|
||||
it('should default to fallback for unknown context', () => {
|
||||
const exprParam = createExpressionParameter('Variables.x', 'default', 1);
|
||||
// Cast to any to test invalid context
|
||||
expect(ParameterValueResolver.resolve(exprParam, 'invalid' as any)).toBe('default');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('toString()', () => {
|
||||
describe('with primitive values', () => {
|
||||
it('should convert string to string', () => {
|
||||
expect(ParameterValueResolver.toString('hello')).toBe('hello');
|
||||
expect(ParameterValueResolver.toString('')).toBe('');
|
||||
});
|
||||
|
||||
it('should convert number to string', () => {
|
||||
expect(ParameterValueResolver.toString(42)).toBe('42');
|
||||
expect(ParameterValueResolver.toString(0)).toBe('0');
|
||||
expect(ParameterValueResolver.toString(-42.5)).toBe('-42.5');
|
||||
});
|
||||
|
||||
it('should convert boolean to string', () => {
|
||||
expect(ParameterValueResolver.toString(true)).toBe('true');
|
||||
expect(ParameterValueResolver.toString(false)).toBe('false');
|
||||
});
|
||||
|
||||
it('should convert undefined to empty string', () => {
|
||||
expect(ParameterValueResolver.toString(undefined)).toBe('');
|
||||
});
|
||||
|
||||
it('should convert null to empty string', () => {
|
||||
expect(ParameterValueResolver.toString(null)).toBe('');
|
||||
});
|
||||
});
|
||||
|
||||
describe('with expression parameters', () => {
|
||||
it('should extract fallback as string from expression parameter', () => {
|
||||
const exprParam = createExpressionParameter('Variables.x', 'test', 1);
|
||||
expect(ParameterValueResolver.toString(exprParam)).toBe('test');
|
||||
});
|
||||
|
||||
it('should convert numeric fallback to string', () => {
|
||||
const exprParam = createExpressionParameter('Variables.count', 42, 1);
|
||||
expect(ParameterValueResolver.toString(exprParam)).toBe('42');
|
||||
});
|
||||
|
||||
it('should convert boolean fallback to string', () => {
|
||||
const exprParam = createExpressionParameter('Variables.flag', true, 1);
|
||||
expect(ParameterValueResolver.toString(exprParam)).toBe('true');
|
||||
});
|
||||
|
||||
it('should handle expression parameter with undefined fallback', () => {
|
||||
const exprParam = createExpressionParameter('Variables.x', undefined, 1);
|
||||
expect(ParameterValueResolver.toString(exprParam)).toBe('');
|
||||
});
|
||||
|
||||
it('should handle expression parameter with null fallback', () => {
|
||||
const exprParam = createExpressionParameter('Variables.x', null, 1);
|
||||
expect(ParameterValueResolver.toString(exprParam)).toBe('');
|
||||
});
|
||||
});
|
||||
|
||||
describe('edge cases', () => {
|
||||
it('should handle objects that are not expression parameters', () => {
|
||||
const regularObj = { foo: 'bar' };
|
||||
// Should return empty string for safety (defensive behavior)
|
||||
expect(ParameterValueResolver.toString(regularObj)).toBe('');
|
||||
});
|
||||
|
||||
it('should handle arrays', () => {
|
||||
expect(ParameterValueResolver.toString([1, 2, 3])).toBe('');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('toNumber()', () => {
|
||||
describe('with primitive values', () => {
|
||||
it('should return number as-is', () => {
|
||||
expect(ParameterValueResolver.toNumber(42)).toBe(42);
|
||||
expect(ParameterValueResolver.toNumber(0)).toBe(0);
|
||||
expect(ParameterValueResolver.toNumber(-42.5)).toBe(-42.5);
|
||||
});
|
||||
|
||||
it('should convert numeric string to number', () => {
|
||||
expect(ParameterValueResolver.toNumber('42')).toBe(42);
|
||||
expect(ParameterValueResolver.toNumber('0')).toBe(0);
|
||||
expect(ParameterValueResolver.toNumber('-42.5')).toBe(-42.5);
|
||||
});
|
||||
|
||||
it('should return undefined for non-numeric string', () => {
|
||||
expect(ParameterValueResolver.toNumber('hello')).toBe(undefined);
|
||||
expect(ParameterValueResolver.toNumber('not a number')).toBe(undefined);
|
||||
});
|
||||
|
||||
it('should return undefined for undefined', () => {
|
||||
expect(ParameterValueResolver.toNumber(undefined)).toBe(undefined);
|
||||
});
|
||||
|
||||
it('should return undefined for null', () => {
|
||||
expect(ParameterValueResolver.toNumber(null)).toBe(undefined);
|
||||
});
|
||||
|
||||
it('should convert boolean to number', () => {
|
||||
expect(ParameterValueResolver.toNumber(true)).toBe(1);
|
||||
expect(ParameterValueResolver.toNumber(false)).toBe(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('with expression parameters', () => {
|
||||
it('should extract numeric fallback from expression parameter', () => {
|
||||
const exprParam = createExpressionParameter('Variables.count', 42, 1);
|
||||
expect(ParameterValueResolver.toNumber(exprParam)).toBe(42);
|
||||
});
|
||||
|
||||
it('should convert string fallback to number', () => {
|
||||
const exprParam = createExpressionParameter('Variables.count', '42', 1);
|
||||
expect(ParameterValueResolver.toNumber(exprParam)).toBe(42);
|
||||
});
|
||||
|
||||
it('should return undefined for non-numeric fallback', () => {
|
||||
const exprParam = createExpressionParameter('Variables.text', 'hello', 1);
|
||||
expect(ParameterValueResolver.toNumber(exprParam)).toBe(undefined);
|
||||
});
|
||||
|
||||
it('should handle expression parameter with undefined fallback', () => {
|
||||
const exprParam = createExpressionParameter('Variables.x', undefined, 1);
|
||||
expect(ParameterValueResolver.toNumber(exprParam)).toBe(undefined);
|
||||
});
|
||||
|
||||
it('should handle expression parameter with null fallback', () => {
|
||||
const exprParam = createExpressionParameter('Variables.x', null, 1);
|
||||
expect(ParameterValueResolver.toNumber(exprParam)).toBe(undefined);
|
||||
});
|
||||
});
|
||||
|
||||
describe('edge cases', () => {
|
||||
it('should handle objects that are not expression parameters', () => {
|
||||
const regularObj = { foo: 'bar' };
|
||||
expect(ParameterValueResolver.toNumber(regularObj)).toBe(undefined);
|
||||
});
|
||||
|
||||
it('should handle arrays', () => {
|
||||
expect(ParameterValueResolver.toNumber([1, 2, 3])).toBe(undefined);
|
||||
});
|
||||
|
||||
it('should handle empty string', () => {
|
||||
expect(ParameterValueResolver.toNumber('')).toBe(0); // Empty string converts to 0
|
||||
});
|
||||
|
||||
it('should handle whitespace string', () => {
|
||||
expect(ParameterValueResolver.toNumber(' ')).toBe(0); // Whitespace converts to 0
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('toBoolean()', () => {
|
||||
describe('with primitive values', () => {
|
||||
it('should return boolean as-is', () => {
|
||||
expect(ParameterValueResolver.toBoolean(true)).toBe(true);
|
||||
expect(ParameterValueResolver.toBoolean(false)).toBe(false);
|
||||
});
|
||||
|
||||
it('should convert truthy strings to true', () => {
|
||||
expect(ParameterValueResolver.toBoolean('hello')).toBe(true);
|
||||
expect(ParameterValueResolver.toBoolean('0')).toBe(true); // Non-empty string is truthy
|
||||
expect(ParameterValueResolver.toBoolean('false')).toBe(true); // Non-empty string is truthy
|
||||
});
|
||||
|
||||
it('should convert empty string to false', () => {
|
||||
expect(ParameterValueResolver.toBoolean('')).toBe(false);
|
||||
});
|
||||
|
||||
it('should convert numbers using truthiness', () => {
|
||||
expect(ParameterValueResolver.toBoolean(1)).toBe(true);
|
||||
expect(ParameterValueResolver.toBoolean(42)).toBe(true);
|
||||
expect(ParameterValueResolver.toBoolean(0)).toBe(false);
|
||||
expect(ParameterValueResolver.toBoolean(-1)).toBe(true);
|
||||
});
|
||||
|
||||
it('should convert undefined to false', () => {
|
||||
expect(ParameterValueResolver.toBoolean(undefined)).toBe(false);
|
||||
});
|
||||
|
||||
it('should convert null to false', () => {
|
||||
expect(ParameterValueResolver.toBoolean(null)).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('with expression parameters', () => {
|
||||
it('should extract boolean fallback from expression parameter', () => {
|
||||
const exprParam = createExpressionParameter('Variables.flag', true, 1);
|
||||
expect(ParameterValueResolver.toBoolean(exprParam)).toBe(true);
|
||||
});
|
||||
|
||||
it('should convert string fallback to boolean', () => {
|
||||
const exprParamTruthy = createExpressionParameter('Variables.text', 'hello', 1);
|
||||
expect(ParameterValueResolver.toBoolean(exprParamTruthy)).toBe(true);
|
||||
|
||||
const exprParamFalsy = createExpressionParameter('Variables.text', '', 1);
|
||||
expect(ParameterValueResolver.toBoolean(exprParamFalsy)).toBe(false);
|
||||
});
|
||||
|
||||
it('should convert numeric fallback to boolean', () => {
|
||||
const exprParamTruthy = createExpressionParameter('Variables.count', 42, 1);
|
||||
expect(ParameterValueResolver.toBoolean(exprParamTruthy)).toBe(true);
|
||||
|
||||
const exprParamFalsy = createExpressionParameter('Variables.count', 0, 1);
|
||||
expect(ParameterValueResolver.toBoolean(exprParamFalsy)).toBe(false);
|
||||
});
|
||||
|
||||
it('should handle expression parameter with undefined fallback', () => {
|
||||
const exprParam = createExpressionParameter('Variables.x', undefined, 1);
|
||||
expect(ParameterValueResolver.toBoolean(exprParam)).toBe(false);
|
||||
});
|
||||
|
||||
it('should handle expression parameter with null fallback', () => {
|
||||
const exprParam = createExpressionParameter('Variables.x', null, 1);
|
||||
expect(ParameterValueResolver.toBoolean(exprParam)).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('edge cases', () => {
|
||||
it('should handle objects that are not expression parameters', () => {
|
||||
const regularObj = { foo: 'bar' };
|
||||
// Non-expression objects should return false (defensive behavior)
|
||||
expect(ParameterValueResolver.toBoolean(regularObj)).toBe(false);
|
||||
});
|
||||
|
||||
it('should handle arrays', () => {
|
||||
expect(ParameterValueResolver.toBoolean([1, 2, 3])).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('isExpression()', () => {
|
||||
it('should return true for expression parameters', () => {
|
||||
const exprParam = createExpressionParameter('Variables.x', 'default', 1);
|
||||
expect(ParameterValueResolver.isExpression(exprParam)).toBe(true);
|
||||
});
|
||||
|
||||
it('should return false for primitive values', () => {
|
||||
expect(ParameterValueResolver.isExpression('hello')).toBe(false);
|
||||
expect(ParameterValueResolver.isExpression(42)).toBe(false);
|
||||
expect(ParameterValueResolver.isExpression(true)).toBe(false);
|
||||
expect(ParameterValueResolver.isExpression(undefined)).toBe(false);
|
||||
expect(ParameterValueResolver.isExpression(null)).toBe(false);
|
||||
});
|
||||
|
||||
it('should return false for regular objects', () => {
|
||||
const regularObj = { foo: 'bar' };
|
||||
expect(ParameterValueResolver.isExpression(regularObj)).toBe(false);
|
||||
});
|
||||
|
||||
it('should return false for arrays', () => {
|
||||
expect(ParameterValueResolver.isExpression([1, 2, 3])).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('integration scenarios', () => {
|
||||
it('should handle converting expression parameter through all type conversions', () => {
|
||||
const exprParam = createExpressionParameter('Variables.count', 42, 1);
|
||||
|
||||
expect(ParameterValueResolver.toString(exprParam)).toBe('42');
|
||||
expect(ParameterValueResolver.toNumber(exprParam)).toBe(42);
|
||||
expect(ParameterValueResolver.toBoolean(exprParam)).toBe(true);
|
||||
expect(ParameterValueResolver.isExpression(exprParam)).toBe(true);
|
||||
});
|
||||
|
||||
it('should handle canvas rendering scenario (text.split prevention)', () => {
|
||||
// This is the actual bug we're fixing - canvas tries to call .split() on a parameter
|
||||
const exprParam = createExpressionParameter('Variables.text', 'Hello\nWorld', 1);
|
||||
|
||||
// Before fix: this would return the object, causing text.split() to crash
|
||||
// After fix: this returns a string that can be safely split
|
||||
const text = ParameterValueResolver.toString(exprParam);
|
||||
expect(typeof text).toBe('string');
|
||||
expect(() => text.split('\n')).not.toThrow();
|
||||
expect(text.split('\n')).toEqual(['Hello', 'World']);
|
||||
});
|
||||
|
||||
it('should handle property panel display scenario', () => {
|
||||
// Property panel needs to show fallback value while user edits expression
|
||||
const exprParam = createExpressionParameter('2 + 2', '4', 1);
|
||||
|
||||
const displayValue = ParameterValueResolver.resolve(exprParam, ValueContext.Display);
|
||||
expect(displayValue).toBe('4');
|
||||
});
|
||||
|
||||
it('should handle serialization scenario', () => {
|
||||
// When saving, we need the full object preserved
|
||||
const exprParam = createExpressionParameter('Variables.x', 'default', 1);
|
||||
|
||||
const serialized = ParameterValueResolver.resolve(exprParam, ValueContext.Serialization);
|
||||
expect(serialized).toBe(exprParam);
|
||||
expect((serialized as ExpressionParameter).expression).toBe('Variables.x');
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1 +1,2 @@
|
||||
export * from './ParameterValueResolver.test';
|
||||
export * from './verify-json.spec';
|
||||
|
||||
Reference in New Issue
Block a user