@teleporthq/teleport-uidl-validator 0.18.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.
Files changed (84) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +14 -0
  3. package/__tests__/parser/componennt-with-referenced-styles.json +111 -0
  4. package/__tests__/parser/component-parsing.ts +116 -0
  5. package/__tests__/parser/component-referenced.ts +50 -0
  6. package/__tests__/parser/component-with-primitive-values.json +61 -0
  7. package/__tests__/parser/component-with-proper-values.json +97 -0
  8. package/__tests__/parser/component-with-reusalble-styles.json +117 -0
  9. package/__tests__/validator/component-element-with-empty-name.json +36 -0
  10. package/__tests__/validator/component-invalid-sample.json +184 -0
  11. package/__tests__/validator/component-uidl-with-event-modifier-undefined.json +45 -0
  12. package/__tests__/validator/component-uidl-with-null-undefined.json +24 -0
  13. package/__tests__/validator/index.ts +216 -0
  14. package/__tests__/validator/old-project-invalid-sample.json +317 -0
  15. package/__tests__/validator/project-invalid-sample-no-route.json +667 -0
  16. package/__tests__/validator/project-invalid-sample.json +671 -0
  17. package/__tests__/validator/project-sample.json +667 -0
  18. package/dist/cjs/decoders/component-decoder.d.ts +4 -0
  19. package/dist/cjs/decoders/component-decoder.js +30 -0
  20. package/dist/cjs/decoders/component-decoder.js.map +1 -0
  21. package/dist/cjs/decoders/custom-combinators.d.ts +8 -0
  22. package/dist/cjs/decoders/custom-combinators.js +59 -0
  23. package/dist/cjs/decoders/custom-combinators.js.map +1 -0
  24. package/dist/cjs/decoders/index.d.ts +2 -0
  25. package/dist/cjs/decoders/index.js +9 -0
  26. package/dist/cjs/decoders/index.js.map +1 -0
  27. package/dist/cjs/decoders/project-decoder.d.ts +5 -0
  28. package/dist/cjs/decoders/project-decoder.js +38 -0
  29. package/dist/cjs/decoders/project-decoder.js.map +1 -0
  30. package/dist/cjs/decoders/utils.d.ts +59 -0
  31. package/dist/cjs/decoders/utils.js +305 -0
  32. package/dist/cjs/decoders/utils.js.map +1 -0
  33. package/dist/cjs/index.d.ts +7 -0
  34. package/dist/cjs/index.js +38 -0
  35. package/dist/cjs/index.js.map +1 -0
  36. package/dist/cjs/parser/index.d.ts +10 -0
  37. package/dist/cjs/parser/index.js +156 -0
  38. package/dist/cjs/parser/index.js.map +1 -0
  39. package/dist/cjs/tsconfig.tsbuildinfo +1 -0
  40. package/dist/cjs/validator/index.d.ts +15 -0
  41. package/dist/cjs/validator/index.js +94 -0
  42. package/dist/cjs/validator/index.js.map +1 -0
  43. package/dist/cjs/validator/utils.d.ts +16 -0
  44. package/dist/cjs/validator/utils.js +373 -0
  45. package/dist/cjs/validator/utils.js.map +1 -0
  46. package/dist/esm/decoders/component-decoder.d.ts +4 -0
  47. package/dist/esm/decoders/component-decoder.js +27 -0
  48. package/dist/esm/decoders/component-decoder.js.map +1 -0
  49. package/dist/esm/decoders/custom-combinators.d.ts +8 -0
  50. package/dist/esm/decoders/custom-combinators.js +56 -0
  51. package/dist/esm/decoders/custom-combinators.js.map +1 -0
  52. package/dist/esm/decoders/index.d.ts +2 -0
  53. package/dist/esm/decoders/index.js +3 -0
  54. package/dist/esm/decoders/index.js.map +1 -0
  55. package/dist/esm/decoders/project-decoder.d.ts +5 -0
  56. package/dist/esm/decoders/project-decoder.js +35 -0
  57. package/dist/esm/decoders/project-decoder.js.map +1 -0
  58. package/dist/esm/decoders/utils.d.ts +59 -0
  59. package/dist/esm/decoders/utils.js +301 -0
  60. package/dist/esm/decoders/utils.js.map +1 -0
  61. package/dist/esm/index.d.ts +7 -0
  62. package/dist/esm/index.js +8 -0
  63. package/dist/esm/index.js.map +1 -0
  64. package/dist/esm/parser/index.d.ts +10 -0
  65. package/dist/esm/parser/index.js +151 -0
  66. package/dist/esm/parser/index.js.map +1 -0
  67. package/dist/esm/tsconfig.tsbuildinfo +1 -0
  68. package/dist/esm/validator/index.d.ts +15 -0
  69. package/dist/esm/validator/index.js +73 -0
  70. package/dist/esm/validator/index.js.map +1 -0
  71. package/dist/esm/validator/utils.d.ts +16 -0
  72. package/dist/esm/validator/utils.js +359 -0
  73. package/dist/esm/validator/utils.js.map +1 -0
  74. package/package.json +32 -0
  75. package/src/decoders/component-decoder.ts +48 -0
  76. package/src/decoders/custom-combinators.ts +59 -0
  77. package/src/decoders/index.ts +2 -0
  78. package/src/decoders/project-decoder.ts +40 -0
  79. package/src/decoders/utils.ts +571 -0
  80. package/src/index.ts +9 -0
  81. package/src/parser/index.ts +227 -0
  82. package/src/validator/index.ts +105 -0
  83. package/src/validator/utils.ts +463 -0
  84. package/tsconfig.json +9 -0
