@nordcraft/search 1.0.45 → 1.0.47
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/dist/problems.worker.js +11 -3
- package/dist/problems.worker.js.map +1 -1
- package/dist/rules/attributes/unknownComponentAttributeRule.js +33 -0
- package/dist/rules/attributes/unknownComponentAttributeRule.js.map +1 -0
- package/dist/rules/attributes/unknownComponentAttributeRule.test.js +145 -0
- package/dist/rules/attributes/unknownComponentAttributeRule.test.js.map +1 -0
- package/dist/rules/logic/noStaticNodeCondition.js +29 -0
- package/dist/rules/logic/noStaticNodeCondition.js.map +1 -0
- package/dist/rules/logic/noStaticNodeCondition.test.js +274 -0
- package/dist/rules/logic/noStaticNodeCondition.test.js.map +1 -0
- package/dist/rules/logic/noUnnecessaryConditionFalsy.js +5 -1
- package/dist/rules/logic/noUnnecessaryConditionFalsy.js.map +1 -1
- package/dist/rules/logic/noUnnecessaryConditionTruthy.js +8 -4
- package/dist/rules/logic/noUnnecessaryConditionTruthy.js.map +1 -1
- package/dist/rules/logic/noUnnecessaryConditionTruthy.test.js +33 -4
- package/dist/rules/logic/noUnnecessaryConditionTruthy.test.js.map +1 -1
- package/dist/rules/noReferenceNodeRule.js +10 -8
- package/dist/rules/noReferenceNodeRule.js.map +1 -1
- package/dist/rules/noReferenceNodeRule.test.js +1 -1
- package/dist/rules/style/invalidStyleSyntaxRule.js +2 -2
- package/dist/rules/style/invalidStyleSyntaxRule.test.js +1 -1
- package/dist/rules/workflows/noPostNavigateAction.js +13 -2
- package/dist/rules/workflows/noPostNavigateAction.js.map +1 -1
- package/dist/rules/workflows/noPostNavigateAction.test.js +102 -1
- package/dist/rules/workflows/noPostNavigateAction.test.js.map +1 -1
- package/dist/util/contextlessEvaluateFormula.js +77 -0
- package/dist/util/contextlessEvaluateFormula.js.map +1 -0
- package/dist/util/contextlessEvaluateFormula.test.js +152 -0
- package/dist/util/contextlessEvaluateFormula.test.js.map +1 -0
- package/dist/util/helpers.test.js +1 -1
- package/dist/util/helpers.test.js.map +1 -1
- package/dist/util/removeUnused.fix.js +18 -1
- package/dist/util/removeUnused.fix.js.map +1 -1
- package/package.json +2 -2
- package/src/problems.worker.ts +18 -5
- package/src/rules/attributes/unknownComponentAttributeRule.test.ts +156 -0
- package/src/rules/attributes/unknownComponentAttributeRule.ts +40 -0
- package/src/rules/logic/noStaticNodeCondition.test.ts +290 -0
- package/src/rules/logic/noStaticNodeCondition.ts +43 -0
- package/src/rules/logic/noUnnecessaryConditionFalsy.ts +5 -4
- package/src/rules/logic/noUnnecessaryConditionTruthy.test.ts +37 -4
- package/src/rules/logic/noUnnecessaryConditionTruthy.ts +10 -8
- package/src/rules/noReferenceNodeRule.test.ts +1 -1
- package/src/rules/noReferenceNodeRule.ts +17 -10
- package/src/rules/style/invalidStyleSyntaxRule.test.ts +1 -1
- package/src/rules/style/invalidStyleSyntaxRule.ts +3 -3
- package/src/rules/workflows/noPostNavigateAction.test.ts +111 -1
- package/src/rules/workflows/noPostNavigateAction.ts +20 -3
- package/src/types.d.ts +12 -0
- package/src/util/contextlessEvaluateFormula.test.ts +190 -0
- package/src/util/contextlessEvaluateFormula.ts +110 -0
- package/src/util/helpers.test.ts +1 -1
- package/src/util/removeUnused.fix.ts +29 -1
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { Rule } from '../../types'
|
|
2
|
+
import { contextlessEvaluateFormula } from '../../util/contextlessEvaluateFormula'
|
|
2
3
|
|
|
3
4
|
export const noUnnecessaryConditionFalsy: Rule = {
|
|
4
5
|
code: 'no-unnecessary-condition-falsy',
|
|
@@ -10,10 +11,10 @@ export const noUnnecessaryConditionFalsy: Rule = {
|
|
|
10
11
|
}
|
|
11
12
|
|
|
12
13
|
if (
|
|
13
|
-
value.arguments.some(
|
|
14
|
-
(arg)
|
|
15
|
-
|
|
16
|
-
)
|
|
14
|
+
value.arguments.some((arg) => {
|
|
15
|
+
const { result, isStatic } = contextlessEvaluateFormula(arg.formula)
|
|
16
|
+
return isStatic && Boolean(result) === false
|
|
17
|
+
})
|
|
17
18
|
) {
|
|
18
19
|
report(path)
|
|
19
20
|
}
|
|
@@ -32,7 +32,41 @@ describe('noUnnecessaryConditionTruthy', () => {
|
|
|
32
32
|
},
|
|
33
33
|
],
|
|
34
34
|
},
|
|
35
|
-
|
|
35
|
+
},
|
|
36
|
+
classes: {},
|
|
37
|
+
events: {},
|
|
38
|
+
tag: 'div',
|
|
39
|
+
children: [],
|
|
40
|
+
style: {},
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
formulas: {},
|
|
44
|
+
apis: {},
|
|
45
|
+
attributes: {},
|
|
46
|
+
variables: {},
|
|
47
|
+
},
|
|
48
|
+
},
|
|
49
|
+
},
|
|
50
|
+
rules: [noUnnecessaryConditionTruthy],
|
|
51
|
+
}),
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
expect(problems).toHaveLength(1)
|
|
55
|
+
expect(problems[0].code).toBe('no-unnecessary-condition-truthy')
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
test('should report unnecessary truthy conditions when a value is of type object or array as they are always consider truthy', () => {
|
|
59
|
+
const problems = Array.from(
|
|
60
|
+
searchProject({
|
|
61
|
+
files: {
|
|
62
|
+
components: {
|
|
63
|
+
test: {
|
|
64
|
+
name: 'test',
|
|
65
|
+
nodes: {
|
|
66
|
+
root: {
|
|
67
|
+
type: 'element',
|
|
68
|
+
attrs: {
|
|
69
|
+
test1: {
|
|
36
70
|
type: 'or',
|
|
37
71
|
arguments: [
|
|
38
72
|
{
|
|
@@ -43,7 +77,7 @@ describe('noUnnecessaryConditionTruthy', () => {
|
|
|
43
77
|
},
|
|
44
78
|
],
|
|
45
79
|
},
|
|
46
|
-
|
|
80
|
+
test2: {
|
|
47
81
|
type: 'or',
|
|
48
82
|
arguments: [
|
|
49
83
|
{
|
|
@@ -73,10 +107,9 @@ describe('noUnnecessaryConditionTruthy', () => {
|
|
|
73
107
|
}),
|
|
74
108
|
)
|
|
75
109
|
|
|
76
|
-
expect(problems).toHaveLength(
|
|
110
|
+
expect(problems).toHaveLength(2)
|
|
77
111
|
expect(problems[0].code).toBe('no-unnecessary-condition-truthy')
|
|
78
112
|
expect(problems[1].code).toBe('no-unnecessary-condition-truthy')
|
|
79
|
-
expect(problems[2].code).toBe('no-unnecessary-condition-truthy')
|
|
80
113
|
})
|
|
81
114
|
|
|
82
115
|
test('should not report necessary truthy conditions', () => {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { Rule } from '../../types'
|
|
2
|
+
import { contextlessEvaluateFormula } from '../../util/contextlessEvaluateFormula'
|
|
2
3
|
|
|
3
4
|
export const noUnnecessaryConditionTruthy: Rule = {
|
|
4
5
|
code: 'no-unnecessary-condition-truthy',
|
|
@@ -10,14 +11,15 @@ export const noUnnecessaryConditionTruthy: Rule = {
|
|
|
10
11
|
}
|
|
11
12
|
|
|
12
13
|
if (
|
|
13
|
-
value.arguments.some(
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
14
|
+
value.arguments.some((arg) => {
|
|
15
|
+
// Objects and arrays, even empty ones, are always truthy
|
|
16
|
+
if (arg.formula.type === 'object' || arg.formula.type === 'array') {
|
|
17
|
+
return true
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const { result, isStatic } = contextlessEvaluateFormula(arg.formula)
|
|
21
|
+
return isStatic && Boolean(result) === true
|
|
22
|
+
})
|
|
21
23
|
) {
|
|
22
24
|
report(path)
|
|
23
25
|
}
|
|
@@ -130,7 +130,7 @@ describe('fix noReferenceNodeRule', () => {
|
|
|
130
130
|
const fixedFiles = fixProject({
|
|
131
131
|
files,
|
|
132
132
|
rule: noReferenceNodeRule,
|
|
133
|
-
fixType: 'delete
|
|
133
|
+
fixType: 'delete-orphan-node',
|
|
134
134
|
})
|
|
135
135
|
expect(Object.keys(fixedFiles.components.test!.nodes)).toEqual([
|
|
136
136
|
'root',
|
|
@@ -6,22 +6,29 @@ export const noReferenceNodeRule: Rule<{ node: string }> = {
|
|
|
6
6
|
level: 'warning',
|
|
7
7
|
category: 'No References',
|
|
8
8
|
visit: (report, args) => {
|
|
9
|
-
if (args.nodeType !== 'component') {
|
|
9
|
+
if (args.nodeType !== 'component-node') {
|
|
10
10
|
return
|
|
11
11
|
}
|
|
12
|
-
const { path,
|
|
13
|
-
const
|
|
14
|
-
|
|
12
|
+
const { path, component } = args
|
|
13
|
+
const nodeId = path.at(-1)
|
|
14
|
+
if (typeof nodeId !== 'string') {
|
|
15
|
+
return
|
|
16
|
+
}
|
|
17
|
+
const referencedNodesInComponent = args.memo(
|
|
18
|
+
`node-references-${component.name}`,
|
|
19
|
+
() =>
|
|
20
|
+
new Set(
|
|
21
|
+
Object.values(component.nodes).flatMap((node) => node.children ?? []),
|
|
22
|
+
),
|
|
15
23
|
)
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
}
|
|
24
|
+
|
|
25
|
+
if (nodeId !== 'root' && !referencedNodesInComponent.has(nodeId)) {
|
|
26
|
+
report(path, { node: nodeId }, ['delete-orphan-node'])
|
|
20
27
|
}
|
|
21
28
|
},
|
|
22
29
|
fixes: {
|
|
23
|
-
'delete
|
|
30
|
+
'delete-orphan-node': removeFromPathFix,
|
|
24
31
|
},
|
|
25
32
|
}
|
|
26
33
|
|
|
27
|
-
export type NoReferenceNodeRuleFix = 'delete
|
|
34
|
+
export type NoReferenceNodeRuleFix = 'delete-orphan-node'
|
|
@@ -92,7 +92,7 @@ describe('fix invalidStyleSyntaxRule', () => {
|
|
|
92
92
|
const fixedFiles = fixProject({
|
|
93
93
|
files,
|
|
94
94
|
rule: invalidStyleSyntaxRule,
|
|
95
|
-
fixType: 'delete
|
|
95
|
+
fixType: 'delete-style-property',
|
|
96
96
|
})
|
|
97
97
|
expect((fixedFiles.components.test!.nodes.root as ElementNodeModel).style)
|
|
98
98
|
.toMatchInlineSnapshot(`
|
|
@@ -24,12 +24,12 @@ export const invalidStyleSyntaxRule: Rule<{
|
|
|
24
24
|
},
|
|
25
25
|
)
|
|
26
26
|
if (!valid) {
|
|
27
|
-
report(path, { property: value.styleProperty }, ['delete
|
|
27
|
+
report(path, { property: value.styleProperty }, ['delete-style-property'])
|
|
28
28
|
}
|
|
29
29
|
},
|
|
30
30
|
fixes: {
|
|
31
|
-
'delete
|
|
31
|
+
'delete-style-property': removeFromPathFix,
|
|
32
32
|
},
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
-
export type InvalidStyleSyntaxRuleFix = 'delete
|
|
35
|
+
export type InvalidStyleSyntaxRuleFix = 'delete-style-property'
|
|
@@ -1,8 +1,11 @@
|
|
|
1
|
+
import type { ElementNodeModel } from '@nordcraft/core/dist/component/component.types'
|
|
2
|
+
import type { ProjectFiles } from '@nordcraft/ssr/dist/ssr.types'
|
|
1
3
|
import { describe, expect, test } from 'bun:test'
|
|
4
|
+
import { fixProject } from '../../fixProject'
|
|
2
5
|
import { searchProject } from '../../searchProject'
|
|
3
6
|
import { noPostNavigateAction } from './noPostNavigateAction'
|
|
4
7
|
|
|
5
|
-
describe('noPostNavigateAction', () => {
|
|
8
|
+
describe('find noPostNavigateAction', () => {
|
|
6
9
|
test('should detect actions after a url navigate action', () => {
|
|
7
10
|
const problems = Array.from(
|
|
8
11
|
searchProject({
|
|
@@ -457,3 +460,110 @@ describe('noPostNavigateAction', () => {
|
|
|
457
460
|
expect(problems).toHaveLength(0)
|
|
458
461
|
})
|
|
459
462
|
})
|
|
463
|
+
|
|
464
|
+
describe('fix noPostNavigateAction', () => {
|
|
465
|
+
test('should remove actions after a url navigate action', () => {
|
|
466
|
+
const files: ProjectFiles = {
|
|
467
|
+
formulas: {},
|
|
468
|
+
components: {
|
|
469
|
+
nav: {
|
|
470
|
+
attributes: {},
|
|
471
|
+
events: [],
|
|
472
|
+
formulas: {},
|
|
473
|
+
nodes: {
|
|
474
|
+
root: {
|
|
475
|
+
type: 'element',
|
|
476
|
+
tag: 'div',
|
|
477
|
+
attrs: {},
|
|
478
|
+
classes: {},
|
|
479
|
+
events: {},
|
|
480
|
+
children: ['wkPXF99C3qdRgZmUcnHO_'],
|
|
481
|
+
style: {},
|
|
482
|
+
},
|
|
483
|
+
wkPXF99C3qdRgZmUcnHO_: {
|
|
484
|
+
tag: 'button',
|
|
485
|
+
type: 'element',
|
|
486
|
+
attrs: {},
|
|
487
|
+
style: {
|
|
488
|
+
color: 'var(--grey-200, #E5E5E5)',
|
|
489
|
+
'border-radius': '6px',
|
|
490
|
+
'background-color': 'var(--blue-600, #2563EB)',
|
|
491
|
+
'padding-left': '8px',
|
|
492
|
+
'padding-right': '8px',
|
|
493
|
+
'padding-top': '8px',
|
|
494
|
+
'padding-bottom': '8px',
|
|
495
|
+
width: 'fit-content',
|
|
496
|
+
cursor: 'pointer',
|
|
497
|
+
},
|
|
498
|
+
events: {
|
|
499
|
+
click: {
|
|
500
|
+
trigger: 'click',
|
|
501
|
+
actions: [
|
|
502
|
+
{
|
|
503
|
+
name: '@toddle/gotToURL',
|
|
504
|
+
arguments: [
|
|
505
|
+
{
|
|
506
|
+
name: 'URL',
|
|
507
|
+
formula: {
|
|
508
|
+
type: 'value',
|
|
509
|
+
value: 'https://example.com',
|
|
510
|
+
},
|
|
511
|
+
},
|
|
512
|
+
],
|
|
513
|
+
label: 'Go to URL',
|
|
514
|
+
},
|
|
515
|
+
{
|
|
516
|
+
name: '@toddle/logToConsole',
|
|
517
|
+
arguments: [
|
|
518
|
+
{
|
|
519
|
+
name: 'Label',
|
|
520
|
+
formula: {
|
|
521
|
+
type: 'value',
|
|
522
|
+
value: '',
|
|
523
|
+
},
|
|
524
|
+
},
|
|
525
|
+
{
|
|
526
|
+
name: 'Data',
|
|
527
|
+
formula: {
|
|
528
|
+
type: 'value',
|
|
529
|
+
value: '<Data>',
|
|
530
|
+
},
|
|
531
|
+
},
|
|
532
|
+
],
|
|
533
|
+
label: 'Log to console',
|
|
534
|
+
},
|
|
535
|
+
],
|
|
536
|
+
},
|
|
537
|
+
},
|
|
538
|
+
classes: {},
|
|
539
|
+
children: ['zFhmZR6YnLy089HmlK-Yn'],
|
|
540
|
+
variants: [],
|
|
541
|
+
},
|
|
542
|
+
'zFhmZR6YnLy089HmlK-Yn': {
|
|
543
|
+
type: 'text',
|
|
544
|
+
value: {
|
|
545
|
+
type: 'value',
|
|
546
|
+
value: 'Button',
|
|
547
|
+
},
|
|
548
|
+
},
|
|
549
|
+
},
|
|
550
|
+
variables: {},
|
|
551
|
+
apis: {},
|
|
552
|
+
name: 'nav',
|
|
553
|
+
},
|
|
554
|
+
},
|
|
555
|
+
}
|
|
556
|
+
const fixedProject = fixProject({
|
|
557
|
+
files,
|
|
558
|
+
rule: noPostNavigateAction,
|
|
559
|
+
fixType: 'delete-following-actions',
|
|
560
|
+
})
|
|
561
|
+
expect(
|
|
562
|
+
(
|
|
563
|
+
fixedProject.components.nav?.nodes[
|
|
564
|
+
'wkPXF99C3qdRgZmUcnHO_'
|
|
565
|
+
] as ElementNodeModel
|
|
566
|
+
)?.events?.click?.actions,
|
|
567
|
+
).toHaveLength(1)
|
|
568
|
+
})
|
|
569
|
+
})
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { get } from '@nordcraft/core/dist/utils/collections'
|
|
2
|
-
import type { Rule } from '../../types'
|
|
1
|
+
import { get, set } from '@nordcraft/core/dist/utils/collections'
|
|
2
|
+
import type { ActionModelNode, Rule } from '../../types'
|
|
3
3
|
|
|
4
4
|
export const noPostNavigateAction: Rule<{ parameter: string }> = {
|
|
5
5
|
code: 'no post navigate action',
|
|
@@ -25,7 +25,24 @@ export const noPostNavigateAction: Rule<{ parameter: string }> = {
|
|
|
25
25
|
const actionIndex = Number(_actionIndex)
|
|
26
26
|
if (actionIndex < actions.length - 1) {
|
|
27
27
|
// If the action is not the last one in the array, report it
|
|
28
|
-
report(path)
|
|
28
|
+
report(path, undefined, ['delete-following-actions'])
|
|
29
29
|
}
|
|
30
30
|
},
|
|
31
|
+
fixes: {
|
|
32
|
+
'delete-following-actions': ({ path, files }: ActionModelNode) => {
|
|
33
|
+
const actionsArrayPath = path.slice(0, -1).map((p) => String(p))
|
|
34
|
+
const actions = get(files, actionsArrayPath)
|
|
35
|
+
const actionIndex = path.at(-1)
|
|
36
|
+
if (actionIndex === undefined || !Array.isArray(actions)) {
|
|
37
|
+
return
|
|
38
|
+
}
|
|
39
|
+
return set(
|
|
40
|
+
files,
|
|
41
|
+
actionsArrayPath,
|
|
42
|
+
actions.slice(0, Number(actionIndex) + 1),
|
|
43
|
+
)
|
|
44
|
+
},
|
|
45
|
+
},
|
|
31
46
|
}
|
|
47
|
+
|
|
48
|
+
export type NoPostNavigateActionRuleFix = 'delete-following-actions'
|
package/src/types.d.ts
CHANGED
|
@@ -25,13 +25,16 @@ import type { LegacyActionRuleFix } from './rules/actions/legacyActionRule'
|
|
|
25
25
|
import type { NoReferenceProjectActionRuleFix } from './rules/actions/noReferenceProjectActionRule'
|
|
26
26
|
import type { NoReferenceApiRuleFix } from './rules/apis/noReferenceApiRule'
|
|
27
27
|
import type { NoReferenceAttributeRuleFix } from './rules/attributes/noReferenceAttributeRule'
|
|
28
|
+
import type { UnknownComponentAttributeRuleFix } from './rules/attributes/unknownComponentAttributeRule'
|
|
28
29
|
import type { NoReferenceComponentRuleFix } from './rules/components/noReferenceComponentRule'
|
|
29
30
|
import type { NoReferenceEventRuleFix } from './rules/events/noReferenceEventRule'
|
|
30
31
|
import type { LegacyFormulaRuleFix } from './rules/formulas/legacyFormulaRule'
|
|
31
32
|
import type { NoReferenceComponentFormulaRuleFix } from './rules/formulas/noReferenceComponentFormulaRule'
|
|
32
33
|
import type { NoReferenceProjectFormulaRuleFix } from './rules/formulas/noReferenceProjectFormulaRule'
|
|
34
|
+
import type { NoStaticNodeConditionRuleFix } from './rules/logic/noStaticNodeCondition'
|
|
33
35
|
import type { NoReferenceNodeRuleFix } from './rules/noReferenceNodeRule'
|
|
34
36
|
import type { InvalidStyleSyntaxRuleFix } from './rules/style/invalidStyleSyntaxRule'
|
|
37
|
+
import type { NoPostNavigateActionRuleFix } from './rules/workflows/noPostNavigateAction'
|
|
35
38
|
|
|
36
39
|
type Code =
|
|
37
40
|
| 'duplicate event trigger'
|
|
@@ -59,6 +62,7 @@ type Code =
|
|
|
59
62
|
| 'no-reference project action'
|
|
60
63
|
| 'no-reference project formula'
|
|
61
64
|
| 'no-reference variable'
|
|
65
|
+
| 'no-static-node-condition'
|
|
62
66
|
| 'no-unnecessary-condition-falsy'
|
|
63
67
|
| 'no-unnecessary-condition-truthy'
|
|
64
68
|
| 'non-empty void element'
|
|
@@ -73,6 +77,7 @@ type Code =
|
|
|
73
77
|
| 'unknown api'
|
|
74
78
|
| 'unknown attribute'
|
|
75
79
|
| 'unknown classname'
|
|
80
|
+
| 'unknown component attribute'
|
|
76
81
|
| 'unknown component formula input'
|
|
77
82
|
| 'unknown component slot'
|
|
78
83
|
| 'unknown context formula'
|
|
@@ -217,6 +222,10 @@ type ComponentWorkflowNode = {
|
|
|
217
222
|
testValue: any
|
|
218
223
|
}[]
|
|
219
224
|
| null
|
|
225
|
+
callbacks?: {
|
|
226
|
+
name: string
|
|
227
|
+
testValue: any
|
|
228
|
+
}[]
|
|
220
229
|
actions: ActionModel[]
|
|
221
230
|
exposeInContext?: boolean | undefined
|
|
222
231
|
}
|
|
@@ -343,6 +352,9 @@ type FixType =
|
|
|
343
352
|
| NoReferenceComponentFormulaRuleFix
|
|
344
353
|
| NoReferenceNodeRuleFix
|
|
345
354
|
| InvalidStyleSyntaxRuleFix
|
|
355
|
+
| NoStaticNodeConditionRuleFix
|
|
356
|
+
| NoPostNavigateActionRuleFix
|
|
357
|
+
| UnknownComponentAttributeRuleFix
|
|
346
358
|
|
|
347
359
|
export interface Rule<T = unknown, V = NodeType> {
|
|
348
360
|
category: Category
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
import { describe, expect, test } from 'bun:test'
|
|
2
|
+
import { contextlessEvaluateFormula } from './contextlessEvaluateFormula'
|
|
3
|
+
|
|
4
|
+
describe('contextlessEvaluateFormula', () => {
|
|
5
|
+
test('should return `true` when the formula is a simple value formula', () => {
|
|
6
|
+
expect(
|
|
7
|
+
contextlessEvaluateFormula({
|
|
8
|
+
type: 'value',
|
|
9
|
+
value: true,
|
|
10
|
+
}),
|
|
11
|
+
).toEqual({
|
|
12
|
+
isStatic: true,
|
|
13
|
+
result: true,
|
|
14
|
+
})
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
test('should not return a result and have `isStatic: false` when the formula uses a non-pure formula ("randomNumber", "Now")', () => {
|
|
18
|
+
expect(
|
|
19
|
+
contextlessEvaluateFormula({
|
|
20
|
+
type: 'apply',
|
|
21
|
+
name: 'randomNumber',
|
|
22
|
+
arguments: [],
|
|
23
|
+
}),
|
|
24
|
+
).toEqual({
|
|
25
|
+
isStatic: false,
|
|
26
|
+
result: undefined,
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
expect(
|
|
30
|
+
contextlessEvaluateFormula({
|
|
31
|
+
type: 'apply',
|
|
32
|
+
name: 'now',
|
|
33
|
+
arguments: [],
|
|
34
|
+
}),
|
|
35
|
+
).toEqual({
|
|
36
|
+
isStatic: false,
|
|
37
|
+
result: undefined,
|
|
38
|
+
})
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
test('should not return a result and have `isStatic: false` when the formula depends on a variable', () => {
|
|
42
|
+
expect(
|
|
43
|
+
contextlessEvaluateFormula({
|
|
44
|
+
type: 'path',
|
|
45
|
+
path: ['Variables', 'myVariable'],
|
|
46
|
+
}),
|
|
47
|
+
).toEqual({
|
|
48
|
+
isStatic: false,
|
|
49
|
+
result: undefined,
|
|
50
|
+
})
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
test('should return a result and static for an array formula where all args are array or value types', () => {
|
|
54
|
+
expect(
|
|
55
|
+
contextlessEvaluateFormula({
|
|
56
|
+
type: 'array',
|
|
57
|
+
arguments: [
|
|
58
|
+
{ formula: { type: 'value', value: 1 } },
|
|
59
|
+
{
|
|
60
|
+
formula: {
|
|
61
|
+
type: 'array',
|
|
62
|
+
arguments: [{ formula: { type: 'value', value: 2 } }],
|
|
63
|
+
},
|
|
64
|
+
},
|
|
65
|
+
],
|
|
66
|
+
}),
|
|
67
|
+
).toEqual({
|
|
68
|
+
isStatic: true,
|
|
69
|
+
result: [1, [2]],
|
|
70
|
+
})
|
|
71
|
+
})
|
|
72
|
+
|
|
73
|
+
test('should return `isStatic: false` for an array formula with a non-static argument', () => {
|
|
74
|
+
expect(
|
|
75
|
+
contextlessEvaluateFormula({
|
|
76
|
+
type: 'array',
|
|
77
|
+
arguments: [
|
|
78
|
+
{ formula: { type: 'value', value: 1 } },
|
|
79
|
+
{ formula: { type: 'apply', name: 'randomNumber', arguments: [] } },
|
|
80
|
+
],
|
|
81
|
+
}),
|
|
82
|
+
).toEqual({
|
|
83
|
+
isStatic: false,
|
|
84
|
+
result: [
|
|
85
|
+
1,
|
|
86
|
+
undefined, // The second argument was not static
|
|
87
|
+
],
|
|
88
|
+
})
|
|
89
|
+
})
|
|
90
|
+
|
|
91
|
+
test('should be static true for `And` formulas with no arguments', () => {
|
|
92
|
+
expect(
|
|
93
|
+
contextlessEvaluateFormula({
|
|
94
|
+
type: 'and',
|
|
95
|
+
arguments: [],
|
|
96
|
+
}),
|
|
97
|
+
).toEqual({
|
|
98
|
+
isStatic: true,
|
|
99
|
+
result: true,
|
|
100
|
+
})
|
|
101
|
+
})
|
|
102
|
+
|
|
103
|
+
test('should be static false for `Or` formulas with no arguments', () => {
|
|
104
|
+
expect(
|
|
105
|
+
contextlessEvaluateFormula({
|
|
106
|
+
type: 'or',
|
|
107
|
+
arguments: [],
|
|
108
|
+
}),
|
|
109
|
+
).toEqual({
|
|
110
|
+
isStatic: true,
|
|
111
|
+
result: false,
|
|
112
|
+
})
|
|
113
|
+
})
|
|
114
|
+
|
|
115
|
+
test('should be static true for `And` formulas with all static truthy arguments', () => {
|
|
116
|
+
expect(
|
|
117
|
+
contextlessEvaluateFormula({
|
|
118
|
+
type: 'and',
|
|
119
|
+
arguments: [
|
|
120
|
+
{ formula: { type: 'value', value: true } },
|
|
121
|
+
{ formula: { type: 'value', value: true } },
|
|
122
|
+
],
|
|
123
|
+
}),
|
|
124
|
+
).toEqual({
|
|
125
|
+
isStatic: true,
|
|
126
|
+
result: true,
|
|
127
|
+
})
|
|
128
|
+
})
|
|
129
|
+
|
|
130
|
+
test('should not be static for `And` where any dynamic value exist', () => {
|
|
131
|
+
expect(
|
|
132
|
+
contextlessEvaluateFormula({
|
|
133
|
+
type: 'and',
|
|
134
|
+
arguments: [
|
|
135
|
+
{ formula: { type: 'value', value: true } },
|
|
136
|
+
{ formula: { type: 'apply', name: 'randomNumber', arguments: [] } },
|
|
137
|
+
],
|
|
138
|
+
}),
|
|
139
|
+
).toEqual({
|
|
140
|
+
isStatic: false,
|
|
141
|
+
result: undefined,
|
|
142
|
+
})
|
|
143
|
+
})
|
|
144
|
+
|
|
145
|
+
test('should be static false for `And` when any argument is static false, even when dynamic arguments exist', () => {
|
|
146
|
+
expect(
|
|
147
|
+
contextlessEvaluateFormula({
|
|
148
|
+
type: 'and',
|
|
149
|
+
arguments: [
|
|
150
|
+
{ formula: { type: 'value', value: true } },
|
|
151
|
+
{ formula: { type: 'path', path: ['Variables', 'myVariable'] } },
|
|
152
|
+
{ formula: { type: 'value', value: false } },
|
|
153
|
+
],
|
|
154
|
+
}),
|
|
155
|
+
).toEqual({
|
|
156
|
+
isStatic: true,
|
|
157
|
+
result: false,
|
|
158
|
+
})
|
|
159
|
+
})
|
|
160
|
+
|
|
161
|
+
test('should not be static for `Or` when any argument is dynamic and all static are falsy', () => {
|
|
162
|
+
expect(
|
|
163
|
+
contextlessEvaluateFormula({
|
|
164
|
+
type: 'or',
|
|
165
|
+
arguments: [
|
|
166
|
+
{ formula: { type: 'value', value: false } },
|
|
167
|
+
{ formula: { type: 'apply', name: 'randomNumber', arguments: [] } },
|
|
168
|
+
],
|
|
169
|
+
}),
|
|
170
|
+
).toEqual({
|
|
171
|
+
isStatic: false,
|
|
172
|
+
result: undefined,
|
|
173
|
+
})
|
|
174
|
+
})
|
|
175
|
+
|
|
176
|
+
test('should be static true for `Or` when any argument is static true, even when dynamic arguments exist', () => {
|
|
177
|
+
expect(
|
|
178
|
+
contextlessEvaluateFormula({
|
|
179
|
+
type: 'or',
|
|
180
|
+
arguments: [
|
|
181
|
+
{ formula: { type: 'value', value: true } },
|
|
182
|
+
{ formula: { type: 'apply', name: 'randomNumber', arguments: [] } },
|
|
183
|
+
],
|
|
184
|
+
}),
|
|
185
|
+
).toEqual({
|
|
186
|
+
isStatic: true,
|
|
187
|
+
result: true,
|
|
188
|
+
})
|
|
189
|
+
})
|
|
190
|
+
})
|