@ccheever/exact-renderer 0.1.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/package.json +118 -0
- package/src/__tests__/adapter-window-state.test.tsx +190 -0
- package/src/__tests__/attrs.test.ts +157 -0
- package/src/__tests__/classname.test.ts +332 -0
- package/src/__tests__/color.test.ts +169 -0
- package/src/__tests__/dom-mirror.test.ts +682 -0
- package/src/__tests__/dom-shim.test.ts +274 -0
- package/src/__tests__/fixtures/SvelteCounter.svelte +7 -0
- package/src/__tests__/fixtures/SvelteInput.svelte +8 -0
- package/src/__tests__/host-config.test.ts +51 -0
- package/src/__tests__/host-ops.test.ts +2234 -0
- package/src/__tests__/image-source.test.ts +135 -0
- package/src/__tests__/liquid-glass.test.ts +72 -0
- package/src/__tests__/multi-root.test.ts +118 -0
- package/src/__tests__/native-view-events.test.ts +102 -0
- package/src/__tests__/nodes.test.ts +399 -0
- package/src/__tests__/normalize.test.ts +576 -0
- package/src/__tests__/paragraph-lowering.test.tsx +144 -0
- package/src/__tests__/props.test.ts +518 -0
- package/src/__tests__/protocol-encoder.test.ts +732 -0
- package/src/__tests__/protocol-fixture-bytes.test.ts +41 -0
- package/src/__tests__/reconciler.test.tsx +241 -0
- package/src/__tests__/svelte-adapter.test.ts +166 -0
- package/src/__tests__/svg-source.test.ts +71 -0
- package/src/__tests__/tags.test.ts +354 -0
- package/src/__tests__/toggle.test.ts +441 -0
- package/src/__tests__/transitions.test.ts +106 -0
- package/src/__tests__/web-primitives.test.tsx +454 -0
- package/src/__tests__/window-hooks.test.tsx +447 -0
- package/src/adapter-contract.ts +68 -0
- package/src/attrs.ts +596 -0
- package/src/classname-contract.ts +87 -0
- package/src/classname-resolve.ts +553 -0
- package/src/classname-runtime.ts +29 -0
- package/src/components.ts +214 -0
- package/src/css-variable-context.ts +83 -0
- package/src/dom-hydration.ts +160 -0
- package/src/dom-mirror.ts +1459 -0
- package/src/dom-shim.ts +1736 -0
- package/src/group-context.ts +69 -0
- package/src/host-config.ts +431 -0
- package/src/host-ops.ts +3167 -0
- package/src/image-source.native.ts +703 -0
- package/src/image-source.ts +554 -0
- package/src/index.ts +278 -0
- package/src/inspector-runtime.ts +244 -0
- package/src/inspector.ts +3570 -0
- package/src/jsx-augmentations.ts +54 -0
- package/src/keyboard-avoidance.ts +217 -0
- package/src/native-primitives.ts +43 -0
- package/src/native-view-events.ts +322 -0
- package/src/native-view.ts +60 -0
- package/src/nodes/index.ts +41 -0
- package/src/nodes/node.ts +531 -0
- package/src/peer-context.ts +100 -0
- package/src/primitives.native.ts +8 -0
- package/src/primitives.ts +8 -0
- package/src/props/index.ts +14 -0
- package/src/props/normalize.ts +816 -0
- package/src/protocol/encoder.ts +940 -0
- package/src/protocol/index.ts +33 -0
- package/src/reconciler.ts +581 -0
- package/src/runtime.ts +11 -0
- package/src/safe-area.ts +543 -0
- package/src/solid.ts +490 -0
- package/src/style/color.js +1 -0
- package/src/style/color.ts +15 -0
- package/src/style/index.js +1 -0
- package/src/style/index.ts +22 -0
- package/src/style/normalize.js +1 -0
- package/src/style/normalize.ts +1426 -0
- package/src/svelte.ts +349 -0
- package/src/svg-source.ts +222 -0
- package/src/tags/index.ts +21 -0
- package/src/tags/tag-map.ts +289 -0
- package/src/text/paragraph-lowering.ts +310 -0
- package/src/types.ts +1175 -0
- package/src/vue.ts +535 -0
- package/src/web-host.ts +19 -0
- package/src/web-primitives.ts +1654 -0
|
@@ -0,0 +1,354 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tag Mapping Tests
|
|
3
|
+
*
|
|
4
|
+
* Tests for the tag configuration and mapping utilities.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { afterEach, describe, it, expect } from 'vitest';
|
|
8
|
+
import {
|
|
9
|
+
_clearNativeViewTagsForTesting,
|
|
10
|
+
getTagConfig,
|
|
11
|
+
isValidTag,
|
|
12
|
+
isWebTag,
|
|
13
|
+
isRNTag,
|
|
14
|
+
getWebTags,
|
|
15
|
+
getRNTags,
|
|
16
|
+
getAllTags,
|
|
17
|
+
defaultTagConfig,
|
|
18
|
+
} from '../tags/tag-map.js';
|
|
19
|
+
import { NodeType } from '@exact/core';
|
|
20
|
+
import { createNativeViewComponent } from '../native-view.js';
|
|
21
|
+
|
|
22
|
+
afterEach(() => {
|
|
23
|
+
_clearNativeViewTagsForTesting();
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
describe('getTagConfig', () => {
|
|
27
|
+
describe('web tags', () => {
|
|
28
|
+
it('should return config for div', () => {
|
|
29
|
+
const config = getTagConfig('div');
|
|
30
|
+
expect(config).not.toBeNull();
|
|
31
|
+
expect(config!.canonicalType).toBe('view');
|
|
32
|
+
expect(config!.nodeType).toBe(NodeType.View);
|
|
33
|
+
expect(config!.isRNStyle).toBe(false);
|
|
34
|
+
expect(config!.defaultFlexDirection).toBe('row');
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it('should return config for span', () => {
|
|
38
|
+
const config = getTagConfig('span');
|
|
39
|
+
expect(config).not.toBeNull();
|
|
40
|
+
expect(config!.canonicalType).toBe('view');
|
|
41
|
+
expect(config!.defaultFlexDirection).toBe('row');
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it('should return text-container configs for paragraph and heading tags', () => {
|
|
45
|
+
expect(getTagConfig('p')?.canonicalType).toBe('text');
|
|
46
|
+
expect(getTagConfig('h1')?.canonicalType).toBe('text');
|
|
47
|
+
expect(getTagConfig('h6')?.isTextContainer).toBe(true);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it('should return config for text', () => {
|
|
51
|
+
const config = getTagConfig('text');
|
|
52
|
+
expect(config).not.toBeNull();
|
|
53
|
+
expect(config!.canonicalType).toBe('text');
|
|
54
|
+
expect(config!.nodeType).toBe(NodeType.Text);
|
|
55
|
+
expect(config!.isTextContainer).toBe(true);
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
it('should return config for img', () => {
|
|
59
|
+
const config = getTagConfig('img');
|
|
60
|
+
expect(config).not.toBeNull();
|
|
61
|
+
expect(config!.canonicalType).toBe('image');
|
|
62
|
+
expect(config!.nodeType).toBe(NodeType.Image);
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it('should return config for lowercase svg', () => {
|
|
66
|
+
const config = getTagConfig('svg');
|
|
67
|
+
expect(config).not.toBeNull();
|
|
68
|
+
expect(config!.canonicalType).toBe('svg');
|
|
69
|
+
expect(config!.nodeType).toBe(NodeType.Svg);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it('should return config for input', () => {
|
|
73
|
+
const config = getTagConfig('input');
|
|
74
|
+
expect(config).not.toBeNull();
|
|
75
|
+
expect(config!.canonicalType).toBe('input');
|
|
76
|
+
expect(config!.nodeType).toBe(NodeType.TextInput);
|
|
77
|
+
expect(config!.supportsChangeEvents).toBe(true);
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it('should return config for textarea', () => {
|
|
81
|
+
const config = getTagConfig('textarea');
|
|
82
|
+
expect(config).not.toBeNull();
|
|
83
|
+
expect(config!.canonicalType).toBe('input');
|
|
84
|
+
expect(config!.nodeType).toBe(NodeType.TextInput);
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
it('should return config for button', () => {
|
|
88
|
+
const config = getTagConfig('button');
|
|
89
|
+
expect(config).not.toBeNull();
|
|
90
|
+
expect(config!.canonicalType).toBe('pressable');
|
|
91
|
+
expect(config!.nodeType).toBe(NodeType.View);
|
|
92
|
+
expect(config!.supportsPressEvents).toBe(true);
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
it('should return config for semantic container tags', () => {
|
|
96
|
+
expect(getTagConfig('section')?.canonicalType).toBe('view');
|
|
97
|
+
expect(getTagConfig('article')?.canonicalType).toBe('view');
|
|
98
|
+
expect(getTagConfig('label')?.canonicalType).toBe('view');
|
|
99
|
+
expect(getTagConfig('a')?.supportsPressEvents).toBe(true);
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
it('should return config for toggle (web)', () => {
|
|
103
|
+
const config = getTagConfig('toggle');
|
|
104
|
+
expect(config).not.toBeNull();
|
|
105
|
+
expect(config!.canonicalType).toBe('nativeView');
|
|
106
|
+
expect(config!.nodeType).toBe(NodeType.NativeView);
|
|
107
|
+
expect(config!.nativeView?.moduleName).toBe('exact.toggle');
|
|
108
|
+
expect(config!.isRNStyle).toBe(false);
|
|
109
|
+
expect(config!.supportsChangeEvents).toBe(true);
|
|
110
|
+
});
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
describe('RN tags', () => {
|
|
114
|
+
it('should return config for View', () => {
|
|
115
|
+
const config = getTagConfig('View');
|
|
116
|
+
expect(config).not.toBeNull();
|
|
117
|
+
expect(config!.canonicalType).toBe('view');
|
|
118
|
+
expect(config!.nodeType).toBe(NodeType.View);
|
|
119
|
+
expect(config!.isRNStyle).toBe(true);
|
|
120
|
+
expect(config!.defaultFlexDirection).toBe('column');
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
it('should return config for Text', () => {
|
|
124
|
+
const config = getTagConfig('Text');
|
|
125
|
+
expect(config).not.toBeNull();
|
|
126
|
+
expect(config!.canonicalType).toBe('text');
|
|
127
|
+
expect(config!.isRNStyle).toBe(true);
|
|
128
|
+
expect(config!.isTextContainer).toBe(true);
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
it('should return config for Image', () => {
|
|
132
|
+
const config = getTagConfig('Image');
|
|
133
|
+
expect(config).not.toBeNull();
|
|
134
|
+
expect(config!.canonicalType).toBe('image');
|
|
135
|
+
expect(config!.isRNStyle).toBe(true);
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
it('should return config for ScrollView', () => {
|
|
139
|
+
const config = getTagConfig('ScrollView');
|
|
140
|
+
expect(config).not.toBeNull();
|
|
141
|
+
expect(config!.canonicalType).toBe('scroll');
|
|
142
|
+
expect(config!.nodeType).toBe(NodeType.ScrollView);
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
it('should return config for List', () => {
|
|
146
|
+
const config = getTagConfig('List');
|
|
147
|
+
expect(config).not.toBeNull();
|
|
148
|
+
expect(config!.canonicalType).toBe('list');
|
|
149
|
+
expect(config!.nodeType).toBe(NodeType.List);
|
|
150
|
+
expect(config!.isRNStyle).toBe(true);
|
|
151
|
+
expect(config!.defaultFlexDirection).toBe('column');
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
it('should return config for TextInput', () => {
|
|
155
|
+
const config = getTagConfig('TextInput');
|
|
156
|
+
expect(config).not.toBeNull();
|
|
157
|
+
expect(config!.canonicalType).toBe('input');
|
|
158
|
+
expect(config!.supportsChangeEvents).toBe(true);
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
it('should return config for Pressable', () => {
|
|
162
|
+
const config = getTagConfig('Pressable');
|
|
163
|
+
expect(config).not.toBeNull();
|
|
164
|
+
expect(config!.canonicalType).toBe('pressable');
|
|
165
|
+
expect(config!.nodeType).toBe(NodeType.Pressable);
|
|
166
|
+
expect(config!.supportsPressEvents).toBe(true);
|
|
167
|
+
expect(config!.isRNStyle).toBe(true);
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
it('should return config for Toggle', () => {
|
|
171
|
+
const config = getTagConfig('Toggle');
|
|
172
|
+
expect(config).not.toBeNull();
|
|
173
|
+
expect(config!.canonicalType).toBe('nativeView');
|
|
174
|
+
expect(config!.nodeType).toBe(NodeType.NativeView);
|
|
175
|
+
expect(config!.nativeView?.moduleName).toBe('exact.toggle');
|
|
176
|
+
expect(config!.isRNStyle).toBe(true);
|
|
177
|
+
expect(config!.supportsChangeEvents).toBe(true);
|
|
178
|
+
expect(config!.supportsPressEvents).toBe(false);
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
it('should return config for Switch', () => {
|
|
182
|
+
const config = getTagConfig('Switch');
|
|
183
|
+
expect(config).not.toBeNull();
|
|
184
|
+
expect(config!.canonicalType).toBe('nativeView');
|
|
185
|
+
expect(config!.nodeType).toBe(NodeType.NativeView);
|
|
186
|
+
expect(config!.nativeView?.moduleName).toBe('exact.toggle');
|
|
187
|
+
expect(config!.isRNStyle).toBe(true);
|
|
188
|
+
expect(config!.supportsChangeEvents).toBe(true);
|
|
189
|
+
});
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
describe('unknown tags', () => {
|
|
193
|
+
it('should return null for unknown tags', () => {
|
|
194
|
+
expect(getTagConfig('unknown')).toBeNull();
|
|
195
|
+
expect(getTagConfig('foo')).toBeNull();
|
|
196
|
+
expect(getTagConfig('CustomComponent')).toBeNull();
|
|
197
|
+
});
|
|
198
|
+
});
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
describe('isValidTag', () => {
|
|
202
|
+
it('should return true for valid web tags', () => {
|
|
203
|
+
expect(isValidTag('div')).toBe(true);
|
|
204
|
+
expect(isValidTag('span')).toBe(true);
|
|
205
|
+
expect(isValidTag('text')).toBe(true);
|
|
206
|
+
expect(isValidTag('img')).toBe(true);
|
|
207
|
+
expect(isValidTag('textarea')).toBe(true);
|
|
208
|
+
expect(isValidTag('svg')).toBe(true);
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
it('should return true for valid RN tags', () => {
|
|
212
|
+
expect(isValidTag('View')).toBe(true);
|
|
213
|
+
expect(isValidTag('Text')).toBe(true);
|
|
214
|
+
expect(isValidTag('Image')).toBe(true);
|
|
215
|
+
expect(isValidTag('Pressable')).toBe(true);
|
|
216
|
+
expect(isValidTag('List')).toBe(true);
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
it('should return false for unknown tags', () => {
|
|
220
|
+
expect(isValidTag('unknown')).toBe(false);
|
|
221
|
+
expect(isValidTag('Foo')).toBe(false);
|
|
222
|
+
});
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
describe('isWebTag', () => {
|
|
226
|
+
it('should return true for web tags', () => {
|
|
227
|
+
expect(isWebTag('div')).toBe(true);
|
|
228
|
+
expect(isWebTag('span')).toBe(true);
|
|
229
|
+
expect(isWebTag('text')).toBe(true);
|
|
230
|
+
expect(isWebTag('img')).toBe(true);
|
|
231
|
+
expect(isWebTag('button')).toBe(true);
|
|
232
|
+
expect(isWebTag('textarea')).toBe(true);
|
|
233
|
+
expect(isWebTag('svg')).toBe(true);
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
it('should return false for RN tags', () => {
|
|
237
|
+
expect(isWebTag('View')).toBe(false);
|
|
238
|
+
expect(isWebTag('Text')).toBe(false);
|
|
239
|
+
expect(isWebTag('Image')).toBe(false);
|
|
240
|
+
});
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
describe('isRNTag', () => {
|
|
244
|
+
it('should return true for RN tags', () => {
|
|
245
|
+
expect(isRNTag('View')).toBe(true);
|
|
246
|
+
expect(isRNTag('Text')).toBe(true);
|
|
247
|
+
expect(isRNTag('Image')).toBe(true);
|
|
248
|
+
expect(isRNTag('Pressable')).toBe(true);
|
|
249
|
+
expect(isRNTag('ScrollView')).toBe(true);
|
|
250
|
+
expect(isRNTag('List')).toBe(true);
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
it('should return false for web tags', () => {
|
|
254
|
+
expect(isRNTag('div')).toBe(false);
|
|
255
|
+
expect(isRNTag('text')).toBe(false);
|
|
256
|
+
expect(isRNTag('img')).toBe(false);
|
|
257
|
+
});
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
describe('getWebTags', () => {
|
|
261
|
+
it('should return all web tag names', () => {
|
|
262
|
+
const tags = getWebTags();
|
|
263
|
+
expect(tags).toContain('div');
|
|
264
|
+
expect(tags).toContain('span');
|
|
265
|
+
expect(tags).toContain('text');
|
|
266
|
+
expect(tags).toContain('img');
|
|
267
|
+
expect(tags).toContain('input');
|
|
268
|
+
expect(tags).toContain('button');
|
|
269
|
+
expect(tags).toContain('toggle');
|
|
270
|
+
expect(tags).toContain('textarea');
|
|
271
|
+
expect(tags).toContain('svg');
|
|
272
|
+
expect(tags).toContain('section');
|
|
273
|
+
expect(tags).toContain('a');
|
|
274
|
+
expect(tags.length).toBe(26);
|
|
275
|
+
});
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
describe('getRNTags', () => {
|
|
279
|
+
it('should return all RN tag names', () => {
|
|
280
|
+
const tags = getRNTags();
|
|
281
|
+
expect(tags).toContain('View');
|
|
282
|
+
expect(tags).toContain('Text');
|
|
283
|
+
expect(tags).toContain('Image');
|
|
284
|
+
expect(tags).toContain('ScrollView');
|
|
285
|
+
expect(tags).toContain('List');
|
|
286
|
+
expect(tags).toContain('TextInput');
|
|
287
|
+
expect(tags).toContain('Pressable');
|
|
288
|
+
expect(tags).toContain('Toggle');
|
|
289
|
+
expect(tags).toContain('Switch');
|
|
290
|
+
expect(tags.length).toBe(11);
|
|
291
|
+
});
|
|
292
|
+
});
|
|
293
|
+
|
|
294
|
+
describe('getAllTags', () => {
|
|
295
|
+
it('should return all tag names', () => {
|
|
296
|
+
const tags = getAllTags();
|
|
297
|
+
expect(tags.length).toBe(37); // 26 web + 11 RN
|
|
298
|
+
expect(tags).toContain('div');
|
|
299
|
+
expect(tags).toContain('View');
|
|
300
|
+
expect(tags).toContain('List');
|
|
301
|
+
expect(tags).toContain('toggle');
|
|
302
|
+
expect(tags).toContain('Toggle');
|
|
303
|
+
expect(tags).toContain('Switch');
|
|
304
|
+
expect(tags).toContain('svg');
|
|
305
|
+
expect(tags).toContain('textarea');
|
|
306
|
+
});
|
|
307
|
+
|
|
308
|
+
it('includes dynamically registered native-view tags', () => {
|
|
309
|
+
const ChartView = createNativeViewComponent<{ title: string }>({
|
|
310
|
+
moduleName: 'com.exact.chart',
|
|
311
|
+
propKeys: ['title'],
|
|
312
|
+
selection: {
|
|
313
|
+
tier: 'atomic',
|
|
314
|
+
},
|
|
315
|
+
});
|
|
316
|
+
|
|
317
|
+
const element = ChartView({ title: 'Revenue' });
|
|
318
|
+
const tag = String(element.type);
|
|
319
|
+
const config = getTagConfig(tag);
|
|
320
|
+
const tags = getAllTags();
|
|
321
|
+
|
|
322
|
+
expect(config).not.toBeNull();
|
|
323
|
+
expect(config!.canonicalType).toBe('nativeView');
|
|
324
|
+
expect(config!.nodeType).toBe(NodeType.NativeView);
|
|
325
|
+
expect(config!.nativeView?.moduleName).toBe('com.exact.chart');
|
|
326
|
+
expect(tags).toContain(tag);
|
|
327
|
+
expect(isValidTag(tag)).toBe(true);
|
|
328
|
+
});
|
|
329
|
+
});
|
|
330
|
+
|
|
331
|
+
describe('defaultTagConfig', () => {
|
|
332
|
+
it('should provide sensible defaults', () => {
|
|
333
|
+
expect(defaultTagConfig.canonicalType).toBe('view');
|
|
334
|
+
expect(defaultTagConfig.nodeType).toBe(NodeType.View);
|
|
335
|
+
expect(defaultTagConfig.isRNStyle).toBe(false);
|
|
336
|
+
expect(defaultTagConfig.defaultFlexDirection).toBe('row');
|
|
337
|
+
expect(defaultTagConfig.isTextContainer).toBe(false);
|
|
338
|
+
expect(defaultTagConfig.supportsPressEvents).toBe(false);
|
|
339
|
+
});
|
|
340
|
+
});
|
|
341
|
+
|
|
342
|
+
describe('flex direction defaults', () => {
|
|
343
|
+
it('web tags should default to row', () => {
|
|
344
|
+
expect(getTagConfig('div')!.defaultFlexDirection).toBe('row');
|
|
345
|
+
expect(getTagConfig('span')!.defaultFlexDirection).toBe('row');
|
|
346
|
+
expect(getTagConfig('button')!.defaultFlexDirection).toBe('row');
|
|
347
|
+
});
|
|
348
|
+
|
|
349
|
+
it('RN tags should default to column', () => {
|
|
350
|
+
expect(getTagConfig('View')!.defaultFlexDirection).toBe('column');
|
|
351
|
+
expect(getTagConfig('Text')!.defaultFlexDirection).toBe('column');
|
|
352
|
+
expect(getTagConfig('Pressable')!.defaultFlexDirection).toBe('column');
|
|
353
|
+
});
|
|
354
|
+
});
|