@@ -0,0 +1,184 @@
1
+ {
2
+ "name": "New UIDL",
3
+ "propDefinitions": {
4
+ "title": {
5
+ "type": "string",
6
+ "defaultValue": "Hello"
7
+ },
8
+ "items": {
9
+ "type": "array",
10
+ "defaultValue": []
11
+ },
12
+ "test": {
13
+ "type": "string",
14
+ "defaultValues": "test"
15
+ }
16
+ },
17
+ "stateDefinitions": {
18
+ "isVisible": {
19
+ "type": "boolean",
20
+ "defaultValue": true,
21
+ "test":"test"
22
+ },
23
+ "test": {
24
+ "type": "string",
25
+ "defaultValues": "test"
26
+ }
27
+ },
28
+ "node": {
29
+ "type": "element",
30
+ "content": {
31
+ "elementType": "container",
32
+ "attrs": {
33
+ "data-static-attr": {
34
+ "type": "static",
35
+ "content": "test"
36
+ },
37
+ "data-dynamic-attr": {
38
+ "type": "dynamic",
39
+ "content": {
40
+ "referenceType": "prop",
41
+ "id": "title"
42
+ }
43
+ }
44
+ },
45
+ "children": [
46
+ {
47
+ "type": "element",
48
+ "content": {
49
+ "elementType": "text",
50
+ "children": [
51
+ {
52
+ "type": "static",
53
+ "content": "Hello World!"
54
+ },
55
+ {
56
+ "type": "dynamic",
57
+ "content": {
58
+ "referenceType": "prop",
59
+ "id": "titles"
60
+ }
61
+ }
62
+ ]
63
+ }
64
+ },
65
+ {
66
+ "type": "repeat",
67
+ "content": {
68
+ "node": {
69
+ "type": "element",
70
+ "content": {
71
+ "elementType": "text",
72
+ "children": [
73
+ {
74
+ "type": "dynamic",
75
+ "content": {
76
+ "referenceType": "local",
77
+ "id": "item"
78
+ }
79
+ },
80
+ {
81
+ "type": "dynamic",
82
+ "content": {
83
+ "referenceType": "local",
84
+ "id": "index"
85
+ }
86
+ },
87
+ {
88
+ "type": "element",
89
+ "content": {
90
+ "elementType": "list",
91
+ "attrs": {
92
+ "items": {
93
+ "type": "static",
94
+ "content": ["angular", "react", "vue"]
95
+ }
96
+ }
97
+ }
98
+ }
99
+ ]
100
+ }
101
+ },
102
+ "dataSource": {
103
+ "type": "dynamic",
104
+ "content": {
105
+ "referenceType": "prop",
106
+ "id": "items"
107
+ }
108
+ },
109
+ "meta": {
110
+ "useIndex": false,
111
+ "iteratorName": "item"
112
+ }
113
+ }
114
+ },
115
+ {
116
+ "type": "repeat",
117
+ "content": {
118
+ "node": {
119
+ "type": "element",
120
+ "content": {
121
+ "elementType": "text",
122
+ "children": [
123
+ {
124
+ "type": "dynamic",
125
+ "content": {
126
+ "referenceType": "local",
127
+ "id": "item"
128
+ }
129
+ },
130
+ {
131
+ "type": "element",
132
+ "content": {
133
+ "elementType": "list",
134
+ "attrs": {
135
+ "items": {
136
+ "type": "static",
137
+ "content": ["angular", "react", "vue"]
138
+ }
139
+ }
140
+ }
141
+ }
142
+ ]
143
+ }
144
+ },
145
+ "dataSource": {
146
+ "type": "dynamic",
147
+ "content": {
148
+ "referenceType": "prop",
149
+ "id": "items"
150
+ }
151
+ },
152
+ "meta": {
153
+ "useIndex": true,
154
+ "iteratorName": "item-test"
155
+ }
156
+ }
157
+ },
158
+ {
159
+ "type": "conditional",
160
+ "content": {
161
+ "reference": {
162
+ "type": "dynamic",
163
+ "content": {
164
+ "referenceType": "state",
165
+ "id": "isVisibles"
166
+ }
167
+ },
168
+ "value": true,
169
+ "node": {
170
+ "type": "element",
171
+ "content": {
172
+ "elementType": "text",
173
+ "children": [{
174
+ "type": "static",
175
+ "content": "Now you see me!"
176
+ }]
177
+ }
178
+ }
179
+ }
180
+ }
181
+ ]
182
+ }
183
+ }
184
+ }
@@ -0,0 +1,45 @@
1
+ {
2
+ "name": "Simple Component",
3
+ "node": {
4
+ "type": "element",
5
+ "content": {
6
+ "elementType": "container",
7
+ "children": [
8
+ {
9
+ "type": "element",
10
+ "content": {
11
+ "elementType": "button",
12
+ "name": "openmodal",
13
+ "children": [
14
+ {
15
+ "type": "static",
16
+ "content": "Show Popup"
17
+ }
18
+ ],
19
+ "events": {
20
+ "click": [
21
+ {
22
+ "type": "stateChange",
23
+ "modifies": "isOpen",
24
+ "newState": true
25
+ }
26
+ ]
27
+ },
28
+ "attrs": {
29
+ "data-static-attr": {
30
+ "type": "static",
31
+ "content": "test"
32
+ }
33
+ },
34
+ "attributes": {
35
+ "htmlFor": {
36
+ "type": "static",
37
+ "content": "test"
38
+ }
39
+ }
40
+ }
41
+ }
42
+ ]
43
+ }
44
+ }
45
+ }
@@ -0,0 +1,24 @@
1
+ {
2
+ "name": "Sample Component",
3
+ "node": {
4
+ "type": "element",
5
+ "content": {
6
+ "elementType": "div",
7
+ "name": "div",
8
+ "children": [
9
+ {
10
+ "type": "static",
11
+ "content": "Hello World"
12
+ }
13
+ ],
14
+ "attrs": {},
15
+ "style": {
16
+ "width": {
17
+ "type": "static",
18
+ "content": "100px"
19
+ },
20
+ "height": null
21
+ }
22
+ }
23
+ }
24
+ }
@@ -0,0 +1,216 @@
1
+ import { Validator } from '../../src'
2
+
3
+ // @ts-ignore
4
+ import ComponentUidlElementWithEmptyName from './component-element-with-empty-name.json'
5
+ // @ts-ignore
6
+ import componentUidlWithEventModifierUndefined from './component-uidl-with-event-modifier-undefined.json'
7
+ // @ts-ignore
8
+ import invalidComponentUidlSample from './component-invalid-sample.json'
9
+ // @ts-ignore
10
+ import projectUidlSample from './project-sample.json'
11
+ // @ts-ignore
12
+ import oldInvalidProjectUidlSample from './old-project-invalid-sample.json'
13
+ // @ts-ignore
14
+ import invalidProjectUidlSample from './project-invalid-sample.json'
15
+ // @ts-ignore
16
+ import noRouteProjectUidlSample from './project-invalid-sample-no-route.json'
17
+ import uidlWithNull from './component-uidl-with-null-undefined.json'
18
+
19
+ import {
20
+ component,
21
+ definition,
22
+ repeatNode,
23
+ dynamicNode,
24
+ elementNode,
25
+ } from '@teleporthq/teleport-uidl-builders'
26
+
27
+ const uidl = component(
28
+ 'Repeat Component',
29
+ elementNode('container', {}, [
30
+ repeatNode(
31
+ elementNode('div', {}, [dynamicNode('local', 'item')]),
32
+ dynamicNode('prop', 'items'),
33
+ {
34
+ useIndex: true,
35
+ }
36
+ ),
37
+ ]),
38
+ { items: definition('array', ['hello', 'world']) },
39
+ { items: definition('array', ['hello', 'world']) }
40
+ )
41
+
42
+ describe('Validate UIDL', () => {
43
+ it('Component UIDL with null / undefined in the JSON', () => {
44
+ const validator = new Validator()
45
+ expect(() =>
46
+ validator.validateComponentSchema(uidlWithNull as unknown as Record<string, unknown>)
47
+ ).toThrow(Error)
48
+ })
49
+
50
+ describe('Component UIDL Format', () => {
51
+ it('returns object with valid=true and errorMsg="" if uidl is valid', () => {
52
+ const validator = new Validator()
53
+ const validationResult = validator.validateComponentSchema(
54
+ uidl as unknown as Record<string, unknown>
55
+ )
56
+
57
+ expect(typeof validationResult).toBe('object')
58
+ expect(validationResult.valid).toEqual(true)
59
+ expect(validationResult.errorMsg).toEqual('')
60
+ expect(typeof validationResult.componentUIDL).toBe('object')
61
+ })
62
+
63
+ it('returns customized errors', () => {
64
+ const validator = new Validator()
65
+ expect(() => validator.validateComponentSchema(invalidComponentUidlSample)).toThrow(Error)
66
+ })
67
+ })
68
+
69
+ describe('Component UIDL Content', () => {
70
+ it('returns object with valid=true and errorMSG="" if everything is ok', () => {
71
+ const validator = new Validator()
72
+ const validationResult = validator.validateComponentContent(uidl)
73
+
74
+ expect(typeof validationResult).toBe('object')
75
+ expect(validationResult.valid).toEqual(true)
76
+ expect(validationResult.errorMsg).toEqual('')
77
+ })
78
+
79
+ it('throws error if prop and state is used but not defined in propDefinitions', () => {
80
+ const validator = new Validator()
81
+ // @ts-ignore
82
+ expect(() => validator.validateComponentContent(invalidComponentUidlSample)).toThrow(Error)
83
+
84
+ // expect(validationResult.errorMsg).toBe(
85
+ // `\nUIDL Component Content Validation Error. Please check the following:
86
+ // "titles" is used but not defined. Please add it in propDefinitions,
87
+ // "isVisibles" is used but not defined. Please add it in stateDefinitions,
88
+ // Index variable is used but the "useIndex" meta information is false.,
89
+ // "item" is used in the "repeat" structure but the iterator name has this value: "item-test"`
90
+ // )
91
+ })
92
+
93
+ it('throws error if prop and state is used but not defined in propDefinitions', () => {
94
+ const validator = new Validator()
95
+ // @ts-ignore
96
+ expect(() => validator.validateComponentContent(invalidComponentUidlSample)).toThrow(Error)
97
+
98
+ // expect(validationResult.errorMsg).toBe(
99
+ // `\nUIDL Component Content Validation Error. Please check the following:
100
+ // "titles" is used but not defined. Please add it in propDefinitions,
101
+ // "isVisibles" is used but not defined. Please add it in stateDefinitions,
102
+ // Index variable is used but the "useIndex" meta information is false.,
103
+ // "item" is used in the "repeat" structure but the iterator name has this value: "item-test"`
104
+ // )
105
+ })
106
+
107
+ it('does not throw error if props and states have same keys', () => {
108
+ const validator = new Validator()
109
+ const warn = jest.spyOn(global.console, 'warn')
110
+
111
+ const validationResult = validator.validateComponentContent(uidl)
112
+
113
+ expect(typeof validationResult).toBe('object')
114
+ expect(validationResult.valid).toEqual(true)
115
+ expect(validationResult.errorMsg).toEqual('')
116
+ expect(warn).toHaveBeenCalled()
117
+ })
118
+
119
+ it('throws an error if event is modifying the state, which is not defined in stateDefinitions', () => {
120
+ const validator = new Validator()
121
+ // @ts-ignore
122
+ expect(() =>
123
+ validator.validateComponentContent(componentUidlWithEventModifierUndefined)
124
+ ).toThrow(Error)
125
+
126
+ // expect(validationResult.errorMsg).toBe(
127
+ // `\nUIDL Component Content Validation Error. Please check the following:
128
+ // "isOpen" is used in events, but not defined. Please add it in stateDefinitions
129
+ // )
130
+ })
131
+
132
+ it('throws an error if event name is empty string', () => {
133
+ const validator = new Validator()
134
+ // @ts-ignore
135
+ expect(() => validator.validateComponentSchema(ComponentUidlElementWithEmptyName)).toThrow(
136
+ Error
137
+ )
138
+
139
+ // expect(validationResult.errorMsg).toBe(
140
+ // UIDL Format Validation Error. Please check the following:
141
+ // - Path undefined: Error: Name attribute cannot be empty.
142
+ // is a undefined
143
+ // )
144
+ })
145
+ })
146
+
147
+ describe('Project UIDL Format', () => {
148
+ it('returns object with valid=true and errorMsg="" if uidl is valid', () => {
149
+ const validator = new Validator()
150
+ const validationResult = validator.validateProjectSchema(projectUidlSample)
151
+
152
+ expect(typeof validationResult).toBe('object')
153
+ expect(validationResult.valid).toEqual(true)
154
+ expect(validationResult.errorMsg).toEqual('')
155
+ expect(typeof validationResult.projectUIDL).toBe('object')
156
+ })
157
+
158
+ it('returns customized error', () => {
159
+ const validator = new Validator()
160
+ expect(() => validator.validateProjectSchema(invalidProjectUidlSample)).toThrow(Error)
161
+
162
+ // expect(validationResult.errorMsg).toBe(
163
+ // `\nUIDL Format Validation Error. Please check the following:
164
+ // - Path input: the key 'name' is required but was not present.
165
+ // is a DecoderError`
166
+ // )
167
+ })
168
+
169
+ it('supports older UIDL version and returns customized error', () => {
170
+ const validator = new Validator()
171
+ expect(() => validator.validateProjectSchema(oldInvalidProjectUidlSample)).toThrow(Error)
172
+
173
+ // expect(validationResult.errorMsg).toBe(
174
+ // `\nUIDL Format Validation Error. Please check the following:
175
+ // - Path input: the key 'name' is required but was not present.
176
+ // is a DecoderError`
177
+ // )
178
+ })
179
+ })
180
+
181
+ describe('Project UIDL Content', () => {
182
+ it('returns object with valid=true and errorMsg="" if uidl content is valid', () => {
183
+ const validator = new Validator()
184
+ // @ts-ignore
185
+ const validationResult = validator.validateProjectContent(projectUidlSample)
186
+
187
+ expect(typeof validationResult).toBe('object')
188
+ expect(validationResult.valid).toEqual(true)
189
+ expect(validationResult.errorMsg).toEqual('')
190
+ })
191
+
192
+ it('returns customized error if content is not properly defined', () => {
193
+ const validator = new Validator()
194
+ // @ts-ignore
195
+ expect(() => validator.validateProjectContent(invalidProjectUidlSample)).toThrow(Error)
196
+
197
+ // expect(validationResult.errorMsg).toEqual(
198
+ // `\nUIDL Project Content Validation Error. Please check the following:
199
+ // The component "Navbars" is not defined in the UIDL's component section.,
200
+ // The following components have different name than their key: Navbar,OneComponent,
201
+ // Root Node contains elements of type "static". It should contain only elements of type "conditional",
202
+ // Root Node contains a route that don't have a specified state: abouts.`
203
+ // )
204
+ })
205
+
206
+ it('returns error if route is missing from state definitions', () => {
207
+ const validator = new Validator()
208
+ // @ts-ignore
209
+ expect(() => validator.validateProjectContent(noRouteProjectUidlSample)).toThrow(Error)
210
+
211
+ // expect(validationResult.errorMsg).toEqual(
212
+ // `\nUIDL Project Content Validation Error. Please check the following: Route is not defined in stateDefinitions`
213
+ // )
214
+ })
215
+ })
216
+ })