@a2ui/web_core 0.9.1 → 0.10.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/.tsbuildinfo +1 -1
- package/LICENSE +25 -1
- package/README.md +1 -1
- package/package.json +1 -1
- package/src/v0_8/data/guards.d.ts +1 -1
- package/src/v0_8/data/guards.d.ts.map +1 -1
- package/src/v0_8/data/guards.js +37 -41
- package/src/v0_8/data/guards.js.map +1 -1
- package/src/v0_8/data/guards.test.js +51 -51
- package/src/v0_8/data/guards.test.js.map +1 -1
- package/src/v0_8/data/model-processor.d.ts +1 -1
- package/src/v0_8/data/model-processor.d.ts.map +1 -1
- package/src/v0_8/data/model-processor.js +85 -88
- package/src/v0_8/data/model-processor.js.map +1 -1
- package/src/v0_8/data/model-processor.test.js +288 -323
- package/src/v0_8/data/model-processor.test.js.map +1 -1
- package/src/v0_8/errors.js +5 -5
- package/src/v0_8/events/index.d.ts +2 -2
- package/src/v0_8/events/index.js +2 -2
- package/src/v0_8/events/validation-event.d.ts +4 -4
- package/src/v0_8/events/validation-event.d.ts.map +1 -1
- package/src/v0_8/events/validation-event.js +1 -1
- package/src/v0_8/events/validation-event.js.map +1 -1
- package/src/v0_8/index.d.ts +8 -8
- package/src/v0_8/index.js +9 -9
- package/src/v0_8/index.js.map +1 -1
- package/src/v0_8/schema/common-types.d.ts +4 -4
- package/src/v0_8/schema/common-types.d.ts.map +1 -1
- package/src/v0_8/schema/common-types.js +36 -84
- package/src/v0_8/schema/common-types.js.map +1 -1
- package/src/v0_8/schema/server-to-client.d.ts +562 -562
- package/src/v0_8/schema/server-to-client.d.ts.map +1 -1
- package/src/v0_8/schema/server-to-client.js +28 -38
- package/src/v0_8/schema/server-to-client.js.map +1 -1
- package/src/v0_8/schema/verify-schema.test.js +31 -43
- package/src/v0_8/schema/verify-schema.test.js.map +1 -1
- package/src/v0_8/schemas/catalog_description_schema.json +1 -5
- package/src/v0_8/schemas/client_to_server.json +2 -11
- package/src/v0_8/schemas/server_to_client_with_standard_catalog.json +3 -23
- package/src/v0_8/schemas/standard_catalog_definition.json +36 -159
- package/src/v0_8/styles/behavior.js +1 -1
- package/src/v0_8/styles/border.js +2 -2
- package/src/v0_8/styles/border.js.map +1 -1
- package/src/v0_8/styles/colors.js +13 -13
- package/src/v0_8/styles/colors.js.map +1 -1
- package/src/v0_8/styles/index.d.ts +1 -1
- package/src/v0_8/styles/index.d.ts.map +1 -1
- package/src/v0_8/styles/index.js +10 -18
- package/src/v0_8/styles/index.js.map +1 -1
- package/src/v0_8/styles/layout.d.ts.map +1 -1
- package/src/v0_8/styles/layout.js +10 -12
- package/src/v0_8/styles/layout.js.map +1 -1
- package/src/v0_8/styles/opacity.js +1 -1
- package/src/v0_8/styles/styles.test.js +43 -43
- package/src/v0_8/styles/styles.test.js.map +1 -1
- package/src/v0_8/styles/type.js +1 -1
- package/src/v0_8/styles/utils.d.ts +1 -1
- package/src/v0_8/styles/utils.d.ts.map +1 -1
- package/src/v0_8/styles/utils.js +4 -4
- package/src/v0_8/styles/utils.js.map +1 -1
- package/src/v0_8/types/client-event.d.ts.map +1 -1
- package/src/v0_8/types/colors.d.ts +13 -13
- package/src/v0_8/types/colors.d.ts.map +1 -1
- package/src/v0_8/types/components.d.ts +2 -2
- package/src/v0_8/types/components.d.ts.map +1 -1
- package/src/v0_8/types/primitives.d.ts +2 -2
- package/src/v0_8/types/primitives.d.ts.map +1 -1
- package/src/v0_8/types/types.d.ts +32 -32
- package/src/v0_8/types/types.d.ts.map +1 -1
- package/src/v0_9/basic_catalog/components/basic_components.d.ts +84 -68
- package/src/v0_9/basic_catalog/components/basic_components.d.ts.map +1 -1
- package/src/v0_9/basic_catalog/components/basic_components.js +17 -46
- package/src/v0_9/basic_catalog/components/basic_components.js.map +1 -1
- package/src/v0_9/basic_catalog/expressions/expression_parser.d.ts.map +1 -1
- package/src/v0_9/basic_catalog/expressions/expression_parser.js +5 -11
- package/src/v0_9/basic_catalog/expressions/expression_parser.js.map +1 -1
- package/src/v0_9/basic_catalog/expressions/expression_parser.test.js +6 -26
- package/src/v0_9/basic_catalog/expressions/expression_parser.test.js.map +1 -1
- package/src/v0_9/basic_catalog/functions/basic_functions.d.ts.map +1 -1
- package/src/v0_9/basic_catalog/functions/basic_functions.js.map +1 -1
- package/src/v0_9/basic_catalog/functions/basic_functions.test.js +1 -3
- package/src/v0_9/basic_catalog/functions/basic_functions.test.js.map +1 -1
- package/src/v0_9/basic_catalog/functions/basic_functions_api.d.ts.map +1 -1
- package/src/v0_9/basic_catalog/functions/basic_functions_api.js.map +1 -1
- package/src/v0_9/basic_catalog/index.d.ts +2 -1
- package/src/v0_9/basic_catalog/index.d.ts.map +1 -1
- package/src/v0_9/basic_catalog/index.js +1 -1
- package/src/v0_9/basic_catalog/index.js.map +1 -1
- package/src/v0_9/basic_catalog/styles/default.d.ts +34 -0
- package/src/v0_9/basic_catalog/styles/default.d.ts.map +1 -1
- package/src/v0_9/basic_catalog/styles/default.js +29 -6
- package/src/v0_9/basic_catalog/styles/default.js.map +1 -1
- package/src/v0_9/catalog/types.d.ts.map +1 -1
- package/src/v0_9/catalog/types.js.map +1 -1
- package/src/v0_9/catalog/types.test.js.map +1 -1
- package/src/v0_9/processing/message-processor.d.ts.map +1 -1
- package/src/v0_9/processing/message-processor.js +1 -2
- package/src/v0_9/processing/message-processor.js.map +1 -1
- package/src/v0_9/processing/message-processor.test.js +7 -19
- package/src/v0_9/processing/message-processor.test.js.map +1 -1
- package/src/v0_9/reactivity/signals.test.d.ts.map +1 -1
- package/src/v0_9/reactivity/signals.test.js +1 -1
- package/src/v0_9/reactivity/signals.test.js.map +1 -1
- package/src/v0_9/rendering/component-context.d.ts.map +1 -1
- package/src/v0_9/rendering/component-context.js.map +1 -1
- package/src/v0_9/rendering/data-context.d.ts.map +1 -1
- package/src/v0_9/rendering/data-context.js.map +1 -1
- package/src/v0_9/rendering/data-context.test.js +1 -4
- package/src/v0_9/rendering/data-context.test.js.map +1 -1
- package/src/v0_9/rendering/generic-binder.d.ts.map +1 -1
- package/src/v0_9/rendering/generic-binder.js +9 -17
- package/src/v0_9/rendering/generic-binder.js.map +1 -1
- package/src/v0_9/rendering/generic-binder.test.js +3 -9
- package/src/v0_9/rendering/generic-binder.test.js.map +1 -1
- package/src/v0_9/schema/client-capabilities.d.ts.map +1 -1
- package/src/v0_9/schema/client-to-server.d.ts +52 -52
- package/src/v0_9/schema/client-to-server.d.ts.map +1 -1
- package/src/v0_9/schema/client-to-server.js +7 -24
- package/src/v0_9/schema/client-to-server.js.map +1 -1
- package/src/v0_9/schema/client-to-server.test.js +1 -1
- package/src/v0_9/schema/client-to-server.test.js.map +1 -1
- package/src/v0_9/schema/common-types.d.ts +2 -0
- package/src/v0_9/schema/common-types.d.ts.map +1 -1
- package/src/v0_9/schema/common-types.js +4 -13
- package/src/v0_9/schema/common-types.js.map +1 -1
- package/src/v0_9/schema/server-to-client.d.ts +28 -28
- package/src/v0_9/schema/server-to-client.d.ts.map +1 -1
- package/src/v0_9/schema/server-to-client.js +6 -19
- package/src/v0_9/schema/server-to-client.js.map +1 -1
- package/src/v0_9/schema/verify-schema.test.js +4 -16
- package/src/v0_9/schema/verify-schema.test.js.map +1 -1
- package/src/v0_9/schemas/basic_catalog.json +6 -10
- package/src/v0_9/schemas/client_capabilities.json +1 -9
- package/src/v0_9/schemas/client_to_server.json +1 -7
- package/src/v0_9/schemas/client_to_server_list_wrapper.json +1 -3
- package/src/v0_9/schemas/common_types.json +2 -12
- package/src/v0_9/schemas/server_to_client.json +1 -1
- package/src/v0_9/schemas/server_to_client_list_wrapper.json +1 -3
- package/src/v0_9/state/data-model.d.ts.map +1 -1
- package/src/v0_9/state/data-model.js +1 -2
- package/src/v0_9/state/data-model.js.map +1 -1
- package/src/v0_9/state/data-model.test.js.map +1 -1
- package/src/v0_9/state/surface-components-model.d.ts.map +1 -1
- package/src/v0_9/state/surface-components-model.js.map +1 -1
- package/src/v0_9/state/surface-group-model.d.ts.map +1 -1
- package/src/v0_9/state/surface-group-model.js.map +1 -1
- package/src/v0_9/state/surface-model.d.ts +1 -1
- package/src/v0_9/state/surface-model.d.ts.map +1 -1
- package/src/v0_9/state/surface-model.js +2 -5
- package/src/v0_9/state/surface-model.js.map +1 -1
- package/src/v0_9/state/surface-model.test.js.map +1 -1
- package/src/v0_9/test/function_execution.spec.js.map +1 -1
- package/src/v0_9/test/test-utils.d.ts.map +1 -1
- package/src/v0_9/test/test-utils.js.map +1 -1
|
@@ -13,139 +13,143 @@
|
|
|
13
13
|
* See the License for the specific language governing permissions and
|
|
14
14
|
* limitations under the License.
|
|
15
15
|
*/
|
|
16
|
-
import assert from
|
|
17
|
-
import { describe, it, beforeEach } from
|
|
18
|
-
import { A2uiMessageProcessor } from
|
|
19
|
-
describe(
|
|
16
|
+
import assert from 'node:assert';
|
|
17
|
+
import { describe, it, beforeEach } from 'node:test';
|
|
18
|
+
import { A2uiMessageProcessor } from './model-processor.js';
|
|
19
|
+
describe('A2uiMessageProcessor', () => {
|
|
20
20
|
let processor;
|
|
21
21
|
beforeEach(() => {
|
|
22
22
|
processor = new A2uiMessageProcessor();
|
|
23
23
|
});
|
|
24
|
-
it(
|
|
24
|
+
it('handles beginRendering', () => {
|
|
25
25
|
processor.processMessages([
|
|
26
26
|
{
|
|
27
27
|
beginRendering: {
|
|
28
|
-
surfaceId:
|
|
29
|
-
root:
|
|
30
|
-
styles: { font:
|
|
28
|
+
surfaceId: 's1',
|
|
29
|
+
root: 'root',
|
|
30
|
+
styles: { font: 'Arial' },
|
|
31
31
|
},
|
|
32
32
|
},
|
|
33
33
|
]);
|
|
34
34
|
const surfaces = processor.getSurfaces();
|
|
35
|
-
const surface = surfaces.get(
|
|
35
|
+
const surface = surfaces.get('s1');
|
|
36
36
|
assert.ok(surface);
|
|
37
|
-
assert.strictEqual(surface.rootComponentId,
|
|
38
|
-
assert.deepStrictEqual(surface.styles, { font:
|
|
37
|
+
assert.strictEqual(surface.rootComponentId, 'root');
|
|
38
|
+
assert.deepStrictEqual(surface.styles, { font: 'Arial' });
|
|
39
39
|
// The component tree remains null until components are added via surfaceUpdate.
|
|
40
40
|
assert.strictEqual(surface.componentTree, null);
|
|
41
41
|
});
|
|
42
|
-
it(
|
|
42
|
+
it('handles surfaceUpdate', () => {
|
|
43
43
|
processor.processMessages([
|
|
44
44
|
{
|
|
45
|
-
beginRendering: { surfaceId:
|
|
45
|
+
beginRendering: { surfaceId: 's1', root: 'root' },
|
|
46
46
|
},
|
|
47
47
|
]);
|
|
48
48
|
processor.processMessages([
|
|
49
49
|
{
|
|
50
50
|
surfaceUpdate: {
|
|
51
|
-
surfaceId:
|
|
51
|
+
surfaceId: 's1',
|
|
52
52
|
components: [
|
|
53
53
|
{
|
|
54
|
-
id:
|
|
54
|
+
id: 'root',
|
|
55
55
|
component: {
|
|
56
|
-
Text: { text: { literal:
|
|
56
|
+
Text: { text: { literal: 'Hello' }, usageHint: 'body' },
|
|
57
57
|
},
|
|
58
58
|
},
|
|
59
59
|
],
|
|
60
60
|
},
|
|
61
61
|
},
|
|
62
62
|
]);
|
|
63
|
-
const surface = processor.getSurfaces().get(
|
|
63
|
+
const surface = processor.getSurfaces().get('s1');
|
|
64
64
|
assert.ok(surface);
|
|
65
65
|
assert.ok(surface.componentTree);
|
|
66
66
|
const root = surface.componentTree;
|
|
67
|
-
assert.strictEqual(root.id,
|
|
68
|
-
assert.strictEqual(root.type,
|
|
67
|
+
assert.strictEqual(root.id, 'root');
|
|
68
|
+
assert.strictEqual(root.type, 'Text');
|
|
69
69
|
// The property preserves the literal wrapper
|
|
70
|
-
assert.deepStrictEqual(root.properties.text, { literal:
|
|
70
|
+
assert.deepStrictEqual(root.properties.text, { literal: 'Hello' });
|
|
71
71
|
});
|
|
72
|
-
it(
|
|
72
|
+
it('handles dataModelUpdate', () => {
|
|
73
73
|
processor.processMessages([
|
|
74
74
|
{
|
|
75
|
-
beginRendering: { surfaceId:
|
|
75
|
+
beginRendering: { surfaceId: 's1', root: 'root' },
|
|
76
76
|
},
|
|
77
77
|
{
|
|
78
78
|
dataModelUpdate: {
|
|
79
|
-
surfaceId:
|
|
80
|
-
contents: [{ key:
|
|
79
|
+
surfaceId: 's1',
|
|
80
|
+
contents: [{ key: 'message', valueString: 'World' }],
|
|
81
81
|
},
|
|
82
82
|
},
|
|
83
83
|
]);
|
|
84
|
-
const surface = processor.getSurfaces().get(
|
|
85
|
-
assert.strictEqual(surface?.dataModel.get(
|
|
84
|
+
const surface = processor.getSurfaces().get('s1');
|
|
85
|
+
assert.strictEqual(surface?.dataModel.get('message'), 'World');
|
|
86
86
|
});
|
|
87
|
-
it(
|
|
87
|
+
it('handles deleteSurface', () => {
|
|
88
88
|
processor.processMessages([
|
|
89
89
|
{
|
|
90
|
-
beginRendering: { surfaceId:
|
|
90
|
+
beginRendering: { surfaceId: 's1', root: 'root' },
|
|
91
91
|
},
|
|
92
92
|
]);
|
|
93
|
-
assert.ok(processor.getSurfaces().has(
|
|
93
|
+
assert.ok(processor.getSurfaces().has('s1'));
|
|
94
94
|
processor.processMessages([
|
|
95
95
|
{
|
|
96
|
-
deleteSurface: { surfaceId:
|
|
96
|
+
deleteSurface: { surfaceId: 's1' },
|
|
97
97
|
},
|
|
98
98
|
]);
|
|
99
|
-
assert.ok(!processor.getSurfaces().has(
|
|
99
|
+
assert.ok(!processor.getSurfaces().has('s1'));
|
|
100
100
|
});
|
|
101
|
-
it(
|
|
101
|
+
it('resolves component references (children)', () => {
|
|
102
102
|
processor.processMessages([
|
|
103
|
-
{ beginRendering: { surfaceId:
|
|
103
|
+
{ beginRendering: { surfaceId: 's1', root: 'row' } },
|
|
104
104
|
{
|
|
105
105
|
surfaceUpdate: {
|
|
106
|
-
surfaceId:
|
|
106
|
+
surfaceId: 's1',
|
|
107
107
|
components: [
|
|
108
108
|
{
|
|
109
|
-
id:
|
|
109
|
+
id: 'row',
|
|
110
110
|
component: {
|
|
111
|
-
Row: { children: { explicitList: [
|
|
111
|
+
Row: { children: { explicitList: ['t1', 't2'] } },
|
|
112
112
|
},
|
|
113
113
|
},
|
|
114
114
|
{
|
|
115
|
-
id:
|
|
115
|
+
id: 't1',
|
|
116
116
|
component: {
|
|
117
|
-
Text: { text: { literal:
|
|
117
|
+
Text: { text: { literal: 'One' }, usageHint: 'body' },
|
|
118
118
|
},
|
|
119
119
|
},
|
|
120
120
|
{
|
|
121
|
-
id:
|
|
121
|
+
id: 't2',
|
|
122
122
|
component: {
|
|
123
|
-
Text: { text: { literal:
|
|
123
|
+
Text: { text: { literal: 'Two' }, usageHint: 'body' },
|
|
124
124
|
},
|
|
125
125
|
},
|
|
126
126
|
],
|
|
127
127
|
},
|
|
128
128
|
},
|
|
129
129
|
]);
|
|
130
|
-
const surface = processor.getSurfaces().get(
|
|
130
|
+
const surface = processor.getSurfaces().get('s1');
|
|
131
131
|
const root = surface?.componentTree;
|
|
132
|
-
assert.strictEqual(root.type,
|
|
132
|
+
assert.strictEqual(root.type, 'Row');
|
|
133
133
|
assert.strictEqual(root.properties.children.length, 2);
|
|
134
|
-
assert.deepStrictEqual(root.properties.children[0].properties.text, {
|
|
135
|
-
|
|
134
|
+
assert.deepStrictEqual(root.properties.children[0].properties.text, {
|
|
135
|
+
literal: 'One',
|
|
136
|
+
});
|
|
137
|
+
assert.deepStrictEqual(root.properties.children[1].properties.text, {
|
|
138
|
+
literal: 'Two',
|
|
139
|
+
});
|
|
136
140
|
});
|
|
137
|
-
it(
|
|
141
|
+
it('resolves templates', () => {
|
|
138
142
|
processor.processMessages([
|
|
139
|
-
{ beginRendering: { surfaceId:
|
|
143
|
+
{ beginRendering: { surfaceId: 's1', root: 'row' } },
|
|
140
144
|
{
|
|
141
145
|
dataModelUpdate: {
|
|
142
|
-
surfaceId:
|
|
146
|
+
surfaceId: 's1',
|
|
143
147
|
contents: [
|
|
144
148
|
{
|
|
145
|
-
key:
|
|
149
|
+
key: 'items',
|
|
146
150
|
valueMap: [
|
|
147
|
-
{ key:
|
|
148
|
-
{ key:
|
|
151
|
+
{ key: '0', valueString: 'Item A' },
|
|
152
|
+
{ key: '1', valueString: 'Item B' },
|
|
149
153
|
],
|
|
150
154
|
},
|
|
151
155
|
],
|
|
@@ -153,40 +157,40 @@ describe("A2uiMessageProcessor", () => {
|
|
|
153
157
|
},
|
|
154
158
|
{
|
|
155
159
|
surfaceUpdate: {
|
|
156
|
-
surfaceId:
|
|
160
|
+
surfaceId: 's1',
|
|
157
161
|
components: [
|
|
158
162
|
{
|
|
159
|
-
id:
|
|
163
|
+
id: 'row',
|
|
160
164
|
component: {
|
|
161
165
|
Row: {
|
|
162
166
|
children: {
|
|
163
167
|
template: {
|
|
164
|
-
componentId:
|
|
165
|
-
dataBinding:
|
|
168
|
+
componentId: 'item',
|
|
169
|
+
dataBinding: '/items',
|
|
166
170
|
},
|
|
167
171
|
},
|
|
168
172
|
},
|
|
169
173
|
},
|
|
170
174
|
},
|
|
171
175
|
{
|
|
172
|
-
id:
|
|
176
|
+
id: 'item',
|
|
173
177
|
component: {
|
|
174
|
-
Text: { text: { path:
|
|
178
|
+
Text: { text: { path: '.' }, usageHint: 'body' },
|
|
175
179
|
},
|
|
176
180
|
},
|
|
177
181
|
],
|
|
178
182
|
},
|
|
179
183
|
},
|
|
180
184
|
]);
|
|
181
|
-
const surface = processor.getSurfaces().get(
|
|
185
|
+
const surface = processor.getSurfaces().get('s1');
|
|
182
186
|
const root = surface?.componentTree;
|
|
183
|
-
assert.strictEqual(root.type,
|
|
187
|
+
assert.strictEqual(root.type, 'Row');
|
|
184
188
|
assert.strictEqual(root.properties.children.length, 2);
|
|
185
189
|
// Verify template expansion
|
|
186
190
|
const child0 = root.properties.children[0];
|
|
187
191
|
const child1 = root.properties.children[1];
|
|
188
192
|
// Check that binding paths are correct (processor does NOT resolve the value, just the binding path context)
|
|
189
|
-
assert.deepStrictEqual(child0.properties.text, { path:
|
|
193
|
+
assert.deepStrictEqual(child0.properties.text, { path: '.' });
|
|
190
194
|
// Now verify we can resolve the data using the node's context
|
|
191
195
|
const textProp0 = child0.properties.text;
|
|
192
196
|
const resolvedValue0 = processor.getData(child0, textProp0.path, 's1');
|
|
@@ -195,178 +199,162 @@ describe("A2uiMessageProcessor", () => {
|
|
|
195
199
|
const resolvedValue1 = processor.getData(child1, textProp1.path, 's1');
|
|
196
200
|
assert.strictEqual(resolvedValue1, 'Item B');
|
|
197
201
|
});
|
|
198
|
-
it(
|
|
202
|
+
it('getData resolves paths relative to node context', () => {
|
|
199
203
|
processor.processMessages([
|
|
200
|
-
{ beginRendering: { surfaceId:
|
|
204
|
+
{ beginRendering: { surfaceId: 's1', root: 'root' } },
|
|
201
205
|
{
|
|
202
206
|
dataModelUpdate: {
|
|
203
|
-
surfaceId:
|
|
204
|
-
contents: [
|
|
205
|
-
{ key: "user", valueMap: [{ key: "name", valueString: "Alice" }] },
|
|
206
|
-
],
|
|
207
|
+
surfaceId: 's1',
|
|
208
|
+
contents: [{ key: 'user', valueMap: [{ key: 'name', valueString: 'Alice' }] }],
|
|
207
209
|
},
|
|
208
210
|
},
|
|
209
211
|
]);
|
|
210
|
-
processor.getSurfaces().get(
|
|
211
|
-
const node = { id:
|
|
212
|
-
const name = processor.getData(node,
|
|
213
|
-
assert.strictEqual(name,
|
|
214
|
-
const self = processor.getData(node,
|
|
212
|
+
processor.getSurfaces().get('s1');
|
|
213
|
+
const node = { id: 'test', dataContextPath: '/user' };
|
|
214
|
+
const name = processor.getData(node, 'name', 's1');
|
|
215
|
+
assert.strictEqual(name, 'Alice');
|
|
216
|
+
const self = processor.getData(node, '.', 's1');
|
|
215
217
|
assert.ok(self instanceof Map);
|
|
216
|
-
assert.strictEqual(self.get(
|
|
217
|
-
const rootData = processor.getData(node,
|
|
218
|
-
assert.strictEqual(rootData,
|
|
218
|
+
assert.strictEqual(self.get('name'), 'Alice');
|
|
219
|
+
const rootData = processor.getData(node, '/user/name', 's1');
|
|
220
|
+
assert.strictEqual(rootData, 'Alice');
|
|
219
221
|
});
|
|
220
|
-
it(
|
|
221
|
-
processor.processMessages([
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
processor.setData(node,
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
const nested = surface?.dataModel.get("nested");
|
|
230
|
-
assert.strictEqual(nested.get("value"), "foo");
|
|
222
|
+
it('setData updates data model', () => {
|
|
223
|
+
processor.processMessages([{ beginRendering: { surfaceId: 's1', root: 'root' } }]);
|
|
224
|
+
const surface = processor.getSurfaces().get('s1');
|
|
225
|
+
const node = { id: 'test', dataContextPath: '/' };
|
|
226
|
+
processor.setData(node, 'count', 42, 's1');
|
|
227
|
+
assert.strictEqual(surface?.dataModel.get('count'), 42);
|
|
228
|
+
processor.setData(node, '/nested/value', 'foo', 's1');
|
|
229
|
+
const nested = surface?.dataModel.get('nested');
|
|
230
|
+
assert.strictEqual(nested.get('value'), 'foo');
|
|
231
231
|
});
|
|
232
|
-
it(
|
|
233
|
-
const p = processor.normalizePath(
|
|
234
|
-
assert.strictEqual(p,
|
|
232
|
+
it('normalizes paths correctly', () => {
|
|
233
|
+
const p = processor.normalizePath('users[0].name');
|
|
234
|
+
assert.strictEqual(p, '/users/0/name');
|
|
235
235
|
});
|
|
236
|
-
it(
|
|
237
|
-
processor.processMessages([
|
|
238
|
-
{ beginRendering: { surfaceId: "s1", root: "root" } },
|
|
239
|
-
]);
|
|
236
|
+
it('parses JSON strings in data', () => {
|
|
237
|
+
processor.processMessages([{ beginRendering: { surfaceId: 's1', root: 'root' } }]);
|
|
240
238
|
// Explicitly testing private/internal parsing logic via public update
|
|
241
239
|
processor.processMessages([
|
|
242
240
|
{
|
|
243
241
|
dataModelUpdate: {
|
|
244
|
-
surfaceId:
|
|
245
|
-
contents: [{ key:
|
|
242
|
+
surfaceId: 's1',
|
|
243
|
+
contents: [{ key: 'config', valueString: '{"theme":"dark"}' }],
|
|
246
244
|
},
|
|
247
245
|
},
|
|
248
246
|
]);
|
|
249
|
-
const surface = processor.getSurfaces().get(
|
|
250
|
-
const config = surface?.dataModel.get(
|
|
251
|
-
assert.deepStrictEqual(config, { theme:
|
|
247
|
+
const surface = processor.getSurfaces().get('s1');
|
|
248
|
+
const config = surface?.dataModel.get('config');
|
|
249
|
+
assert.deepStrictEqual(config, { theme: 'dark' });
|
|
252
250
|
});
|
|
253
|
-
it(
|
|
251
|
+
it('test basic edge cases and internal fallbacks', () => {
|
|
254
252
|
// 1. clearSurfaces
|
|
255
|
-
processor.processMessages([
|
|
256
|
-
{ beginRendering: { surfaceId: "s1", root: "root" } },
|
|
257
|
-
]);
|
|
253
|
+
processor.processMessages([{ beginRendering: { surfaceId: 's1', root: 'root' } }]);
|
|
258
254
|
assert.strictEqual(processor.getSurfaces().size, 1);
|
|
259
255
|
processor.clearSurfaces();
|
|
260
256
|
assert.strictEqual(processor.getSurfaces().size, 0);
|
|
261
257
|
// 2. setData with null node
|
|
262
|
-
processor.processMessages([
|
|
263
|
-
{ beginRendering: { surfaceId: "s1", root: "root" } },
|
|
264
|
-
]);
|
|
258
|
+
processor.processMessages([{ beginRendering: { surfaceId: 's1', root: 'root' } }]);
|
|
265
259
|
// Shouldn't throw
|
|
266
|
-
processor.setData(null,
|
|
260
|
+
processor.setData(null, 'foo', 'bar');
|
|
267
261
|
// 3. setData with default data context (default to /)
|
|
268
|
-
const node = { id:
|
|
269
|
-
processor.setData(node,
|
|
270
|
-
const surface = processor.getSurfaces().get(
|
|
271
|
-
assert.strictEqual(surface.dataModel.get(
|
|
262
|
+
const node = { id: 'test', dataContextPath: undefined };
|
|
263
|
+
processor.setData(node, '.', 'value');
|
|
264
|
+
const surface = processor.getSurfaces().get('s1');
|
|
265
|
+
assert.strictEqual(surface.dataModel.get('.'), undefined); // Normal setDataByPath logic when root=/
|
|
272
266
|
// 4. parseIfJsonString with invalid JSON layout
|
|
273
267
|
processor.processMessages([
|
|
274
268
|
{
|
|
275
269
|
dataModelUpdate: {
|
|
276
|
-
surfaceId:
|
|
277
|
-
contents: [{ key:
|
|
270
|
+
surfaceId: 's1',
|
|
271
|
+
contents: [{ key: 'badJson', valueString: '{bad" }' }],
|
|
278
272
|
},
|
|
279
273
|
},
|
|
280
274
|
]);
|
|
281
|
-
assert.strictEqual(surface.dataModel.get(
|
|
275
|
+
assert.strictEqual(surface.dataModel.get('badJson'), '{bad" }'); // Returns original
|
|
282
276
|
// 5. convertKeyValueArrayToMap with missing valueKey
|
|
283
277
|
// Explicit array pass directly to internal mapping simulation
|
|
284
|
-
processor.setDataByPath(surface.dataModel,
|
|
285
|
-
{ key:
|
|
278
|
+
processor.setDataByPath(surface.dataModel, 'malformed', [
|
|
279
|
+
{ key: 'foo', unknownKey: 'bar' }, // missing value map/string etc
|
|
286
280
|
]);
|
|
287
|
-
const malformed = surface.dataModel.get(
|
|
288
|
-
assert.strictEqual(malformed.has(
|
|
281
|
+
const malformed = surface.dataModel.get('malformed');
|
|
282
|
+
assert.strictEqual(malformed.has('foo'), false); // Skips processing
|
|
289
283
|
// 6. object normalization at root
|
|
290
|
-
processor.setDataByPath(surface.dataModel,
|
|
291
|
-
plain:
|
|
284
|
+
processor.setDataByPath(surface.dataModel, '/', {
|
|
285
|
+
plain: 'object',
|
|
292
286
|
});
|
|
293
|
-
assert.strictEqual(surface.dataModel.get(
|
|
287
|
+
assert.strictEqual(surface.dataModel.get('plain'), 'object');
|
|
294
288
|
// 7. non-map/object root fails gracefully
|
|
295
|
-
processor.setDataByPath(surface.dataModel,
|
|
289
|
+
processor.setDataByPath(surface.dataModel, '/', 'stringroot');
|
|
296
290
|
// Doesn't explode, just prints error internally
|
|
297
291
|
});
|
|
298
|
-
it(
|
|
299
|
-
processor.processMessages([
|
|
300
|
-
|
|
301
|
-
]);
|
|
302
|
-
const surface = processor.getSurfaces().get("s1");
|
|
292
|
+
it('test array set operations and invalid primitive traversal', () => {
|
|
293
|
+
processor.processMessages([{ beginRendering: { surfaceId: 's1', root: 'root' } }]);
|
|
294
|
+
const surface = processor.getSurfaces().get('s1');
|
|
303
295
|
// Manually set an array to avoid empty array being converted to Map
|
|
304
|
-
surface.dataModel.set(
|
|
296
|
+
surface.dataModel.set('list', ['dummy']);
|
|
305
297
|
// Now it's an array at /list, set something deep inside it
|
|
306
|
-
processor.setDataByPath(surface.dataModel,
|
|
298
|
+
processor.setDataByPath(surface.dataModel, '/list/0/name', 'Alice');
|
|
307
299
|
// Also test setting a value directly on an array index
|
|
308
|
-
processor.setDataByPath(surface.dataModel,
|
|
309
|
-
const list = surface.dataModel.get(
|
|
310
|
-
assert.strictEqual(list[0].get(
|
|
311
|
-
assert.strictEqual(list[1],
|
|
300
|
+
processor.setDataByPath(surface.dataModel, '/list/1', 'Bob');
|
|
301
|
+
const list = surface.dataModel.get('list');
|
|
302
|
+
assert.strictEqual(list[0].get('name'), 'Alice'); // It creates a Map for `name`
|
|
303
|
+
assert.strictEqual(list[1], 'Bob');
|
|
312
304
|
// Test getDataByPath failing traversal over a primitive
|
|
313
|
-
processor.setDataByPath(surface.dataModel,
|
|
314
|
-
const result = processor.getDataByPath(surface.dataModel,
|
|
305
|
+
processor.setDataByPath(surface.dataModel, '/primitive', 'hello');
|
|
306
|
+
const result = processor.getDataByPath(surface.dataModel, '/primitive/invalid');
|
|
315
307
|
assert.strictEqual(result, null);
|
|
316
308
|
});
|
|
317
|
-
it(
|
|
318
|
-
processor.processMessages([
|
|
319
|
-
|
|
320
|
-
]);
|
|
321
|
-
const surface = processor.getSurfaces().get("s_tree");
|
|
309
|
+
it('test tree rebuilding edge cases', () => {
|
|
310
|
+
processor.processMessages([{ beginRendering: { surfaceId: 's_tree', root: 'root' } }]);
|
|
311
|
+
const surface = processor.getSurfaces().get('s_tree');
|
|
322
312
|
// 1. rebuildComponentTree without rootComponentId
|
|
323
313
|
surface.rootComponentId = null;
|
|
324
314
|
processor.rebuildComponentTree(surface);
|
|
325
315
|
assert.strictEqual(surface.componentTree, null);
|
|
326
316
|
// 2. Circular dependency
|
|
327
|
-
surface.rootComponentId =
|
|
328
|
-
surface.components.set(
|
|
329
|
-
id:
|
|
330
|
-
component: { Row: { children: [
|
|
317
|
+
surface.rootComponentId = 'circleA';
|
|
318
|
+
surface.components.set('circleA', {
|
|
319
|
+
id: 'circleA',
|
|
320
|
+
component: { Row: { children: ['circleB'] } },
|
|
331
321
|
});
|
|
332
|
-
surface.components.set(
|
|
333
|
-
id:
|
|
334
|
-
component: { Row: { children: [
|
|
322
|
+
surface.components.set('circleB', {
|
|
323
|
+
id: 'circleB',
|
|
324
|
+
component: { Row: { children: ['circleA'] } },
|
|
335
325
|
});
|
|
336
326
|
assert.throws(() => {
|
|
337
327
|
processor.rebuildComponentTree(surface);
|
|
338
328
|
}, /Circular dependency/);
|
|
339
329
|
});
|
|
340
|
-
it(
|
|
341
|
-
processor.processMessages([
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
const surface = processor.getSurfaces().get("s_bad_comp");
|
|
345
|
-
surface.rootComponentId = "bad";
|
|
330
|
+
it('throws A2uiValidationError for malformed components', () => {
|
|
331
|
+
processor.processMessages([{ beginRendering: { surfaceId: `s_bad_comp`, root: 'bad' } }]);
|
|
332
|
+
const surface = processor.getSurfaces().get('s_bad_comp');
|
|
333
|
+
surface.rootComponentId = 'bad';
|
|
346
334
|
// Divider is omitted here because it has no required fields, so `{}` is valid.
|
|
347
335
|
const types = [
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
336
|
+
'Text',
|
|
337
|
+
'Image',
|
|
338
|
+
'Icon',
|
|
339
|
+
'Video',
|
|
340
|
+
'AudioPlayer',
|
|
341
|
+
'Row',
|
|
342
|
+
'Column',
|
|
343
|
+
'List',
|
|
344
|
+
'Card',
|
|
345
|
+
'Tabs',
|
|
346
|
+
'Modal',
|
|
347
|
+
'Button',
|
|
348
|
+
'CheckBox',
|
|
349
|
+
'TextField',
|
|
350
|
+
'DateTimeInput',
|
|
351
|
+
'MultipleChoice',
|
|
352
|
+
'Slider',
|
|
365
353
|
];
|
|
366
354
|
for (const type of types) {
|
|
367
355
|
assert.throws(() => {
|
|
368
|
-
surface.components.set(
|
|
369
|
-
id:
|
|
356
|
+
surface.components.set('bad', {
|
|
357
|
+
id: 'bad',
|
|
370
358
|
component: {
|
|
371
359
|
[type]: {
|
|
372
360
|
/* missing required fields */
|
|
@@ -377,334 +365,311 @@ describe("A2uiMessageProcessor", () => {
|
|
|
377
365
|
}, /Invalid data; expected/);
|
|
378
366
|
}
|
|
379
367
|
// Default catch-all (doesn't throw, just passes it through)
|
|
380
|
-
surface.components.set(
|
|
381
|
-
id:
|
|
382
|
-
component: { CustomWidget: { foo:
|
|
368
|
+
surface.components.set('bad', {
|
|
369
|
+
id: 'bad',
|
|
370
|
+
component: { CustomWidget: { foo: 'bar' } },
|
|
383
371
|
});
|
|
384
372
|
processor.rebuildComponentTree(surface);
|
|
385
|
-
assert.strictEqual(surface.componentTree.type,
|
|
373
|
+
assert.strictEqual(surface.componentTree.type, 'CustomWidget');
|
|
386
374
|
});
|
|
387
|
-
it(
|
|
375
|
+
it('resolves Template with Array data', () => {
|
|
388
376
|
processor.processMessages([
|
|
389
|
-
{ beginRendering: { surfaceId:
|
|
377
|
+
{ beginRendering: { surfaceId: 's3_arr', root: 'list' } },
|
|
390
378
|
{
|
|
391
379
|
dataModelUpdate: {
|
|
392
|
-
surfaceId:
|
|
393
|
-
path:
|
|
380
|
+
surfaceId: 's3_arr',
|
|
381
|
+
path: '/items',
|
|
394
382
|
contents: [
|
|
395
|
-
{ key:
|
|
396
|
-
{ key:
|
|
383
|
+
{ key: '0', valueString: 'a' },
|
|
384
|
+
{ key: '1', valueString: 'b' },
|
|
397
385
|
],
|
|
398
386
|
},
|
|
399
387
|
},
|
|
400
388
|
]);
|
|
401
|
-
const surface = processor.getSurfaces().get(
|
|
402
|
-
surface.components.set(
|
|
403
|
-
id:
|
|
389
|
+
const surface = processor.getSurfaces().get('s3_arr');
|
|
390
|
+
surface.components.set('list', {
|
|
391
|
+
id: 'list',
|
|
404
392
|
component: {
|
|
405
393
|
List: {
|
|
406
394
|
children: {
|
|
407
|
-
template: { dataBinding:
|
|
395
|
+
template: { dataBinding: '/items', componentId: 'item' },
|
|
408
396
|
},
|
|
409
397
|
},
|
|
410
398
|
},
|
|
411
399
|
});
|
|
412
|
-
surface.components.set(
|
|
413
|
-
id:
|
|
400
|
+
surface.components.set('item', {
|
|
401
|
+
id: 'item',
|
|
414
402
|
component: {
|
|
415
|
-
Text: { text: { literalString:
|
|
403
|
+
Text: { text: { literalString: 'hello' }, usageHint: 'body' },
|
|
416
404
|
},
|
|
417
405
|
});
|
|
418
406
|
processor.rebuildComponentTree(surface);
|
|
419
407
|
const root = surface.componentTree;
|
|
420
408
|
assert.strictEqual(root.properties.children.length, 2);
|
|
421
|
-
assert.strictEqual(root.properties.children[0].id,
|
|
422
|
-
assert.strictEqual(root.properties.children[0].dataContextPath,
|
|
423
|
-
assert.strictEqual(root.properties.children[1].id,
|
|
424
|
-
assert.strictEqual(root.properties.children[1].dataContextPath,
|
|
409
|
+
assert.strictEqual(root.properties.children[0].id, 'item:0');
|
|
410
|
+
assert.strictEqual(root.properties.children[0].dataContextPath, '/items/0');
|
|
411
|
+
assert.strictEqual(root.properties.children[1].id, 'item:1');
|
|
412
|
+
assert.strictEqual(root.properties.children[1].dataContextPath, '/items/1');
|
|
425
413
|
});
|
|
426
|
-
it(
|
|
427
|
-
processor.processMessages([
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
surface.components.set("list", {
|
|
432
|
-
id: "list",
|
|
414
|
+
it('resolves Template with undefined/null data as empty array', () => {
|
|
415
|
+
processor.processMessages([{ beginRendering: { surfaceId: 's3_null', root: 'list' } }]);
|
|
416
|
+
const surface = processor.getSurfaces().get('s3_null');
|
|
417
|
+
surface.components.set('list', {
|
|
418
|
+
id: 'list',
|
|
433
419
|
component: {
|
|
434
420
|
List: {
|
|
435
421
|
children: {
|
|
436
|
-
template: { dataBinding:
|
|
422
|
+
template: { dataBinding: '/missingItems', componentId: 'item' },
|
|
437
423
|
},
|
|
438
424
|
},
|
|
439
425
|
},
|
|
440
426
|
});
|
|
441
|
-
surface.components.set(
|
|
442
|
-
id:
|
|
427
|
+
surface.components.set('item', {
|
|
428
|
+
id: 'item',
|
|
443
429
|
component: {
|
|
444
|
-
Text: { text: { literalString:
|
|
430
|
+
Text: { text: { literalString: 'hello' }, usageHint: 'body' },
|
|
445
431
|
},
|
|
446
432
|
});
|
|
447
433
|
processor.rebuildComponentTree(surface);
|
|
448
434
|
const root = surface.componentTree;
|
|
449
435
|
assert.deepStrictEqual(root.properties.children, []);
|
|
450
436
|
});
|
|
451
|
-
it(
|
|
437
|
+
it('sets primitive at path via single array element with dot key', () => {
|
|
452
438
|
processor.processMessages([
|
|
453
|
-
{ beginRendering: { surfaceId:
|
|
439
|
+
{ beginRendering: { surfaceId: 's1_prim', root: 'list' } },
|
|
454
440
|
{
|
|
455
441
|
dataModelUpdate: {
|
|
456
|
-
surfaceId:
|
|
457
|
-
path:
|
|
458
|
-
contents: [{ key:
|
|
442
|
+
surfaceId: 's1_prim',
|
|
443
|
+
path: '/nested/item',
|
|
444
|
+
contents: [{ key: '.', valueString: 'hello' }],
|
|
459
445
|
},
|
|
460
446
|
},
|
|
461
447
|
]);
|
|
462
|
-
const surface = processor.getSurfaces().get(
|
|
463
|
-
surface.components.set(
|
|
464
|
-
id:
|
|
448
|
+
const surface = processor.getSurfaces().get('s1_prim');
|
|
449
|
+
surface.components.set('list', {
|
|
450
|
+
id: 'list',
|
|
465
451
|
component: { Row: { children: { explicitList: [] } } },
|
|
466
452
|
});
|
|
467
453
|
processor.rebuildComponentTree(surface);
|
|
468
454
|
const root = surface.componentTree;
|
|
469
|
-
assert.strictEqual(processor.getData(root,
|
|
455
|
+
assert.strictEqual(processor.getData(root, '/nested/item', 's1_prim'), 'hello');
|
|
470
456
|
// Test the fallback where valueString is absent but we pass something anyway
|
|
471
457
|
assert.throws(() => {
|
|
472
458
|
processor.processMessages([
|
|
473
459
|
{
|
|
474
460
|
dataModelUpdate: {
|
|
475
|
-
surfaceId:
|
|
476
|
-
path:
|
|
477
|
-
contents: [{ key:
|
|
461
|
+
surfaceId: 's1_prim',
|
|
462
|
+
path: '/nested/malformed',
|
|
463
|
+
contents: [{ key: '.' }],
|
|
478
464
|
},
|
|
479
465
|
},
|
|
480
466
|
]);
|
|
481
467
|
}, /Value must have exactly one value property/);
|
|
482
468
|
});
|
|
483
|
-
it(
|
|
484
|
-
processor.processMessages([
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
surface.dataModel.set("obj", {
|
|
489
|
-
nestedMap: new Map([["index", [1, 2, { val: "hi" }]]]),
|
|
469
|
+
it('path resolves through primitive objects and arrays', () => {
|
|
470
|
+
processor.processMessages([{ beginRendering: { surfaceId: 's1_path', root: 'list' } }]);
|
|
471
|
+
const surface = processor.getSurfaces().get('s1_path');
|
|
472
|
+
surface.dataModel.set('obj', {
|
|
473
|
+
nestedMap: new Map([['index', [1, 2, { val: 'hi' }]]]),
|
|
490
474
|
});
|
|
491
|
-
surface.components.set(
|
|
492
|
-
id:
|
|
475
|
+
surface.components.set('list', {
|
|
476
|
+
id: 'list',
|
|
493
477
|
component: { Row: { children: { explicitList: [] } } },
|
|
494
478
|
});
|
|
495
479
|
processor.rebuildComponentTree(surface);
|
|
496
480
|
const root = surface.componentTree;
|
|
497
|
-
assert.strictEqual(processor.getData(root,
|
|
498
|
-
assert.strictEqual(processor.getData(root,
|
|
481
|
+
assert.strictEqual(processor.getData(root, '/obj/nestedMap/index/2/val', 's1_path'), 'hi');
|
|
482
|
+
assert.strictEqual(processor.getData(root, '/obj/nestedMap/index/2/val/deeper', 's1_path'), null);
|
|
499
483
|
});
|
|
500
|
-
it(
|
|
501
|
-
const surfaceId =
|
|
502
|
-
processor.processMessages([{ beginRendering: { surfaceId, root:
|
|
484
|
+
it('builds all component types correctly', () => {
|
|
485
|
+
const surfaceId = 's_all';
|
|
486
|
+
processor.processMessages([{ beginRendering: { surfaceId, root: 'col' } }]);
|
|
503
487
|
const surface = processor.getSurfaces().get(surfaceId);
|
|
504
488
|
const components = {
|
|
505
|
-
col: { Column: { children: [
|
|
506
|
-
row: { Row: { children: [
|
|
507
|
-
card: { Card: { child:
|
|
489
|
+
col: { Column: { children: ['row'] } },
|
|
490
|
+
row: { Row: { children: ['card'] } },
|
|
491
|
+
card: { Card: { child: 'tabs' } },
|
|
508
492
|
tabs: {
|
|
509
493
|
Tabs: {
|
|
510
|
-
tabItems: [{ title: { literalString:
|
|
494
|
+
tabItems: [{ title: { literalString: 'T1' }, child: 'modal' }],
|
|
511
495
|
},
|
|
512
496
|
},
|
|
513
|
-
modal: { Modal: { entryPointChild:
|
|
514
|
-
list: { List: { children: [
|
|
515
|
-
btn: { Button: { child:
|
|
516
|
-
btn_txt: { Text: { text: { literalString:
|
|
497
|
+
modal: { Modal: { entryPointChild: 'list', contentChild: 'div' } },
|
|
498
|
+
list: { List: { children: ['btn'] } },
|
|
499
|
+
btn: { Button: { child: 'btn_txt', action: 'act' } },
|
|
500
|
+
btn_txt: { Text: { text: { literalString: 'Click' } } },
|
|
517
501
|
div: { Divider: {} },
|
|
518
502
|
chk: {
|
|
519
503
|
CheckBox: {
|
|
520
|
-
label: { literalString:
|
|
504
|
+
label: { literalString: 'Chk' },
|
|
521
505
|
value: { literalBoolean: true },
|
|
522
506
|
},
|
|
523
507
|
},
|
|
524
|
-
txt: { TextField: { label: { literalString:
|
|
525
|
-
dt: { DateTimeInput: { value: { literalString:
|
|
526
|
-
mc: { MultipleChoice: { selections: { literal: [
|
|
508
|
+
txt: { TextField: { label: { literalString: 'Txt' } } },
|
|
509
|
+
dt: { DateTimeInput: { value: { literalString: '2022-01-01' } } },
|
|
510
|
+
mc: { MultipleChoice: { selections: { literal: ['a'] } } },
|
|
527
511
|
sl: { Slider: { value: { literalNumber: 50 } } },
|
|
528
|
-
img: { Image: { url: { literalString:
|
|
529
|
-
icon: { Icon: { name: { literalString:
|
|
530
|
-
vid: { Video: { url: { literalString:
|
|
531
|
-
aud: { AudioPlayer: { url: { literalString:
|
|
512
|
+
img: { Image: { url: { literalString: 'http://img' } } },
|
|
513
|
+
icon: { Icon: { name: { literalString: 'home' } } },
|
|
514
|
+
vid: { Video: { url: { literalString: 'http://vid' } } },
|
|
515
|
+
aud: { AudioPlayer: { url: { literalString: 'http://aud' } } },
|
|
532
516
|
};
|
|
533
517
|
for (const [id, comp] of Object.entries(components)) {
|
|
534
518
|
surface.components.set(id, { id, component: comp });
|
|
535
519
|
}
|
|
536
|
-
surface.components.set(
|
|
537
|
-
id:
|
|
520
|
+
surface.components.set('root_all', {
|
|
521
|
+
id: 'root_all',
|
|
538
522
|
component: {
|
|
539
523
|
Column: {
|
|
540
|
-
children: [
|
|
541
|
-
"col",
|
|
542
|
-
"chk",
|
|
543
|
-
"txt",
|
|
544
|
-
"dt",
|
|
545
|
-
"mc",
|
|
546
|
-
"sl",
|
|
547
|
-
"img",
|
|
548
|
-
"icon",
|
|
549
|
-
"vid",
|
|
550
|
-
"aud",
|
|
551
|
-
],
|
|
524
|
+
children: ['col', 'chk', 'txt', 'dt', 'mc', 'sl', 'img', 'icon', 'vid', 'aud'],
|
|
552
525
|
},
|
|
553
526
|
},
|
|
554
527
|
});
|
|
555
|
-
surface.rootComponentId =
|
|
528
|
+
surface.rootComponentId = 'root_all';
|
|
556
529
|
processor.rebuildComponentTree(surface);
|
|
557
530
|
const root = surface.componentTree;
|
|
558
|
-
assert.strictEqual(root.type,
|
|
531
|
+
assert.strictEqual(root.type, 'Column');
|
|
559
532
|
assert.strictEqual(root.properties.children.length, 10);
|
|
560
533
|
});
|
|
561
|
-
it(
|
|
534
|
+
it('handles recursive valueMap in convertKeyValueArrayToMap', () => {
|
|
562
535
|
// We need to bypass private check or use a method that calls it.
|
|
563
536
|
// setData calls setDataByPath which calls convertKeyValueArrayToMap.
|
|
564
537
|
const update = {
|
|
565
|
-
surfaceId:
|
|
538
|
+
surfaceId: 's_rec',
|
|
566
539
|
contents: [
|
|
567
540
|
{
|
|
568
|
-
key:
|
|
569
|
-
valueMap: [{ key:
|
|
541
|
+
key: 'nested',
|
|
542
|
+
valueMap: [{ key: 'inner', valueString: 'val' }],
|
|
570
543
|
},
|
|
571
544
|
], // Recursive generic type difficult to construct in TS literal
|
|
572
545
|
};
|
|
573
546
|
// We can use processMessages with dataModelUpdate.
|
|
574
|
-
processor.processMessages([
|
|
575
|
-
{ beginRendering: { surfaceId: "s_rec", root: "root" } },
|
|
576
|
-
]);
|
|
547
|
+
processor.processMessages([{ beginRendering: { surfaceId: 's_rec', root: 'root' } }]);
|
|
577
548
|
// Set root to Map first
|
|
578
|
-
const surface = processor.getSurfaces().get(
|
|
579
|
-
surface.dataModel.set(
|
|
549
|
+
const surface = processor.getSurfaces().get('s_rec');
|
|
550
|
+
surface.dataModel.set('junk', 'ignored');
|
|
580
551
|
processor.processMessages([{ dataModelUpdate: update }]);
|
|
581
552
|
// Verify data
|
|
582
|
-
const nested = surface.dataModel.get(
|
|
553
|
+
const nested = surface.dataModel.get('nested');
|
|
583
554
|
assert.strictEqual(nested instanceof Map, true);
|
|
584
|
-
assert.strictEqual(nested.get(
|
|
555
|
+
assert.strictEqual(nested.get('inner'), 'val');
|
|
585
556
|
});
|
|
586
|
-
it(
|
|
557
|
+
it('resolves Template with Map data', () => {
|
|
587
558
|
processor.processMessages([
|
|
588
|
-
{ beginRendering: { surfaceId:
|
|
559
|
+
{ beginRendering: { surfaceId: 's3_map', root: 'list' } },
|
|
589
560
|
{
|
|
590
561
|
dataModelUpdate: {
|
|
591
|
-
surfaceId:
|
|
562
|
+
surfaceId: 's3_map',
|
|
592
563
|
contents: [
|
|
593
564
|
{
|
|
594
|
-
key:
|
|
565
|
+
key: 'items',
|
|
595
566
|
valueMap: [
|
|
596
|
-
{ key:
|
|
597
|
-
{ key:
|
|
567
|
+
{ key: 'a', valueString: 'valA' },
|
|
568
|
+
{ key: 'b', valueString: 'valB' },
|
|
598
569
|
],
|
|
599
570
|
},
|
|
600
571
|
],
|
|
601
572
|
},
|
|
602
573
|
},
|
|
603
574
|
]);
|
|
604
|
-
const surface = processor.getSurfaces().get(
|
|
575
|
+
const surface = processor.getSurfaces().get('s3_map');
|
|
605
576
|
// Define components
|
|
606
|
-
surface.components.set(
|
|
607
|
-
id:
|
|
577
|
+
surface.components.set('list', {
|
|
578
|
+
id: 'list',
|
|
608
579
|
component: {
|
|
609
580
|
List: {
|
|
610
581
|
children: {
|
|
611
582
|
template: {
|
|
612
|
-
dataBinding:
|
|
613
|
-
componentId:
|
|
583
|
+
dataBinding: '/items',
|
|
584
|
+
componentId: 'item',
|
|
614
585
|
},
|
|
615
586
|
},
|
|
616
587
|
},
|
|
617
588
|
},
|
|
618
589
|
});
|
|
619
|
-
surface.components.set(
|
|
620
|
-
id:
|
|
590
|
+
surface.components.set('item', {
|
|
591
|
+
id: 'item',
|
|
621
592
|
component: {
|
|
622
|
-
Text: { text: { path:
|
|
593
|
+
Text: { text: { path: '.' } },
|
|
623
594
|
},
|
|
624
595
|
});
|
|
625
596
|
processor.rebuildComponentTree(surface);
|
|
626
597
|
const root = surface.componentTree;
|
|
627
|
-
assert.strictEqual(root.type,
|
|
598
|
+
assert.strictEqual(root.type, 'List');
|
|
628
599
|
// Map entries should be processed.
|
|
629
600
|
assert.strictEqual(root.properties.children.length, 2);
|
|
630
601
|
});
|
|
631
|
-
it(
|
|
632
|
-
const surfaceId =
|
|
633
|
-
processor.processMessages([
|
|
634
|
-
{ beginRendering: { surfaceId, root: "root" } },
|
|
635
|
-
]);
|
|
602
|
+
it('handles Object-to-Map normalization in handleDataModelUpdate (direct call)', () => {
|
|
603
|
+
const surfaceId = 's_norm';
|
|
604
|
+
processor.processMessages([{ beginRendering: { surfaceId, root: 'root' } }]);
|
|
636
605
|
const surface = processor.getSurfaces().get(surfaceId);
|
|
637
606
|
// Direct call to bypass Zod validation in processMessages
|
|
638
607
|
processor.handleDataModelUpdate({
|
|
639
608
|
surfaceId,
|
|
640
|
-
contents: { normalized:
|
|
609
|
+
contents: { normalized: 'value' },
|
|
641
610
|
}, surfaceId);
|
|
642
|
-
assert.strictEqual(surface.dataModel.get(
|
|
611
|
+
assert.strictEqual(surface.dataModel.get('normalized'), 'value');
|
|
643
612
|
});
|
|
644
|
-
it(
|
|
645
|
-
const surfaceId =
|
|
646
|
-
processor.processMessages([
|
|
647
|
-
{ beginRendering: { surfaceId, root: "badList" } },
|
|
648
|
-
]);
|
|
613
|
+
it('throws validation error for invalid List', () => {
|
|
614
|
+
const surfaceId = 's_bad_list';
|
|
615
|
+
processor.processMessages([{ beginRendering: { surfaceId, root: 'badList' } }]);
|
|
649
616
|
const surface = processor.getSurfaces().get(surfaceId);
|
|
650
|
-
surface.components.set(
|
|
651
|
-
id:
|
|
652
|
-
component: { List: { children:
|
|
617
|
+
surface.components.set('badList', {
|
|
618
|
+
id: 'badList',
|
|
619
|
+
component: { List: { children: 'not-array' } },
|
|
653
620
|
});
|
|
654
621
|
assert.throws(() => {
|
|
655
622
|
processor.rebuildComponentTree(surface);
|
|
656
623
|
}, /Invalid data; expected List/);
|
|
657
624
|
});
|
|
658
|
-
it(
|
|
659
|
-
const surfaceId =
|
|
625
|
+
it('handles recursive valueMap in convertKeyValueArrayToMap with dot key', () => {
|
|
626
|
+
const surfaceId = 's_dot_rec';
|
|
660
627
|
const surface = processor.getOrCreateSurface(surfaceId);
|
|
661
628
|
const value = [
|
|
662
629
|
{
|
|
663
|
-
key:
|
|
664
|
-
valueMap: [{ key:
|
|
630
|
+
key: '.',
|
|
631
|
+
valueMap: [{ key: 'inner', valueString: 'val' }],
|
|
665
632
|
},
|
|
666
633
|
];
|
|
667
634
|
// Calling setDataByPath directly (private)
|
|
668
|
-
processor.setDataByPath(surface.dataModel,
|
|
669
|
-
const target = surface.dataModel.get(
|
|
635
|
+
processor.setDataByPath(surface.dataModel, '/target', value);
|
|
636
|
+
const target = surface.dataModel.get('target');
|
|
670
637
|
assert.strictEqual(target instanceof Map, true);
|
|
671
|
-
assert.strictEqual(target.get(
|
|
638
|
+
assert.strictEqual(target.get('inner'), 'val');
|
|
672
639
|
});
|
|
673
|
-
it(
|
|
674
|
-
const surfaceId =
|
|
675
|
-
processor.processMessages([
|
|
676
|
-
{ beginRendering: { surfaceId, root: "list" } },
|
|
677
|
-
]);
|
|
640
|
+
it('resolves Template with Array data (via direct setData)', () => {
|
|
641
|
+
const surfaceId = 's_direct_arr';
|
|
642
|
+
processor.processMessages([{ beginRendering: { surfaceId, root: 'list' } }]);
|
|
678
643
|
const surface = processor.getSurfaces().get(surfaceId);
|
|
679
644
|
// Setup components
|
|
680
|
-
surface.components.set(
|
|
681
|
-
id:
|
|
645
|
+
surface.components.set('list', {
|
|
646
|
+
id: 'list',
|
|
682
647
|
component: {
|
|
683
648
|
List: {
|
|
684
649
|
children: {
|
|
685
650
|
template: {
|
|
686
|
-
dataBinding:
|
|
687
|
-
componentId:
|
|
651
|
+
dataBinding: '/items',
|
|
652
|
+
componentId: 'item',
|
|
688
653
|
},
|
|
689
654
|
},
|
|
690
655
|
},
|
|
691
656
|
},
|
|
692
657
|
});
|
|
693
|
-
surface.components.set(
|
|
694
|
-
id:
|
|
658
|
+
surface.components.set('item', {
|
|
659
|
+
id: 'item',
|
|
695
660
|
component: {
|
|
696
|
-
Text: { text: { path:
|
|
661
|
+
Text: { text: { path: '.' } },
|
|
697
662
|
},
|
|
698
663
|
});
|
|
699
664
|
// Direct set array to bypass normalization/schema
|
|
700
|
-
const itemsArray = [
|
|
701
|
-
surface.dataModel.set(
|
|
665
|
+
const itemsArray = ['A', 'B'];
|
|
666
|
+
surface.dataModel.set('items', itemsArray);
|
|
702
667
|
processor.rebuildComponentTree(surface);
|
|
703
668
|
const root = surface.componentTree;
|
|
704
|
-
assert.strictEqual(root.type,
|
|
669
|
+
assert.strictEqual(root.type, 'List');
|
|
705
670
|
assert.strictEqual(root.properties.children.length, 2);
|
|
706
671
|
});
|
|
707
|
-
it(
|
|
672
|
+
it('ignores non-strings when trying to parse JSON', () => {
|
|
708
673
|
const result = processor.parseIfJsonString(123);
|
|
709
674
|
assert.strictEqual(result, 123);
|
|
710
675
|
});
|