From fef18242e568169b1a95f740111d4dab8b9a38bf Mon Sep 17 00:00:00 2001 From: Samuel Male Date: Fri, 6 Dec 2024 12:27:20 +0300 Subject: [PATCH] (fix) Correctly extract function names in expression evaluation exceptions (#1224) --- .../src/evaluator.test.ts | 33 +++++++++++++++++-- .../esm-expression-evaluator/src/evaluator.ts | 17 ++++++++-- 2 files changed, 44 insertions(+), 6 deletions(-) diff --git a/packages/framework/esm-expression-evaluator/src/evaluator.test.ts b/packages/framework/esm-expression-evaluator/src/evaluator.test.ts index 75c45653a..da0ec8e04 100644 --- a/packages/framework/esm-expression-evaluator/src/evaluator.test.ts +++ b/packages/framework/esm-expression-evaluator/src/evaluator.test.ts @@ -6,7 +6,7 @@ describe('OpenMRS Expression Evaluator', () => { expect(evaluate('1 + 1')).toBe(2); }); - it('Should support mulitplication', () => { + it('Should support multiplication', () => { expect(evaluate('1 * 2')).toBe(2); }); @@ -93,7 +93,7 @@ describe('OpenMRS Expression Evaluator', () => { expect(evaluate('a ? 1 : 2', { a: false })).toBe(2); }); - it('Should support hexdecimal', () => { + it('Should support hexadecimal', () => { expect(evaluate('0xff')).toBe(255); }); @@ -154,7 +154,7 @@ describe('OpenMRS Expression Evaluator', () => { expect(evaluate(exp)).toBe(2); }); - it('Should not support variable assignement', () => { + it('Should not support variable assignment', () => { expect(() => evaluate('var a = 1; a')).toThrow(); }); @@ -198,4 +198,31 @@ describe('OpenMRS Expression Evaluator', () => { ), ).toBe(true); }); + + it('Should throw an error with correct message for non-existent function', () => { + expect(() => { + evaluate('api.nonExistingFunction()', { api: {} }); + }).toThrow('No function named nonExistingFunction is defined in this context'); + + // nested ref + expect(() => { + evaluate('objectWrapper.path.deepNested()', { + objectWrapper: { + path: { + deepNested: undefined, + }, + }, + }); + }).toThrow('No function named deepNested is defined in this context'); + }); + + it('Should throw an error with correct message for non-callable targets', () => { + expect(() => { + evaluate('objectWrapper.path()', { + objectWrapper: { + path: {}, + }, + }); + }).toThrow('path is not a function'); + }); }); diff --git a/packages/framework/esm-expression-evaluator/src/evaluator.ts b/packages/framework/esm-expression-evaluator/src/evaluator.ts index 3a00499cd..40e0e0a4c 100644 --- a/packages/framework/esm-expression-evaluator/src/evaluator.ts +++ b/packages/framework/esm-expression-evaluator/src/evaluator.ts @@ -1,5 +1,5 @@ /** @category Utility */ -import jsep from 'jsep'; +import jsep, { type Expression, type MemberExpression } from 'jsep'; import jsepArrow, { type ArrowExpression } from '@jsep-plugin/arrow'; import jsepNew, { type NewExpression } from '@jsep-plugin/new'; import jsepNumbers from '@jsep-plugin/numbers'; @@ -467,9 +467,9 @@ function visitCallExpression(expression: jsep.CallExpression, context: Evaluatio let callee = visitExpression(expression.callee, context); if (!callee) { - throw `No function named ${expression.callee} is defined in this context`; + throw `No function named ${getCallTargetName(expression.callee)} is defined in this context`; } else if (!(typeof callee === 'function')) { - throw `${expression.callee} is not a function`; + throw `${getCallTargetName(expression.callee)} is not a function`; } return callee(...args); @@ -722,3 +722,14 @@ function isValidVariableType(val: unknown): val is VariablesMap['a'] { return false; } + +function getCallTargetName(expression: Expression) { + if (!expression) { + return null; + } + if (expression.type === 'MemberExpression') { + return (expression.property as MemberExpression)?.name; + } + // identifier expression + return expression.name; +}