@openmrs/esm-expression-evaluator 5.8.2-pre.2534 → 6.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +1 -1
- package/jest.config.js +1 -0
- package/package.json +2 -3
- package/src/evaluator.test.ts +31 -31
- package/src/extractor.test.ts +10 -10
package/.turbo/turbo-build.log
CHANGED
|
@@ -2,4 +2,4 @@ asset openmrs-esm-expression-evaluator.js 32.1 KiB [emitted] [minimized] (name:
|
|
|
2
2
|
orphan modules 89.8 KiB [orphan] 10 modules
|
|
3
3
|
runtime modules 670 bytes 3 modules
|
|
4
4
|
./src/index.ts + 10 modules 89.9 KiB [built] [code generated]
|
|
5
|
-
webpack 5.88.0 compiled successfully in
|
|
5
|
+
webpack 5.88.0 compiled successfully in 8159 ms
|
package/jest.config.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openmrs/esm-expression-evaluator",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "6.0.0",
|
|
4
4
|
"license": "MPL-2.0",
|
|
5
5
|
"description": "Utilities for evaluating user-defined expressions",
|
|
6
6
|
"browser": "dist/openmrs-esm-expression-evaluator.js",
|
|
@@ -51,6 +51,5 @@
|
|
|
51
51
|
"cross-env": "^7.0.3",
|
|
52
52
|
"jest": "^29.7.0",
|
|
53
53
|
"jest-cli": "^29.7.0"
|
|
54
|
-
}
|
|
55
|
-
"stableVersion": "5.7.3"
|
|
54
|
+
}
|
|
56
55
|
}
|
package/src/evaluator.test.ts
CHANGED
|
@@ -2,26 +2,26 @@ import { describe, it, expect } from '@jest/globals';
|
|
|
2
2
|
import { compile, evaluate, evaluateAsBoolean, evaluateAsNumber, evaluateAsType, evaluateAsync } from './evaluator';
|
|
3
3
|
|
|
4
4
|
describe('OpenMRS Expression Evaluator', () => {
|
|
5
|
-
it('
|
|
5
|
+
it('should evaluate a simple expression', () => {
|
|
6
6
|
expect(evaluate('1 + 1')).toBe(2);
|
|
7
7
|
});
|
|
8
8
|
|
|
9
|
-
it('
|
|
9
|
+
it('should support multiplication', () => {
|
|
10
10
|
expect(evaluate('1 * 2')).toBe(2);
|
|
11
11
|
});
|
|
12
12
|
|
|
13
|
-
it('
|
|
13
|
+
it('should support the not operator', () => {
|
|
14
14
|
expect(evaluate('!1')).toBe(false);
|
|
15
15
|
expect(evaluate('!0')).toBe(true);
|
|
16
16
|
expect(evaluate('!true')).toBe(false);
|
|
17
17
|
expect(evaluate('!false')).toBe(true);
|
|
18
18
|
});
|
|
19
19
|
|
|
20
|
-
it('
|
|
20
|
+
it('should support expressions in parentheses', () => {
|
|
21
21
|
expect(evaluate('(1, 2, 3)')).toBe(3);
|
|
22
22
|
});
|
|
23
23
|
|
|
24
|
-
it('
|
|
24
|
+
it('should support order of operations', () => {
|
|
25
25
|
expect(evaluate('(1 + 2) * 3')).toBe(9);
|
|
26
26
|
expect(evaluate('1 + 2 * 3')).toBe(7);
|
|
27
27
|
});
|
|
@@ -34,26 +34,26 @@ describe('OpenMRS Expression Evaluator', () => {
|
|
|
34
34
|
expect(evaluate('1 in [1, 2, 3]')).toBe(true);
|
|
35
35
|
});
|
|
36
36
|
|
|
37
|
-
it('
|
|
37
|
+
it('should support basic variables', () => {
|
|
38
38
|
expect(evaluate('1 + a', { a: 3 })).toBe(4);
|
|
39
39
|
});
|
|
40
40
|
|
|
41
|
-
it('
|
|
41
|
+
it('should support nullish coalescing', () => {
|
|
42
42
|
expect(evaluate('a ?? b', { a: null, b: 3 })).toBe(3);
|
|
43
43
|
expect(evaluate('a ?? b', { a: 3, b: null })).toBe(3);
|
|
44
44
|
});
|
|
45
45
|
|
|
46
|
-
it('
|
|
46
|
+
it('should support functions', () => {
|
|
47
47
|
expect(evaluate('a(1)', { a: (i: number) => i + 1 })).toBe(2);
|
|
48
48
|
});
|
|
49
49
|
|
|
50
|
-
it('
|
|
50
|
+
it('should support built-in functions', () => {
|
|
51
51
|
expect(evaluate('a.includes("v")', { a: 'value' })).toBe(true);
|
|
52
52
|
expect(evaluate('"value".includes("v")')).toBe(true);
|
|
53
53
|
expect(evaluate('(3.14159).toPrecision(3)')).toBe('3.14');
|
|
54
54
|
});
|
|
55
55
|
|
|
56
|
-
it('
|
|
56
|
+
it('should give a useful error message for properties on missing objects', () => {
|
|
57
57
|
expect(() => evaluate('a.b')).toThrow('ReferenceError: a is not defined');
|
|
58
58
|
expect(() => evaluate('a["b"]')).toThrow('ReferenceError: a is not defined');
|
|
59
59
|
expect(() => evaluate('a.b.c', { a: {} })).toThrow("TypeError: cannot read properties of undefined (reading 'c')");
|
|
@@ -65,17 +65,17 @@ describe('OpenMRS Expression Evaluator', () => {
|
|
|
65
65
|
);
|
|
66
66
|
});
|
|
67
67
|
|
|
68
|
-
it('
|
|
68
|
+
it('should not support this', () => {
|
|
69
69
|
expect(() => evaluate('this')).toThrow(
|
|
70
70
|
/Expression evaluator does not support expression of type 'ThisExpression'.*/i,
|
|
71
71
|
);
|
|
72
72
|
});
|
|
73
73
|
|
|
74
|
-
it('
|
|
74
|
+
it('should support property references', () => {
|
|
75
75
|
expect(evaluate('a.b.c', { a: { b: { c: 3 } } })).toBe(3);
|
|
76
76
|
});
|
|
77
77
|
|
|
78
|
-
it('
|
|
78
|
+
it('should not support prototype references', () => {
|
|
79
79
|
expect(() => evaluate('a.__proto__', { a: {} })).toThrow(/Cannot access the __proto__ property .*/i);
|
|
80
80
|
expect(() => evaluate('a["__proto__"]', { a: {} })).toThrow(/Cannot access the __proto__ property .*/i);
|
|
81
81
|
expect(() => evaluate('a[b]', { a: {}, b: '__proto__' })).toThrow(/Cannot access the __proto__ property .*/i);
|
|
@@ -88,36 +88,36 @@ describe('OpenMRS Expression Evaluator', () => {
|
|
|
88
88
|
expect(() => evaluate('a[b]', { a: {}, b: 'prototype' })).toThrow(/Cannot access the prototype property .*/i);
|
|
89
89
|
});
|
|
90
90
|
|
|
91
|
-
it('
|
|
91
|
+
it('should support ternaries', () => {
|
|
92
92
|
expect(evaluate('a ? 1 : 2', { a: true })).toBe(1);
|
|
93
93
|
expect(evaluate('a ? 1 : 2', { a: false })).toBe(2);
|
|
94
94
|
});
|
|
95
95
|
|
|
96
|
-
it('
|
|
96
|
+
it('should support hexadecimal', () => {
|
|
97
97
|
expect(evaluate('0xff')).toBe(255);
|
|
98
98
|
});
|
|
99
99
|
|
|
100
|
-
it('
|
|
100
|
+
it('should support string templates', () => {
|
|
101
101
|
expect(evaluate('`${a.b}`', { a: { b: 'string' } })).toBe('string');
|
|
102
102
|
});
|
|
103
103
|
|
|
104
|
-
it('
|
|
104
|
+
it('should support new Date()', () => {
|
|
105
105
|
expect(evaluate('new Date().getTime()')).toBeLessThanOrEqual(new Date().getTime());
|
|
106
106
|
});
|
|
107
107
|
|
|
108
|
-
it('
|
|
108
|
+
it('should support RegExp', () => {
|
|
109
109
|
expect(evaluate('/.*/.test(a)', { a: 'a' })).toBe(true);
|
|
110
110
|
});
|
|
111
111
|
|
|
112
|
-
it('
|
|
112
|
+
it('should support RegExp objects', () => {
|
|
113
113
|
expect(evaluate('new RegExp(".*").test(a)', { a: 'a' })).toBe(true);
|
|
114
114
|
});
|
|
115
115
|
|
|
116
|
-
it('
|
|
116
|
+
it('should support arrow functions inside expressions', () => {
|
|
117
117
|
expect(evaluate('[1, 2, 3].find(v => v === 3)')).toBe(3);
|
|
118
118
|
});
|
|
119
119
|
|
|
120
|
-
it('
|
|
120
|
+
it('should support globals', () => {
|
|
121
121
|
expect(evaluate('NaN')).toBeNaN();
|
|
122
122
|
expect(evaluate('Infinity')).toBe(Infinity);
|
|
123
123
|
expect(evaluate('Boolean(true)')).toBe(true);
|
|
@@ -127,7 +127,7 @@ describe('OpenMRS Expression Evaluator', () => {
|
|
|
127
127
|
expect(evaluate('Number.isInteger(42)')).toBe(true);
|
|
128
128
|
});
|
|
129
129
|
|
|
130
|
-
it('
|
|
130
|
+
it('should not support creating arbitrary objects', () => {
|
|
131
131
|
expect(() => evaluate('new object()')).toThrow(/Cannot instantiate object .*/i);
|
|
132
132
|
class Fn {
|
|
133
133
|
constructor() {}
|
|
@@ -135,12 +135,12 @@ describe('OpenMRS Expression Evaluator', () => {
|
|
|
135
135
|
expect(() => evaluate('new Fn()', { Fn })).toThrow(/Cannot instantiate object .*/i);
|
|
136
136
|
});
|
|
137
137
|
|
|
138
|
-
it('
|
|
138
|
+
it('should not support invalid property references on supported objects', () => {
|
|
139
139
|
expect(() => evaluate('new Date().__proto__')).toThrow(/Cannot access the __proto__ property .*/i);
|
|
140
140
|
expect(() => evaluate('new Date().prototype')).toThrow(/Cannot access the prototype property .*/i);
|
|
141
141
|
});
|
|
142
142
|
|
|
143
|
-
it('
|
|
143
|
+
it('should not return invalid types', () => {
|
|
144
144
|
expect(() => evaluate('a', { a: {} })).toThrow(/.* did not produce a valid result/i);
|
|
145
145
|
expect(() => evaluateAsBoolean('a', { a: 'value' })).toThrow(/.* did not produce a valid result/i);
|
|
146
146
|
expect(() => evaluateAsNumber('a', { a: true })).toThrow(/.* did not produce a valid result/i);
|
|
@@ -149,16 +149,16 @@ describe('OpenMRS Expression Evaluator', () => {
|
|
|
149
149
|
);
|
|
150
150
|
});
|
|
151
151
|
|
|
152
|
-
it('
|
|
152
|
+
it('should support a compilation phase', () => {
|
|
153
153
|
const exp = compile('1 + 1');
|
|
154
154
|
expect(evaluate(exp)).toBe(2);
|
|
155
155
|
});
|
|
156
156
|
|
|
157
|
-
it('
|
|
157
|
+
it('should not support variable assignment', () => {
|
|
158
158
|
expect(() => evaluate('var a = 1; a')).toThrow();
|
|
159
159
|
});
|
|
160
160
|
|
|
161
|
-
it('
|
|
161
|
+
it('should support asynchronous evaluation', async () => {
|
|
162
162
|
await expect(evaluateAsync('1 + 1')).resolves.toBe(2);
|
|
163
163
|
let a = new Promise((resolve) => {
|
|
164
164
|
setTimeout(() => resolve(1), 10);
|
|
@@ -172,13 +172,13 @@ describe('OpenMRS Expression Evaluator', () => {
|
|
|
172
172
|
).resolves.toBe(2);
|
|
173
173
|
});
|
|
174
174
|
|
|
175
|
-
it('
|
|
175
|
+
it('should support mock functions', () => {
|
|
176
176
|
expect(evaluate('api.getValue()', { api: { getValue: jest.fn().mockImplementation(() => 'value') } })).toBe(
|
|
177
177
|
'value',
|
|
178
178
|
);
|
|
179
179
|
});
|
|
180
180
|
|
|
181
|
-
it('
|
|
181
|
+
it('should support real-world use-cases', () => {
|
|
182
182
|
expect(
|
|
183
183
|
evaluate('!isEmpty(array)', {
|
|
184
184
|
array: [],
|
|
@@ -199,7 +199,7 @@ describe('OpenMRS Expression Evaluator', () => {
|
|
|
199
199
|
).toBe(true);
|
|
200
200
|
});
|
|
201
201
|
|
|
202
|
-
it('
|
|
202
|
+
it('should throw an error with correct message for non-existent function', () => {
|
|
203
203
|
expect(() => {
|
|
204
204
|
evaluate('api.nonExistingFunction()', { api: {} });
|
|
205
205
|
}).toThrow('No function named nonExistingFunction is defined in this context');
|
|
@@ -216,7 +216,7 @@ describe('OpenMRS Expression Evaluator', () => {
|
|
|
216
216
|
}).toThrow('No function named deepNested is defined in this context');
|
|
217
217
|
});
|
|
218
218
|
|
|
219
|
-
it('
|
|
219
|
+
it('should throw an error with correct message for non-callable targets', () => {
|
|
220
220
|
expect(() => {
|
|
221
221
|
evaluate('objectWrapper.path()', {
|
|
222
222
|
objectWrapper: {
|
package/src/extractor.test.ts
CHANGED
|
@@ -2,46 +2,46 @@ import { describe, it, expect } from '@jest/globals';
|
|
|
2
2
|
import { extractVariableNames } from './extractor';
|
|
3
3
|
|
|
4
4
|
describe('OpenMRS Expression Extractor', () => {
|
|
5
|
-
it('
|
|
5
|
+
it('returns an empty list for expression lacking variables', () => {
|
|
6
6
|
expect(extractVariableNames('1 + 1')).toEqual([]);
|
|
7
7
|
});
|
|
8
8
|
|
|
9
|
-
it('
|
|
9
|
+
it('supports basic variables', () => {
|
|
10
10
|
expect(extractVariableNames('1 + a')).toEqual(['a']);
|
|
11
11
|
});
|
|
12
12
|
|
|
13
|
-
it('
|
|
13
|
+
it('extracts both variables from binary operators', () => {
|
|
14
14
|
expect(extractVariableNames('a ?? b')).toEqual(['a', 'b']);
|
|
15
15
|
});
|
|
16
16
|
|
|
17
|
-
it('
|
|
17
|
+
it('supports functions', () => {
|
|
18
18
|
expect(extractVariableNames('a(b)')).toEqual(['a', 'b']);
|
|
19
19
|
});
|
|
20
20
|
|
|
21
|
-
it('
|
|
21
|
+
it('supports built-in functions', () => {
|
|
22
22
|
expect(extractVariableNames('a.includes("v")')).toEqual(['a']);
|
|
23
23
|
expect(extractVariableNames('"value".includes(a)')).toEqual(['a']);
|
|
24
24
|
expect(extractVariableNames('(3.14159).toPrecision(a)')).toEqual(['a']);
|
|
25
25
|
});
|
|
26
26
|
|
|
27
|
-
it('
|
|
27
|
+
it('supports string templates', () => {
|
|
28
28
|
expect(extractVariableNames('`${a.b}`')).toEqual(['a']);
|
|
29
29
|
});
|
|
30
30
|
|
|
31
|
-
it('
|
|
31
|
+
it('supports RegExp', () => {
|
|
32
32
|
expect(extractVariableNames('/.*/.test(a)')).toEqual(['a']);
|
|
33
33
|
});
|
|
34
34
|
|
|
35
|
-
it('
|
|
35
|
+
it('supports global objects', () => {
|
|
36
36
|
expect(extractVariableNames('Math.min(a, b, c)')).toEqual(['a', 'b', 'c']);
|
|
37
37
|
expect(extractVariableNames('isNaN(a)')).toEqual(['a']);
|
|
38
38
|
});
|
|
39
39
|
|
|
40
|
-
it('
|
|
40
|
+
it('supports arrow functions inside expressions', () => {
|
|
41
41
|
expect(extractVariableNames('[1, 2, 3].find(v => v === a)')).toEqual(['a']);
|
|
42
42
|
});
|
|
43
43
|
|
|
44
|
-
it('
|
|
44
|
+
it('supports real-world use-cases', () => {
|
|
45
45
|
expect(extractVariableNames('!isEmpty(array)')).toEqual(['isEmpty', 'array']);
|
|
46
46
|
|
|
47
47
|
expect(
|