@pyreon/core 0.24.4 → 0.24.6
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/lib/analysis/index.js.html +1 -1
- package/lib/index.js +53 -31
- package/package.json +2 -6
- package/src/compat-marker.ts +0 -79
- package/src/compat-shared.ts +0 -80
- package/src/component.ts +0 -98
- package/src/context.ts +0 -349
- package/src/defer.ts +0 -279
- package/src/dynamic.ts +0 -32
- package/src/env.d.ts +0 -6
- package/src/error-boundary.ts +0 -90
- package/src/for.ts +0 -51
- package/src/h.ts +0 -80
- package/src/index.ts +0 -80
- package/src/jsx-dev-runtime.ts +0 -2
- package/src/jsx-runtime.ts +0 -747
- package/src/lazy.ts +0 -25
- package/src/lifecycle.ts +0 -152
- package/src/manifest.ts +0 -579
- package/src/map-array.ts +0 -42
- package/src/portal.ts +0 -39
- package/src/props.ts +0 -269
- package/src/ref.ts +0 -32
- package/src/show.ts +0 -121
- package/src/style.ts +0 -102
- package/src/suspense.ts +0 -52
- package/src/telemetry.ts +0 -120
- package/src/tests/compat-marker.test.ts +0 -96
- package/src/tests/compat-shared.test.ts +0 -99
- package/src/tests/component.test.ts +0 -281
- package/src/tests/context.test.ts +0 -629
- package/src/tests/core.test.ts +0 -1290
- package/src/tests/cx.test.ts +0 -70
- package/src/tests/defer.test.ts +0 -359
- package/src/tests/dynamic.test.ts +0 -87
- package/src/tests/error-boundary.test.ts +0 -181
- package/src/tests/extract-props-overloads.types.test.ts +0 -135
- package/src/tests/for.test.ts +0 -117
- package/src/tests/h.test.ts +0 -221
- package/src/tests/jsx-compat.test.tsx +0 -86
- package/src/tests/lazy.test.ts +0 -100
- package/src/tests/lifecycle.test.ts +0 -350
- package/src/tests/manifest-snapshot.test.ts +0 -100
- package/src/tests/map-array.test.ts +0 -313
- package/src/tests/native-marker-error-boundary.test.ts +0 -12
- package/src/tests/portal.test.ts +0 -48
- package/src/tests/props-extended.test.ts +0 -157
- package/src/tests/props.test.ts +0 -250
- package/src/tests/reactive-context.test.ts +0 -69
- package/src/tests/reactive-props.test.ts +0 -157
- package/src/tests/ref.test.ts +0 -70
- package/src/tests/show.test.ts +0 -314
- package/src/tests/style.test.ts +0 -157
- package/src/tests/suspense.test.ts +0 -139
- package/src/tests/telemetry.test.ts +0 -297
- package/src/types.ts +0 -116
package/src/tests/show.test.ts
DELETED
|
@@ -1,314 +0,0 @@
|
|
|
1
|
-
import { h } from '../h'
|
|
2
|
-
import { Match, MatchSymbol, Show, Switch } from '../show'
|
|
3
|
-
import type { VNodeChild } from '../types'
|
|
4
|
-
|
|
5
|
-
describe('Show', () => {
|
|
6
|
-
test('returns a reactive getter (function)', () => {
|
|
7
|
-
const result = Show({ when: () => true, children: 'visible' })
|
|
8
|
-
expect(typeof result).toBe('function')
|
|
9
|
-
})
|
|
10
|
-
|
|
11
|
-
test('getter returns children when condition is truthy', () => {
|
|
12
|
-
const getter = Show({ when: () => true, children: 'visible' }) as unknown as () => VNodeChild
|
|
13
|
-
expect(getter()).toBe('visible')
|
|
14
|
-
})
|
|
15
|
-
|
|
16
|
-
test('getter returns null when condition is falsy and no fallback', () => {
|
|
17
|
-
const getter = Show({ when: () => false, children: 'hidden' }) as unknown as () => VNodeChild
|
|
18
|
-
expect(getter()).toBeNull()
|
|
19
|
-
})
|
|
20
|
-
|
|
21
|
-
test('getter returns fallback when condition is falsy', () => {
|
|
22
|
-
const fb = h('span', null, 'fallback')
|
|
23
|
-
const getter = Show({
|
|
24
|
-
when: () => false,
|
|
25
|
-
fallback: fb,
|
|
26
|
-
children: 'main',
|
|
27
|
-
}) as unknown as () => VNodeChild
|
|
28
|
-
expect(getter()).toBe(fb)
|
|
29
|
-
})
|
|
30
|
-
|
|
31
|
-
test('reacts to condition changes', () => {
|
|
32
|
-
let flag = true
|
|
33
|
-
const getter = Show({
|
|
34
|
-
when: () => flag,
|
|
35
|
-
children: 'yes',
|
|
36
|
-
fallback: 'no',
|
|
37
|
-
}) as unknown as () => VNodeChild
|
|
38
|
-
expect(getter()).toBe('yes')
|
|
39
|
-
flag = false
|
|
40
|
-
expect(getter()).toBe('no')
|
|
41
|
-
flag = true
|
|
42
|
-
expect(getter()).toBe('yes')
|
|
43
|
-
})
|
|
44
|
-
|
|
45
|
-
test('returns null for undefined children when truthy', () => {
|
|
46
|
-
const getter = Show({ when: () => true }) as unknown as () => VNodeChild
|
|
47
|
-
expect(getter()).toBeNull()
|
|
48
|
-
})
|
|
49
|
-
|
|
50
|
-
test('returns null for undefined fallback when falsy', () => {
|
|
51
|
-
const getter = Show({ when: () => false, children: 'x' }) as unknown as () => VNodeChild
|
|
52
|
-
expect(getter()).toBeNull()
|
|
53
|
-
})
|
|
54
|
-
|
|
55
|
-
test('VNode children are preserved as-is', () => {
|
|
56
|
-
const child = h('div', null, 'content')
|
|
57
|
-
const getter = Show({ when: () => true, children: child }) as unknown as () => VNodeChild
|
|
58
|
-
expect(getter()).toBe(child)
|
|
59
|
-
})
|
|
60
|
-
|
|
61
|
-
test('truthiness: non-empty string', () => {
|
|
62
|
-
const getter = Show({
|
|
63
|
-
when: () => 'truthy-string',
|
|
64
|
-
children: 'shown',
|
|
65
|
-
}) as unknown as () => VNodeChild
|
|
66
|
-
expect(getter()).toBe('shown')
|
|
67
|
-
})
|
|
68
|
-
|
|
69
|
-
test('truthiness: 0 is falsy', () => {
|
|
70
|
-
const getter = Show({
|
|
71
|
-
when: () => 0,
|
|
72
|
-
children: 'shown',
|
|
73
|
-
fallback: 'hidden',
|
|
74
|
-
}) as unknown as () => VNodeChild
|
|
75
|
-
expect(getter()).toBe('hidden')
|
|
76
|
-
})
|
|
77
|
-
|
|
78
|
-
test('truthiness: empty string is falsy', () => {
|
|
79
|
-
const getter = Show({
|
|
80
|
-
when: () => '',
|
|
81
|
-
children: 'shown',
|
|
82
|
-
fallback: 'hidden',
|
|
83
|
-
}) as unknown as () => VNodeChild
|
|
84
|
-
expect(getter()).toBe('hidden')
|
|
85
|
-
})
|
|
86
|
-
|
|
87
|
-
test('truthiness: null is falsy', () => {
|
|
88
|
-
const getter = Show({
|
|
89
|
-
when: () => null,
|
|
90
|
-
children: 'shown',
|
|
91
|
-
fallback: 'hidden',
|
|
92
|
-
}) as unknown as () => VNodeChild
|
|
93
|
-
expect(getter()).toBe('hidden')
|
|
94
|
-
})
|
|
95
|
-
|
|
96
|
-
test('truthiness: object is truthy', () => {
|
|
97
|
-
const getter = Show({
|
|
98
|
-
when: () => ({ a: 1 }),
|
|
99
|
-
children: 'shown',
|
|
100
|
-
}) as unknown as () => VNodeChild
|
|
101
|
-
expect(getter()).toBe('shown')
|
|
102
|
-
})
|
|
103
|
-
|
|
104
|
-
// Value-or-accessor normalization. Previously `when` was strictly an
|
|
105
|
-
// accessor; passing a value crashed with "props.when is not a function".
|
|
106
|
-
// The compiler's signal auto-call rewrites bare `when={mySignal}` to
|
|
107
|
-
// `when={mySignal()}` at the prop site, producing a value — so the
|
|
108
|
-
// framework now accepts both shapes defensively.
|
|
109
|
-
test('accepts boolean value (true)', () => {
|
|
110
|
-
const getter = Show({ when: true, children: 'shown' }) as unknown as () => VNodeChild
|
|
111
|
-
expect(getter()).toBe('shown')
|
|
112
|
-
})
|
|
113
|
-
|
|
114
|
-
test('accepts boolean value (false)', () => {
|
|
115
|
-
const getter = Show({
|
|
116
|
-
when: false,
|
|
117
|
-
children: 'shown',
|
|
118
|
-
fallback: 'hidden',
|
|
119
|
-
}) as unknown as () => VNodeChild
|
|
120
|
-
expect(getter()).toBe('hidden')
|
|
121
|
-
})
|
|
122
|
-
|
|
123
|
-
test('accepts truthy value (string)', () => {
|
|
124
|
-
const getter = Show({ when: 'yes', children: 'shown' }) as unknown as () => VNodeChild
|
|
125
|
-
expect(getter()).toBe('shown')
|
|
126
|
-
})
|
|
127
|
-
|
|
128
|
-
test('accepts falsy value (0)', () => {
|
|
129
|
-
const getter = Show({
|
|
130
|
-
when: 0,
|
|
131
|
-
children: 'shown',
|
|
132
|
-
fallback: 'hidden',
|
|
133
|
-
}) as unknown as () => VNodeChild
|
|
134
|
-
expect(getter()).toBe('hidden')
|
|
135
|
-
})
|
|
136
|
-
|
|
137
|
-
test('accepts undefined as falsy', () => {
|
|
138
|
-
const getter = Show({
|
|
139
|
-
when: undefined as unknown as boolean,
|
|
140
|
-
children: 'shown',
|
|
141
|
-
fallback: 'hidden',
|
|
142
|
-
}) as unknown as () => VNodeChild
|
|
143
|
-
expect(getter()).toBe('hidden')
|
|
144
|
-
})
|
|
145
|
-
|
|
146
|
-
test('accessor and value forms produce identical behavior', () => {
|
|
147
|
-
const valueGetter = Show({ when: true, children: 'x' }) as unknown as () => VNodeChild
|
|
148
|
-
const accessorGetter = Show({ when: () => true, children: 'x' }) as unknown as () => VNodeChild
|
|
149
|
-
expect(valueGetter()).toBe(accessorGetter())
|
|
150
|
-
})
|
|
151
|
-
})
|
|
152
|
-
|
|
153
|
-
describe('Match', () => {
|
|
154
|
-
test('returns null (marker-only component)', () => {
|
|
155
|
-
const result = Match({ when: () => true, children: 'content' })
|
|
156
|
-
expect(result).toBeNull()
|
|
157
|
-
})
|
|
158
|
-
|
|
159
|
-
test('MatchSymbol is a unique symbol', () => {
|
|
160
|
-
expect(typeof MatchSymbol).toBe('symbol')
|
|
161
|
-
expect(MatchSymbol.toString()).toContain('pyreon.Match')
|
|
162
|
-
})
|
|
163
|
-
})
|
|
164
|
-
|
|
165
|
-
describe('Switch', () => {
|
|
166
|
-
test('renders first truthy Match branch', () => {
|
|
167
|
-
const result = Switch({
|
|
168
|
-
children: [
|
|
169
|
-
h(Match, { when: () => false }, 'first'),
|
|
170
|
-
h(Match, { when: () => true }, 'second'),
|
|
171
|
-
h(Match, { when: () => true }, 'third'),
|
|
172
|
-
],
|
|
173
|
-
})
|
|
174
|
-
const getter = result as unknown as () => VNodeChild
|
|
175
|
-
expect(getter()).toBe('second')
|
|
176
|
-
})
|
|
177
|
-
|
|
178
|
-
test('renders fallback when no match', () => {
|
|
179
|
-
const fb = h('p', null, '404')
|
|
180
|
-
const result = Switch({
|
|
181
|
-
fallback: fb,
|
|
182
|
-
children: [h(Match, { when: () => false }, 'a'), h(Match, { when: () => false }, 'b')],
|
|
183
|
-
})
|
|
184
|
-
const getter = result as unknown as () => VNodeChild
|
|
185
|
-
expect(getter()).toBe(fb)
|
|
186
|
-
})
|
|
187
|
-
|
|
188
|
-
test('returns null when no match and no fallback', () => {
|
|
189
|
-
const result = Switch({
|
|
190
|
-
children: [h(Match, { when: () => false }, 'a')],
|
|
191
|
-
})
|
|
192
|
-
const getter = result as unknown as () => VNodeChild
|
|
193
|
-
expect(getter()).toBeNull()
|
|
194
|
-
})
|
|
195
|
-
|
|
196
|
-
test('handles single child (not array)', () => {
|
|
197
|
-
const result = Switch({
|
|
198
|
-
children: h(Match, { when: () => true }, 'only'),
|
|
199
|
-
})
|
|
200
|
-
const getter = result as unknown as () => VNodeChild
|
|
201
|
-
expect(getter()).toBe('only')
|
|
202
|
-
})
|
|
203
|
-
|
|
204
|
-
test('handles no children', () => {
|
|
205
|
-
const result = Switch({})
|
|
206
|
-
const getter = result as unknown as () => VNodeChild
|
|
207
|
-
expect(getter()).toBeNull()
|
|
208
|
-
})
|
|
209
|
-
|
|
210
|
-
test('handles null/undefined children', () => {
|
|
211
|
-
const result = Switch({ children: null as unknown as VNodeChild })
|
|
212
|
-
const getter = result as unknown as () => VNodeChild
|
|
213
|
-
expect(getter()).toBeNull()
|
|
214
|
-
})
|
|
215
|
-
|
|
216
|
-
test('skips non-Match VNode children', () => {
|
|
217
|
-
const result = Switch({
|
|
218
|
-
fallback: 'default',
|
|
219
|
-
children: [h('div', null, 'not-a-match'), h(Match, { when: () => true }, 'found')],
|
|
220
|
-
})
|
|
221
|
-
const getter = result as unknown as () => VNodeChild
|
|
222
|
-
expect(getter()).toBe('found')
|
|
223
|
-
})
|
|
224
|
-
|
|
225
|
-
test('skips non-object children (strings, null)', () => {
|
|
226
|
-
const result = Switch({
|
|
227
|
-
fallback: 'default',
|
|
228
|
-
children: [
|
|
229
|
-
null as unknown as VNodeChild,
|
|
230
|
-
'string-child' as unknown as VNodeChild,
|
|
231
|
-
h(Match, { when: () => true }, 'found'),
|
|
232
|
-
],
|
|
233
|
-
})
|
|
234
|
-
const getter = result as unknown as () => VNodeChild
|
|
235
|
-
expect(getter()).toBe('found')
|
|
236
|
-
})
|
|
237
|
-
|
|
238
|
-
test('reacts to condition changes', () => {
|
|
239
|
-
let a = false
|
|
240
|
-
let b = false
|
|
241
|
-
const result = Switch({
|
|
242
|
-
fallback: 'none',
|
|
243
|
-
children: [h(Match, { when: () => a }, 'A'), h(Match, { when: () => b }, 'B')],
|
|
244
|
-
})
|
|
245
|
-
const getter = result as unknown as () => VNodeChild
|
|
246
|
-
expect(getter()).toBe('none')
|
|
247
|
-
b = true
|
|
248
|
-
expect(getter()).toBe('B')
|
|
249
|
-
a = true
|
|
250
|
-
expect(getter()).toBe('A') // first match wins
|
|
251
|
-
b = false
|
|
252
|
-
expect(getter()).toBe('A')
|
|
253
|
-
a = false
|
|
254
|
-
expect(getter()).toBe('none')
|
|
255
|
-
})
|
|
256
|
-
|
|
257
|
-
test('Match with multiple children returns array', () => {
|
|
258
|
-
const result = Switch({
|
|
259
|
-
children: [h(Match, { when: () => true }, 'child1', 'child2')],
|
|
260
|
-
})
|
|
261
|
-
const getter = result as unknown as () => VNodeChild
|
|
262
|
-
const value = getter()
|
|
263
|
-
expect(Array.isArray(value)).toBe(true)
|
|
264
|
-
expect(value).toEqual(['child1', 'child2'])
|
|
265
|
-
})
|
|
266
|
-
|
|
267
|
-
test('Match with zero vnode.children falls back to props.children', () => {
|
|
268
|
-
const matchVNode = {
|
|
269
|
-
type: Match,
|
|
270
|
-
props: { when: () => true, children: 'from-props' },
|
|
271
|
-
children: [],
|
|
272
|
-
key: null,
|
|
273
|
-
} as unknown as VNodeChild
|
|
274
|
-
const result = Switch({ children: [matchVNode] })
|
|
275
|
-
const getter = result as unknown as () => VNodeChild
|
|
276
|
-
expect(getter()).toBe('from-props')
|
|
277
|
-
})
|
|
278
|
-
|
|
279
|
-
test('Match with single vnode.children returns it directly (not array)', () => {
|
|
280
|
-
const result = Switch({
|
|
281
|
-
children: [h(Match, { when: () => true }, 'single')],
|
|
282
|
-
})
|
|
283
|
-
const getter = result as unknown as () => VNodeChild
|
|
284
|
-
expect(getter()).toBe('single')
|
|
285
|
-
})
|
|
286
|
-
|
|
287
|
-
// Match `when` accepts value or accessor — same defensive normalization as Show.
|
|
288
|
-
test('Match accepts boolean value', () => {
|
|
289
|
-
const result = Switch({
|
|
290
|
-
fallback: 'none',
|
|
291
|
-
children: [h(Match, { when: true }, 'matched')],
|
|
292
|
-
})
|
|
293
|
-
const getter = result as unknown as () => VNodeChild
|
|
294
|
-
expect(getter()).toBe('matched')
|
|
295
|
-
})
|
|
296
|
-
|
|
297
|
-
test('Match with all-false boolean values renders fallback', () => {
|
|
298
|
-
const result = Switch({
|
|
299
|
-
fallback: 'none',
|
|
300
|
-
children: [h(Match, { when: false }, 'a'), h(Match, { when: false }, 'b')],
|
|
301
|
-
})
|
|
302
|
-
const getter = result as unknown as () => VNodeChild
|
|
303
|
-
expect(getter()).toBe('none')
|
|
304
|
-
})
|
|
305
|
-
|
|
306
|
-
test('Match mixes value and accessor branches in same Switch', () => {
|
|
307
|
-
const result = Switch({
|
|
308
|
-
fallback: 'none',
|
|
309
|
-
children: [h(Match, { when: false }, 'a'), h(Match, { when: () => true }, 'b')],
|
|
310
|
-
})
|
|
311
|
-
const getter = result as unknown as () => VNodeChild
|
|
312
|
-
expect(getter()).toBe('b')
|
|
313
|
-
})
|
|
314
|
-
})
|
package/src/tests/style.test.ts
DELETED
|
@@ -1,157 +0,0 @@
|
|
|
1
|
-
import { CSS_UNITLESS, cx, normalizeStyleValue, toKebabCase } from '../style'
|
|
2
|
-
|
|
3
|
-
// cx() is extensively tested in cx.test.ts — these tests cover toKebabCase,
|
|
4
|
-
// normalizeStyleValue, and CSS_UNITLESS which are used by runtime-dom/runtime-server.
|
|
5
|
-
|
|
6
|
-
describe('toKebabCase', () => {
|
|
7
|
-
test('converts camelCase to kebab-case', () => {
|
|
8
|
-
expect(toKebabCase('backgroundColor')).toBe('background-color')
|
|
9
|
-
})
|
|
10
|
-
|
|
11
|
-
test('handles single uppercase letter', () => {
|
|
12
|
-
expect(toKebabCase('zIndex')).toBe('z-index')
|
|
13
|
-
})
|
|
14
|
-
|
|
15
|
-
test('handles multiple uppercase letters', () => {
|
|
16
|
-
expect(toKebabCase('borderTopLeftRadius')).toBe('border-top-left-radius')
|
|
17
|
-
})
|
|
18
|
-
|
|
19
|
-
test('returns lowercase string unchanged', () => {
|
|
20
|
-
expect(toKebabCase('color')).toBe('color')
|
|
21
|
-
})
|
|
22
|
-
|
|
23
|
-
test('handles empty string', () => {
|
|
24
|
-
expect(toKebabCase('')).toBe('')
|
|
25
|
-
})
|
|
26
|
-
|
|
27
|
-
test('handles consecutive uppercase (treated individually)', () => {
|
|
28
|
-
expect(toKebabCase('MSTransform')).toBe('-m-s-transform')
|
|
29
|
-
})
|
|
30
|
-
|
|
31
|
-
test('handles leading lowercase with single word', () => {
|
|
32
|
-
expect(toKebabCase('opacity')).toBe('opacity')
|
|
33
|
-
})
|
|
34
|
-
})
|
|
35
|
-
|
|
36
|
-
describe('normalizeStyleValue', () => {
|
|
37
|
-
test('appends px to numbers for non-unitless properties', () => {
|
|
38
|
-
expect(normalizeStyleValue('width', 100)).toBe('100px')
|
|
39
|
-
expect(normalizeStyleValue('height', 50)).toBe('50px')
|
|
40
|
-
expect(normalizeStyleValue('padding', 0)).toBe('0px')
|
|
41
|
-
expect(normalizeStyleValue('marginTop', 20)).toBe('20px')
|
|
42
|
-
})
|
|
43
|
-
|
|
44
|
-
test('does not append px to unitless properties', () => {
|
|
45
|
-
expect(normalizeStyleValue('opacity', 0.5)).toBe('0.5')
|
|
46
|
-
expect(normalizeStyleValue('zIndex', 10)).toBe('10')
|
|
47
|
-
expect(normalizeStyleValue('flexGrow', 1)).toBe('1')
|
|
48
|
-
expect(normalizeStyleValue('fontWeight', 700)).toBe('700')
|
|
49
|
-
expect(normalizeStyleValue('lineHeight', 1.5)).toBe('1.5')
|
|
50
|
-
expect(normalizeStyleValue('order', 3)).toBe('3')
|
|
51
|
-
expect(normalizeStyleValue('columns', 2)).toBe('2')
|
|
52
|
-
expect(normalizeStyleValue('flex', 1)).toBe('1')
|
|
53
|
-
expect(normalizeStyleValue('scale', 2)).toBe('2')
|
|
54
|
-
expect(normalizeStyleValue('widows', 2)).toBe('2')
|
|
55
|
-
expect(normalizeStyleValue('orphans', 3)).toBe('3')
|
|
56
|
-
})
|
|
57
|
-
|
|
58
|
-
test('passes through string values unchanged', () => {
|
|
59
|
-
expect(normalizeStyleValue('width', '100%')).toBe('100%')
|
|
60
|
-
expect(normalizeStyleValue('color', 'red')).toBe('red')
|
|
61
|
-
expect(normalizeStyleValue('display', 'flex')).toBe('flex')
|
|
62
|
-
})
|
|
63
|
-
|
|
64
|
-
test('converts non-string/non-number to string', () => {
|
|
65
|
-
expect(normalizeStyleValue('display', null)).toBe('null')
|
|
66
|
-
expect(normalizeStyleValue('display', undefined)).toBe('undefined')
|
|
67
|
-
expect(normalizeStyleValue('display', true)).toBe('true')
|
|
68
|
-
})
|
|
69
|
-
|
|
70
|
-
test('handles zero correctly for non-unitless props', () => {
|
|
71
|
-
expect(normalizeStyleValue('margin', 0)).toBe('0px')
|
|
72
|
-
})
|
|
73
|
-
|
|
74
|
-
test('handles negative numbers', () => {
|
|
75
|
-
expect(normalizeStyleValue('marginLeft', -10)).toBe('-10px')
|
|
76
|
-
expect(normalizeStyleValue('zIndex', -1)).toBe('-1')
|
|
77
|
-
})
|
|
78
|
-
})
|
|
79
|
-
|
|
80
|
-
describe('CSS_UNITLESS', () => {
|
|
81
|
-
test('is a Set', () => {
|
|
82
|
-
expect(CSS_UNITLESS).toBeInstanceOf(Set)
|
|
83
|
-
})
|
|
84
|
-
|
|
85
|
-
test('contains common unitless properties', () => {
|
|
86
|
-
expect(CSS_UNITLESS.has('opacity')).toBe(true)
|
|
87
|
-
expect(CSS_UNITLESS.has('zIndex')).toBe(true)
|
|
88
|
-
expect(CSS_UNITLESS.has('fontWeight')).toBe(true)
|
|
89
|
-
expect(CSS_UNITLESS.has('lineHeight')).toBe(true)
|
|
90
|
-
expect(CSS_UNITLESS.has('flex')).toBe(true)
|
|
91
|
-
expect(CSS_UNITLESS.has('flexGrow')).toBe(true)
|
|
92
|
-
expect(CSS_UNITLESS.has('flexShrink')).toBe(true)
|
|
93
|
-
expect(CSS_UNITLESS.has('order')).toBe(true)
|
|
94
|
-
expect(CSS_UNITLESS.has('columnCount')).toBe(true)
|
|
95
|
-
expect(CSS_UNITLESS.has('animationIterationCount')).toBe(true)
|
|
96
|
-
})
|
|
97
|
-
|
|
98
|
-
test('contains SVG unitless properties', () => {
|
|
99
|
-
expect(CSS_UNITLESS.has('fillOpacity')).toBe(true)
|
|
100
|
-
expect(CSS_UNITLESS.has('floodOpacity')).toBe(true)
|
|
101
|
-
expect(CSS_UNITLESS.has('stopOpacity')).toBe(true)
|
|
102
|
-
expect(CSS_UNITLESS.has('strokeOpacity')).toBe(true)
|
|
103
|
-
expect(CSS_UNITLESS.has('strokeWidth')).toBe(true)
|
|
104
|
-
expect(CSS_UNITLESS.has('strokeMiterlimit')).toBe(true)
|
|
105
|
-
expect(CSS_UNITLESS.has('strokeDasharray')).toBe(true)
|
|
106
|
-
expect(CSS_UNITLESS.has('strokeDashoffset')).toBe(true)
|
|
107
|
-
})
|
|
108
|
-
|
|
109
|
-
test('does not contain properties that need units', () => {
|
|
110
|
-
expect(CSS_UNITLESS.has('width')).toBe(false)
|
|
111
|
-
expect(CSS_UNITLESS.has('height')).toBe(false)
|
|
112
|
-
expect(CSS_UNITLESS.has('margin')).toBe(false)
|
|
113
|
-
expect(CSS_UNITLESS.has('padding')).toBe(false)
|
|
114
|
-
expect(CSS_UNITLESS.has('fontSize')).toBe(false)
|
|
115
|
-
expect(CSS_UNITLESS.has('borderWidth')).toBe(false)
|
|
116
|
-
expect(CSS_UNITLESS.has('top')).toBe(false)
|
|
117
|
-
expect(CSS_UNITLESS.has('left')).toBe(false)
|
|
118
|
-
})
|
|
119
|
-
})
|
|
120
|
-
|
|
121
|
-
describe('cx — additional edge cases', () => {
|
|
122
|
-
test('object with all false values', () => {
|
|
123
|
-
expect(cx({ a: false, b: false, c: false })).toBe('')
|
|
124
|
-
})
|
|
125
|
-
|
|
126
|
-
test('object with null and undefined values', () => {
|
|
127
|
-
expect(cx({ a: null, b: undefined, c: true })).toBe('c')
|
|
128
|
-
})
|
|
129
|
-
|
|
130
|
-
test('mixed array of numbers, strings, and objects', () => {
|
|
131
|
-
expect(cx([1, 'two', { three: true, four: false }])).toBe('1 two three')
|
|
132
|
-
})
|
|
133
|
-
|
|
134
|
-
test('single-element array', () => {
|
|
135
|
-
expect(cx(['only'])).toBe('only')
|
|
136
|
-
})
|
|
137
|
-
|
|
138
|
-
test('number 0 in an array', () => {
|
|
139
|
-
expect(cx([0, 'one'])).toBe('0 one')
|
|
140
|
-
})
|
|
141
|
-
|
|
142
|
-
test('boolean true in array is filtered', () => {
|
|
143
|
-
expect(cx([true, 'visible'])).toBe('visible')
|
|
144
|
-
})
|
|
145
|
-
|
|
146
|
-
test('nested empty arrays', () => {
|
|
147
|
-
expect(cx([[], [[]], [[[]]]])).toBe('')
|
|
148
|
-
})
|
|
149
|
-
|
|
150
|
-
test('object with function returning false', () => {
|
|
151
|
-
expect(cx({ hidden: () => false })).toBe('')
|
|
152
|
-
})
|
|
153
|
-
|
|
154
|
-
test('single key object', () => {
|
|
155
|
-
expect(cx({ active: true })).toBe('active')
|
|
156
|
-
})
|
|
157
|
-
})
|
|
@@ -1,139 +0,0 @@
|
|
|
1
|
-
import { Fragment, h } from '../h'
|
|
2
|
-
import { Suspense } from '../suspense'
|
|
3
|
-
import type { ComponentFn, VNodeChild } from '../types'
|
|
4
|
-
|
|
5
|
-
describe('Suspense', () => {
|
|
6
|
-
test('returns a Fragment VNode', () => {
|
|
7
|
-
const node = Suspense({
|
|
8
|
-
fallback: h('div', null, 'loading'),
|
|
9
|
-
children: h('div', null, 'content'),
|
|
10
|
-
})
|
|
11
|
-
expect(node.type).toBe(Fragment)
|
|
12
|
-
})
|
|
13
|
-
|
|
14
|
-
test('Fragment contains a single reactive getter child', () => {
|
|
15
|
-
const node = Suspense({
|
|
16
|
-
fallback: h('span', null, 'loading'),
|
|
17
|
-
children: h('div', null, 'content'),
|
|
18
|
-
})
|
|
19
|
-
expect(node.children).toHaveLength(1)
|
|
20
|
-
expect(typeof node.children[0]).toBe('function')
|
|
21
|
-
})
|
|
22
|
-
|
|
23
|
-
test('renders children when not loading (plain VNode)', () => {
|
|
24
|
-
const child = h('div', null, 'loaded')
|
|
25
|
-
const node = Suspense({
|
|
26
|
-
fallback: h('span', null, 'loading'),
|
|
27
|
-
children: child,
|
|
28
|
-
})
|
|
29
|
-
const getter = node.children[0] as () => VNodeChild
|
|
30
|
-
expect(getter()).toBe(child)
|
|
31
|
-
})
|
|
32
|
-
|
|
33
|
-
test('renders children when child type has no __loading', () => {
|
|
34
|
-
const regularComp: ComponentFn = () => h('div', null)
|
|
35
|
-
const child = h(regularComp, null)
|
|
36
|
-
const node = Suspense({
|
|
37
|
-
fallback: 'loading',
|
|
38
|
-
children: child,
|
|
39
|
-
})
|
|
40
|
-
const getter = node.children[0] as () => VNodeChild
|
|
41
|
-
expect(getter()).toBe(child)
|
|
42
|
-
})
|
|
43
|
-
|
|
44
|
-
test('renders fallback when child __loading() is true', () => {
|
|
45
|
-
const fallback = h('span', null, 'loading...')
|
|
46
|
-
const lazyFn = (() => h('div', null)) as unknown as ComponentFn & {
|
|
47
|
-
__loading: () => boolean
|
|
48
|
-
}
|
|
49
|
-
lazyFn.__loading = () => true
|
|
50
|
-
const child = h(lazyFn, null)
|
|
51
|
-
|
|
52
|
-
const node = Suspense({ fallback, children: child })
|
|
53
|
-
const getter = node.children[0] as () => VNodeChild
|
|
54
|
-
expect(getter()).toBe(fallback)
|
|
55
|
-
})
|
|
56
|
-
|
|
57
|
-
test('renders children when __loading() is false', () => {
|
|
58
|
-
const fallback = h('span', null, 'loading...')
|
|
59
|
-
const lazyFn = (() => h('div', null)) as unknown as ComponentFn & {
|
|
60
|
-
__loading: () => boolean
|
|
61
|
-
}
|
|
62
|
-
lazyFn.__loading = () => false
|
|
63
|
-
const child = h(lazyFn, null)
|
|
64
|
-
|
|
65
|
-
const node = Suspense({ fallback, children: child })
|
|
66
|
-
const getter = node.children[0] as () => VNodeChild
|
|
67
|
-
expect(getter()).toBe(child)
|
|
68
|
-
})
|
|
69
|
-
|
|
70
|
-
test('handles function children (reactive getter)', () => {
|
|
71
|
-
const child = h('div', null, 'content')
|
|
72
|
-
const node = Suspense({
|
|
73
|
-
fallback: h('span', null, 'loading'),
|
|
74
|
-
children: () => child,
|
|
75
|
-
})
|
|
76
|
-
const getter = node.children[0] as () => VNodeChild
|
|
77
|
-
expect(getter()).toBe(child)
|
|
78
|
-
})
|
|
79
|
-
|
|
80
|
-
test('evaluates function fallback', () => {
|
|
81
|
-
const fbNode = h('div', null, 'fb')
|
|
82
|
-
const lazyFn = (() => h('div', null)) as unknown as ComponentFn & {
|
|
83
|
-
__loading: () => boolean
|
|
84
|
-
}
|
|
85
|
-
lazyFn.__loading = () => true
|
|
86
|
-
const child = h(lazyFn, null)
|
|
87
|
-
|
|
88
|
-
const node = Suspense({ fallback: () => fbNode, children: child })
|
|
89
|
-
const getter = node.children[0] as () => VNodeChild
|
|
90
|
-
expect(getter()).toBe(fbNode)
|
|
91
|
-
})
|
|
92
|
-
|
|
93
|
-
test('handles null/undefined children', () => {
|
|
94
|
-
const node = Suspense({ fallback: 'loading' })
|
|
95
|
-
const getter = node.children[0] as () => VNodeChild
|
|
96
|
-
// undefined children — not a VNode, not loading
|
|
97
|
-
expect(getter()).toBeUndefined()
|
|
98
|
-
})
|
|
99
|
-
|
|
100
|
-
test('handles string children', () => {
|
|
101
|
-
const node = Suspense({ fallback: 'loading', children: 'text content' })
|
|
102
|
-
const getter = node.children[0] as () => VNodeChild
|
|
103
|
-
expect(getter()).toBe('text content')
|
|
104
|
-
})
|
|
105
|
-
|
|
106
|
-
test('handles array children (not loading)', () => {
|
|
107
|
-
const children = [h('a', null), h('b', null)]
|
|
108
|
-
const node = Suspense({
|
|
109
|
-
fallback: 'loading',
|
|
110
|
-
children: children as unknown as VNodeChild,
|
|
111
|
-
})
|
|
112
|
-
const getter = node.children[0] as () => VNodeChild
|
|
113
|
-
expect(getter()).toBe(children)
|
|
114
|
-
})
|
|
115
|
-
|
|
116
|
-
test('warns when fallback prop is missing', () => {
|
|
117
|
-
const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {})
|
|
118
|
-
Suspense({ fallback: undefined as unknown as VNodeChild, children: 'x' })
|
|
119
|
-
expect(warnSpy).toHaveBeenCalledWith(expect.stringContaining('<Suspense>'))
|
|
120
|
-
warnSpy.mockRestore()
|
|
121
|
-
})
|
|
122
|
-
|
|
123
|
-
test('transition from loading to loaded', () => {
|
|
124
|
-
let isLoading = true
|
|
125
|
-
const lazyFn = (() => h('div', null)) as unknown as ComponentFn & {
|
|
126
|
-
__loading: () => boolean
|
|
127
|
-
}
|
|
128
|
-
lazyFn.__loading = () => isLoading
|
|
129
|
-
const child = h(lazyFn, null)
|
|
130
|
-
const fallback = h('span', null, 'loading')
|
|
131
|
-
|
|
132
|
-
const node = Suspense({ fallback, children: child })
|
|
133
|
-
const getter = node.children[0] as () => VNodeChild
|
|
134
|
-
|
|
135
|
-
expect(getter()).toBe(fallback)
|
|
136
|
-
isLoading = false
|
|
137
|
-
expect(getter()).toBe(child)
|
|
138
|
-
})
|
|
139
|
-
})
|