@pyreon/core 0.11.5 → 0.11.7
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/README.md +2 -2
- package/lib/analysis/index.js.html +1 -1
- package/lib/index.js +33 -5
- package/lib/index.js.map +1 -1
- package/lib/jsx-dev-runtime.js.map +1 -1
- package/lib/jsx-runtime.js.map +1 -1
- package/lib/types/index.d.ts +145 -98
- package/lib/types/index.d.ts.map +1 -1
- package/lib/types/jsx-dev-runtime.d.ts +94 -94
- package/lib/types/jsx-runtime.d.ts +94 -94
- package/package.json +11 -11
- package/src/component.ts +2 -2
- package/src/context.ts +75 -4
- package/src/dynamic.ts +4 -4
- package/src/error-boundary.ts +10 -10
- package/src/for.ts +8 -2
- package/src/h.ts +4 -4
- package/src/index.ts +30 -27
- package/src/jsx-dev-runtime.ts +1 -1
- package/src/jsx-runtime.ts +108 -108
- package/src/lazy.ts +4 -4
- package/src/lifecycle.ts +6 -6
- package/src/portal.ts +2 -2
- package/src/show.ts +4 -4
- package/src/style.ts +51 -51
- package/src/suspense.ts +8 -8
- package/src/telemetry.ts +1 -1
- package/src/tests/component.test.ts +60 -60
- package/src/tests/context.test.ts +102 -102
- package/src/tests/core.test.ts +376 -376
- package/src/tests/cx.test.ts +34 -34
- package/src/tests/dynamic.test.ts +28 -28
- package/src/tests/error-boundary.test.ts +51 -51
- package/src/tests/for.test.ts +26 -26
- package/src/tests/h.test.ts +100 -100
- package/src/tests/jsx-compat.test.tsx +41 -41
- package/src/tests/lazy.test.ts +28 -28
- package/src/tests/lifecycle.test.ts +35 -35
- package/src/tests/map-array.test.ts +36 -36
- package/src/tests/portal.test.ts +21 -21
- package/src/tests/props-extended.test.ts +51 -51
- package/src/tests/props.test.ts +62 -62
- package/src/tests/ref.test.ts +20 -20
- package/src/tests/show.test.ts +94 -94
- package/src/tests/style.test.ts +101 -101
- package/src/tests/suspense.test.ts +44 -44
- package/src/tests/telemetry.test.ts +35 -35
package/src/tests/cx.test.ts
CHANGED
|
@@ -1,45 +1,45 @@
|
|
|
1
|
-
import { cx } from
|
|
1
|
+
import { cx } from '../style'
|
|
2
2
|
|
|
3
|
-
describe(
|
|
4
|
-
test(
|
|
5
|
-
expect(cx(null)).toBe(
|
|
6
|
-
expect(cx(undefined)).toBe(
|
|
7
|
-
expect(cx(false)).toBe(
|
|
8
|
-
expect(cx(true)).toBe(
|
|
3
|
+
describe('cx', () => {
|
|
4
|
+
test('returns empty string for falsy values', () => {
|
|
5
|
+
expect(cx(null)).toBe('')
|
|
6
|
+
expect(cx(undefined)).toBe('')
|
|
7
|
+
expect(cx(false)).toBe('')
|
|
8
|
+
expect(cx(true)).toBe('')
|
|
9
9
|
})
|
|
10
10
|
|
|
11
|
-
test(
|
|
12
|
-
expect(cx(
|
|
11
|
+
test('passes through strings', () => {
|
|
12
|
+
expect(cx('foo bar')).toBe('foo bar')
|
|
13
13
|
})
|
|
14
14
|
|
|
15
|
-
test(
|
|
16
|
-
expect(cx(42)).toBe(
|
|
15
|
+
test('converts numbers to strings', () => {
|
|
16
|
+
expect(cx(42)).toBe('42')
|
|
17
17
|
})
|
|
18
18
|
|
|
19
|
-
test(
|
|
20
|
-
expect(cx([
|
|
19
|
+
test('filters and joins arrays', () => {
|
|
20
|
+
expect(cx(['foo', false, 'bar', null, 'baz'])).toBe('foo bar baz')
|
|
21
21
|
})
|
|
22
22
|
|
|
23
|
-
test(
|
|
24
|
-
expect(cx({ active: true, hidden: false, bold: true })).toBe(
|
|
23
|
+
test('resolves object keys with truthy values', () => {
|
|
24
|
+
expect(cx({ active: true, hidden: false, bold: true })).toBe('active bold')
|
|
25
25
|
})
|
|
26
26
|
|
|
27
|
-
test(
|
|
28
|
-
expect(cx({ active: () => true, hidden: () => false })).toBe(
|
|
27
|
+
test('resolves object values that are functions', () => {
|
|
28
|
+
expect(cx({ active: () => true, hidden: () => false })).toBe('active')
|
|
29
29
|
})
|
|
30
30
|
|
|
31
|
-
test(
|
|
32
|
-
expect(cx([
|
|
33
|
-
|
|
31
|
+
test('handles nested arrays and objects', () => {
|
|
32
|
+
expect(cx(['base', { active: true }, ['nested', { deep: true }]])).toBe(
|
|
33
|
+
'base active nested deep',
|
|
34
34
|
)
|
|
35
35
|
})
|
|
36
36
|
|
|
37
|
-
test(
|
|
38
|
-
expect(cx([])).toBe(
|
|
39
|
-
expect(cx({})).toBe(
|
|
37
|
+
test('handles empty inputs', () => {
|
|
38
|
+
expect(cx([])).toBe('')
|
|
39
|
+
expect(cx({})).toBe('')
|
|
40
40
|
})
|
|
41
41
|
|
|
42
|
-
test(
|
|
42
|
+
test('object where ALL values are functions returning booleans', () => {
|
|
43
43
|
expect(
|
|
44
44
|
cx({
|
|
45
45
|
active: () => true,
|
|
@@ -47,24 +47,24 @@ describe("cx", () => {
|
|
|
47
47
|
bold: () => true,
|
|
48
48
|
italic: () => false,
|
|
49
49
|
}),
|
|
50
|
-
).toBe(
|
|
50
|
+
).toBe('active bold')
|
|
51
51
|
})
|
|
52
52
|
|
|
53
|
-
test(
|
|
54
|
-
expect(cx([[[
|
|
53
|
+
test('deeply nested arrays (3+ levels)', () => {
|
|
54
|
+
expect(cx([[['level3', [['level5']]]]])).toBe('level3 level5')
|
|
55
55
|
})
|
|
56
56
|
|
|
57
|
-
test(
|
|
58
|
-
expect(cx([
|
|
59
|
-
|
|
57
|
+
test('mixed: string, object with function, deeply nested array', () => {
|
|
58
|
+
expect(cx(['base', { active: () => true }, [['deeply-nested']]])).toBe(
|
|
59
|
+
'base active deeply-nested',
|
|
60
60
|
)
|
|
61
61
|
})
|
|
62
62
|
|
|
63
|
-
test(
|
|
64
|
-
expect(cx([
|
|
63
|
+
test('empty string values are filtered', () => {
|
|
64
|
+
expect(cx(['foo', '', 'bar', ''])).toBe('foo bar')
|
|
65
65
|
})
|
|
66
66
|
|
|
67
|
-
test(
|
|
68
|
-
expect(cx(0)).toBe(
|
|
67
|
+
test('number 0 is a valid class name as string', () => {
|
|
68
|
+
expect(cx(0)).toBe('0')
|
|
69
69
|
})
|
|
70
70
|
})
|
|
@@ -1,55 +1,55 @@
|
|
|
1
|
-
import { Dynamic } from
|
|
2
|
-
import { h } from
|
|
3
|
-
import type { ComponentFn, VNode } from
|
|
1
|
+
import { Dynamic } from '../dynamic'
|
|
2
|
+
import { h } from '../h'
|
|
3
|
+
import type { ComponentFn, VNode } from '../types'
|
|
4
4
|
|
|
5
|
-
describe(
|
|
6
|
-
test(
|
|
7
|
-
const Greeting: ComponentFn = (props) => h(
|
|
8
|
-
const result = Dynamic({ component: Greeting, name:
|
|
5
|
+
describe('Dynamic', () => {
|
|
6
|
+
test('renders component function', () => {
|
|
7
|
+
const Greeting: ComponentFn = (props) => h('span', null, (props as { name: string }).name)
|
|
8
|
+
const result = Dynamic({ component: Greeting, name: 'world' })
|
|
9
9
|
expect(result).not.toBeNull()
|
|
10
10
|
expect((result as VNode).type).toBe(Greeting)
|
|
11
|
-
expect((result as VNode).props).toEqual({ name:
|
|
11
|
+
expect((result as VNode).props).toEqual({ name: 'world' })
|
|
12
12
|
})
|
|
13
13
|
|
|
14
|
-
test(
|
|
15
|
-
const result = Dynamic({ component:
|
|
14
|
+
test('renders string element', () => {
|
|
15
|
+
const result = Dynamic({ component: 'div', class: 'box', id: 'main' })
|
|
16
16
|
expect(result).not.toBeNull()
|
|
17
|
-
expect((result as VNode).type).toBe(
|
|
18
|
-
expect((result as VNode).props).toEqual({ class:
|
|
17
|
+
expect((result as VNode).type).toBe('div')
|
|
18
|
+
expect((result as VNode).props).toEqual({ class: 'box', id: 'main' })
|
|
19
19
|
})
|
|
20
20
|
|
|
21
|
-
test(
|
|
22
|
-
const result = Dynamic({ component:
|
|
21
|
+
test('strips component prop from rest props', () => {
|
|
22
|
+
const result = Dynamic({ component: 'span', id: 'x' })
|
|
23
23
|
expect((result as VNode).props.component).toBeUndefined()
|
|
24
|
-
expect((result as VNode).props.id).toBe(
|
|
24
|
+
expect((result as VNode).props.id).toBe('x')
|
|
25
25
|
})
|
|
26
26
|
|
|
27
|
-
test(
|
|
28
|
-
const result = Dynamic({ component:
|
|
27
|
+
test('returns null for empty string component', () => {
|
|
28
|
+
const result = Dynamic({ component: '' })
|
|
29
29
|
expect(result).toBeNull()
|
|
30
30
|
})
|
|
31
31
|
|
|
32
|
-
test(
|
|
33
|
-
const warnSpy = vi.spyOn(console,
|
|
34
|
-
Dynamic({ component:
|
|
35
|
-
expect(warnSpy).toHaveBeenCalledWith(expect.stringContaining(
|
|
32
|
+
test('warns when component prop is falsy', () => {
|
|
33
|
+
const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {})
|
|
34
|
+
Dynamic({ component: '' })
|
|
35
|
+
expect(warnSpy).toHaveBeenCalledWith(expect.stringContaining('<Dynamic>'))
|
|
36
36
|
warnSpy.mockRestore()
|
|
37
37
|
})
|
|
38
38
|
|
|
39
|
-
test(
|
|
40
|
-
const Comp: ComponentFn = (props) => h(
|
|
39
|
+
test('passes all extra props to the rendered component', () => {
|
|
40
|
+
const Comp: ComponentFn = (props) => h('div', null, JSON.stringify(props))
|
|
41
41
|
const result = Dynamic({
|
|
42
42
|
component: Comp,
|
|
43
43
|
a: 1,
|
|
44
|
-
b:
|
|
44
|
+
b: 'two',
|
|
45
45
|
c: true,
|
|
46
46
|
})
|
|
47
|
-
expect((result as VNode).props).toEqual({ a: 1, b:
|
|
47
|
+
expect((result as VNode).props).toEqual({ a: 1, b: 'two', c: true })
|
|
48
48
|
})
|
|
49
49
|
|
|
50
|
-
test(
|
|
51
|
-
const result = Dynamic({ component:
|
|
50
|
+
test('renders with no extra props', () => {
|
|
51
|
+
const result = Dynamic({ component: 'br' })
|
|
52
52
|
expect(result).not.toBeNull()
|
|
53
|
-
expect((result as VNode).type).toBe(
|
|
53
|
+
expect((result as VNode).type).toBe('br')
|
|
54
54
|
})
|
|
55
55
|
})
|
|
@@ -1,62 +1,62 @@
|
|
|
1
|
-
import { dispatchToErrorBoundary, popErrorBoundary, runWithHooks } from
|
|
2
|
-
import { ErrorBoundary } from
|
|
3
|
-
import { h } from
|
|
4
|
-
import type { VNodeChild } from
|
|
1
|
+
import { dispatchToErrorBoundary, popErrorBoundary, runWithHooks } from '../component'
|
|
2
|
+
import { ErrorBoundary } from '../error-boundary'
|
|
3
|
+
import { h } from '../h'
|
|
4
|
+
import type { VNodeChild } from '../types'
|
|
5
5
|
|
|
6
|
-
describe(
|
|
6
|
+
describe('ErrorBoundary', () => {
|
|
7
7
|
// Clean up error boundary stack after each test
|
|
8
8
|
afterEach(() => {
|
|
9
9
|
// Pop all boundaries that tests may have left
|
|
10
|
-
while (dispatchToErrorBoundary(
|
|
10
|
+
while (dispatchToErrorBoundary('cleanup')) {
|
|
11
11
|
popErrorBoundary()
|
|
12
12
|
}
|
|
13
13
|
})
|
|
14
14
|
|
|
15
|
-
test(
|
|
16
|
-
expect(typeof ErrorBoundary).toBe(
|
|
15
|
+
test('is a function', () => {
|
|
16
|
+
expect(typeof ErrorBoundary).toBe('function')
|
|
17
17
|
})
|
|
18
18
|
|
|
19
|
-
test(
|
|
19
|
+
test('returns a reactive getter', () => {
|
|
20
20
|
let result: VNodeChild = null
|
|
21
21
|
runWithHooks(() => {
|
|
22
22
|
result = ErrorBoundary({
|
|
23
23
|
fallback: (err) => `Error: ${err}`,
|
|
24
|
-
children:
|
|
24
|
+
children: 'child',
|
|
25
25
|
})
|
|
26
26
|
return null
|
|
27
27
|
}, {})
|
|
28
|
-
expect(typeof result).toBe(
|
|
28
|
+
expect(typeof result).toBe('function')
|
|
29
29
|
})
|
|
30
30
|
|
|
31
|
-
test(
|
|
31
|
+
test('renders children when no error', () => {
|
|
32
32
|
let result: VNodeChild = null
|
|
33
33
|
runWithHooks(() => {
|
|
34
34
|
result = ErrorBoundary({
|
|
35
35
|
fallback: (err) => `Error: ${err}`,
|
|
36
|
-
children:
|
|
36
|
+
children: 'child content',
|
|
37
37
|
})
|
|
38
38
|
return null
|
|
39
39
|
}, {})
|
|
40
40
|
const getter = result as unknown as () => VNodeChild
|
|
41
|
-
expect(getter()).toBe(
|
|
41
|
+
expect(getter()).toBe('child content')
|
|
42
42
|
})
|
|
43
43
|
|
|
44
|
-
test(
|
|
44
|
+
test('renders function children by calling them', () => {
|
|
45
45
|
let result: VNodeChild = null
|
|
46
46
|
runWithHooks(() => {
|
|
47
47
|
result = ErrorBoundary({
|
|
48
48
|
fallback: (err) => `Error: ${err}`,
|
|
49
|
-
children: () =>
|
|
49
|
+
children: () => 'dynamic child',
|
|
50
50
|
})
|
|
51
51
|
return null
|
|
52
52
|
}, {})
|
|
53
53
|
const getter = result as unknown as () => VNodeChild
|
|
54
|
-
expect(getter()).toBe(
|
|
54
|
+
expect(getter()).toBe('dynamic child')
|
|
55
55
|
})
|
|
56
56
|
|
|
57
|
-
test(
|
|
57
|
+
test('renders VNode children', () => {
|
|
58
58
|
let result: VNodeChild = null
|
|
59
|
-
const child = h(
|
|
59
|
+
const child = h('div', null, 'content')
|
|
60
60
|
runWithHooks(() => {
|
|
61
61
|
result = ErrorBoundary({
|
|
62
62
|
fallback: (err) => `Error: ${err}`,
|
|
@@ -68,92 +68,92 @@ describe("ErrorBoundary", () => {
|
|
|
68
68
|
expect(getter()).toBe(child)
|
|
69
69
|
})
|
|
70
70
|
|
|
71
|
-
test(
|
|
71
|
+
test('registers unmount cleanup hook', () => {
|
|
72
72
|
const { hooks } = runWithHooks(() => {
|
|
73
73
|
ErrorBoundary({
|
|
74
74
|
fallback: (err) => `Error: ${err}`,
|
|
75
|
-
children:
|
|
75
|
+
children: 'child',
|
|
76
76
|
})
|
|
77
77
|
return null
|
|
78
78
|
}, {})
|
|
79
79
|
expect(hooks.unmount.length).toBeGreaterThanOrEqual(1)
|
|
80
80
|
})
|
|
81
81
|
|
|
82
|
-
test(
|
|
83
|
-
const warnSpy = vi.spyOn(console,
|
|
82
|
+
test('warns when fallback is not a function', () => {
|
|
83
|
+
const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {})
|
|
84
84
|
runWithHooks(() => {
|
|
85
85
|
ErrorBoundary({
|
|
86
|
-
fallback:
|
|
87
|
-
children:
|
|
86
|
+
fallback: 'not-a-function' as unknown as (err: unknown, reset: () => void) => VNodeChild,
|
|
87
|
+
children: 'child',
|
|
88
88
|
})
|
|
89
89
|
return null
|
|
90
90
|
}, {})
|
|
91
|
-
expect(warnSpy).toHaveBeenCalledWith(expect.stringContaining(
|
|
91
|
+
expect(warnSpy).toHaveBeenCalledWith(expect.stringContaining('<ErrorBoundary>'))
|
|
92
92
|
warnSpy.mockRestore()
|
|
93
93
|
})
|
|
94
94
|
|
|
95
|
-
test(
|
|
95
|
+
test('dispatched error triggers fallback rendering', () => {
|
|
96
96
|
let result: VNodeChild = null
|
|
97
97
|
runWithHooks(() => {
|
|
98
98
|
result = ErrorBoundary({
|
|
99
99
|
fallback: (err) => `Caught: ${err}`,
|
|
100
|
-
children:
|
|
100
|
+
children: 'normal',
|
|
101
101
|
})
|
|
102
102
|
return null
|
|
103
103
|
}, {})
|
|
104
104
|
const getter = result as unknown as () => VNodeChild
|
|
105
|
-
expect(getter()).toBe(
|
|
105
|
+
expect(getter()).toBe('normal')
|
|
106
106
|
|
|
107
|
-
dispatchToErrorBoundary(new Error(
|
|
108
|
-
expect(getter()).toBe(
|
|
107
|
+
dispatchToErrorBoundary(new Error('boom'))
|
|
108
|
+
expect(getter()).toBe('Caught: Error: boom')
|
|
109
109
|
})
|
|
110
110
|
|
|
111
|
-
test(
|
|
111
|
+
test('fallback receives reset function that clears error', () => {
|
|
112
112
|
let result: VNodeChild = null
|
|
113
113
|
let capturedReset: (() => void) | undefined
|
|
114
114
|
runWithHooks(() => {
|
|
115
115
|
result = ErrorBoundary({
|
|
116
116
|
fallback: (_err, reset) => {
|
|
117
117
|
capturedReset = reset
|
|
118
|
-
return
|
|
118
|
+
return 'error-ui'
|
|
119
119
|
},
|
|
120
|
-
children:
|
|
120
|
+
children: 'child',
|
|
121
121
|
})
|
|
122
122
|
return null
|
|
123
123
|
}, {})
|
|
124
124
|
const getter = result as unknown as () => VNodeChild
|
|
125
|
-
expect(getter()).toBe(
|
|
125
|
+
expect(getter()).toBe('child')
|
|
126
126
|
|
|
127
|
-
dispatchToErrorBoundary(
|
|
128
|
-
expect(getter()).toBe(
|
|
127
|
+
dispatchToErrorBoundary('test-error')
|
|
128
|
+
expect(getter()).toBe('error-ui')
|
|
129
129
|
expect(capturedReset).toBeDefined()
|
|
130
130
|
|
|
131
131
|
capturedReset?.()
|
|
132
|
-
expect(getter()).toBe(
|
|
132
|
+
expect(getter()).toBe('child')
|
|
133
133
|
})
|
|
134
134
|
|
|
135
|
-
test(
|
|
135
|
+
test('second error while already in error state is not handled', () => {
|
|
136
136
|
let result: VNodeChild = null
|
|
137
137
|
runWithHooks(() => {
|
|
138
138
|
result = ErrorBoundary({
|
|
139
139
|
fallback: (err) => `Error: ${err}`,
|
|
140
|
-
children:
|
|
140
|
+
children: 'child',
|
|
141
141
|
})
|
|
142
142
|
return null
|
|
143
143
|
}, {})
|
|
144
144
|
const getter = result as unknown as () => VNodeChild
|
|
145
145
|
|
|
146
146
|
// First error handled
|
|
147
|
-
expect(dispatchToErrorBoundary(
|
|
148
|
-
expect(getter()).toBe(
|
|
147
|
+
expect(dispatchToErrorBoundary('first')).toBe(true)
|
|
148
|
+
expect(getter()).toBe('Error: first')
|
|
149
149
|
|
|
150
150
|
// Second error not handled (already in error state)
|
|
151
|
-
expect(dispatchToErrorBoundary(
|
|
151
|
+
expect(dispatchToErrorBoundary('second')).toBe(false)
|
|
152
152
|
// Still showing first error
|
|
153
|
-
expect(getter()).toBe(
|
|
153
|
+
expect(getter()).toBe('Error: first')
|
|
154
154
|
})
|
|
155
155
|
|
|
156
|
-
test(
|
|
156
|
+
test('after reset, new error can be caught again', () => {
|
|
157
157
|
let result: VNodeChild = null
|
|
158
158
|
let capturedReset: (() => void) | undefined
|
|
159
159
|
runWithHooks(() => {
|
|
@@ -162,20 +162,20 @@ describe("ErrorBoundary", () => {
|
|
|
162
162
|
capturedReset = reset
|
|
163
163
|
return `Error: ${err}`
|
|
164
164
|
},
|
|
165
|
-
children:
|
|
165
|
+
children: 'child',
|
|
166
166
|
})
|
|
167
167
|
return null
|
|
168
168
|
}, {})
|
|
169
169
|
const getter = result as unknown as () => VNodeChild
|
|
170
170
|
|
|
171
|
-
dispatchToErrorBoundary(
|
|
172
|
-
expect(getter()).toBe(
|
|
171
|
+
dispatchToErrorBoundary('first-error')
|
|
172
|
+
expect(getter()).toBe('Error: first-error')
|
|
173
173
|
|
|
174
174
|
capturedReset?.()
|
|
175
|
-
expect(getter()).toBe(
|
|
175
|
+
expect(getter()).toBe('child')
|
|
176
176
|
|
|
177
177
|
// Can catch new error after reset
|
|
178
|
-
expect(dispatchToErrorBoundary(
|
|
179
|
-
expect(getter()).toBe(
|
|
178
|
+
expect(dispatchToErrorBoundary('second-error')).toBe(true)
|
|
179
|
+
expect(getter()).toBe('Error: second-error')
|
|
180
180
|
})
|
|
181
181
|
})
|
package/src/tests/for.test.ts
CHANGED
|
@@ -1,39 +1,39 @@
|
|
|
1
|
-
import { For, ForSymbol } from
|
|
2
|
-
import { h } from
|
|
3
|
-
import type { VNode } from
|
|
1
|
+
import { For, ForSymbol } from '../for'
|
|
2
|
+
import { h } from '../h'
|
|
3
|
+
import type { VNode } from '../types'
|
|
4
4
|
|
|
5
|
-
describe(
|
|
6
|
-
test(
|
|
5
|
+
describe('For', () => {
|
|
6
|
+
test('returns VNode with ForSymbol type', () => {
|
|
7
7
|
const node = For({
|
|
8
8
|
each: () => [1, 2, 3],
|
|
9
9
|
by: (item) => item,
|
|
10
|
-
children: (item) => h(
|
|
10
|
+
children: (item) => h('li', null, String(item)),
|
|
11
11
|
})
|
|
12
12
|
expect(node.type).toBe(ForSymbol)
|
|
13
13
|
})
|
|
14
14
|
|
|
15
|
-
test(
|
|
15
|
+
test('VNode has empty children array', () => {
|
|
16
16
|
const node = For({
|
|
17
17
|
each: () => [],
|
|
18
18
|
by: (item: number) => item,
|
|
19
|
-
children: (item) => h(
|
|
19
|
+
children: (item) => h('span', null, String(item)),
|
|
20
20
|
})
|
|
21
21
|
expect(node.children).toEqual([])
|
|
22
22
|
})
|
|
23
23
|
|
|
24
|
-
test(
|
|
24
|
+
test('VNode has null key', () => {
|
|
25
25
|
const node = For({
|
|
26
26
|
each: () => [1],
|
|
27
27
|
by: (item) => item,
|
|
28
|
-
children: (item) => h(
|
|
28
|
+
children: (item) => h('li', null, String(item)),
|
|
29
29
|
})
|
|
30
30
|
expect(node.key).toBeNull()
|
|
31
31
|
})
|
|
32
32
|
|
|
33
|
-
test(
|
|
34
|
-
const eachFn = () => [
|
|
33
|
+
test('props contain each, by, children functions', () => {
|
|
34
|
+
const eachFn = () => ['a', 'b']
|
|
35
35
|
const byFn = (item: string) => item
|
|
36
|
-
const childFn = (item: string) => h(
|
|
36
|
+
const childFn = (item: string) => h('span', null, item)
|
|
37
37
|
const node = For({ each: eachFn, by: byFn, children: childFn })
|
|
38
38
|
|
|
39
39
|
const props = node.props as unknown as {
|
|
@@ -46,41 +46,41 @@ describe("For", () => {
|
|
|
46
46
|
expect(props.children).toBe(childFn)
|
|
47
47
|
})
|
|
48
48
|
|
|
49
|
-
test(
|
|
50
|
-
expect(typeof ForSymbol).toBe(
|
|
51
|
-
expect(ForSymbol.toString()).toContain(
|
|
49
|
+
test('ForSymbol is a unique symbol', () => {
|
|
50
|
+
expect(typeof ForSymbol).toBe('symbol')
|
|
51
|
+
expect(ForSymbol.toString()).toContain('pyreon.For')
|
|
52
52
|
})
|
|
53
53
|
|
|
54
|
-
test(
|
|
54
|
+
test('works with object items', () => {
|
|
55
55
|
interface Item {
|
|
56
56
|
id: number
|
|
57
57
|
name: string
|
|
58
58
|
}
|
|
59
59
|
const items: Item[] = [
|
|
60
|
-
{ id: 1, name:
|
|
61
|
-
{ id: 2, name:
|
|
60
|
+
{ id: 1, name: 'one' },
|
|
61
|
+
{ id: 2, name: 'two' },
|
|
62
62
|
]
|
|
63
63
|
const node = For<Item>({
|
|
64
64
|
each: () => items,
|
|
65
65
|
by: (item) => item.id,
|
|
66
|
-
children: (item) => h(
|
|
66
|
+
children: (item) => h('li', null, item.name),
|
|
67
67
|
})
|
|
68
68
|
expect(node.type).toBe(ForSymbol)
|
|
69
69
|
const props = node.props as unknown as { each: () => Item[] }
|
|
70
70
|
expect(props.each()).toBe(items)
|
|
71
71
|
})
|
|
72
72
|
|
|
73
|
-
test(
|
|
73
|
+
test('works with string keys', () => {
|
|
74
74
|
const node = For({
|
|
75
|
-
each: () => [{ slug:
|
|
75
|
+
each: () => [{ slug: 'hello' }, { slug: 'world' }],
|
|
76
76
|
by: (item) => item.slug,
|
|
77
|
-
children: (item) => h(
|
|
77
|
+
children: (item) => h('div', null, item.slug),
|
|
78
78
|
})
|
|
79
79
|
expect(node.type).toBe(ForSymbol)
|
|
80
80
|
})
|
|
81
81
|
|
|
82
|
-
test(
|
|
83
|
-
const childFn = (n: number) => h(
|
|
82
|
+
test('children function produces VNodes', () => {
|
|
83
|
+
const childFn = (n: number) => h('li', { key: n }, String(n))
|
|
84
84
|
const node = For({
|
|
85
85
|
each: () => [1, 2, 3],
|
|
86
86
|
by: (n) => n,
|
|
@@ -88,7 +88,7 @@ describe("For", () => {
|
|
|
88
88
|
})
|
|
89
89
|
const props = node.props as unknown as { children: typeof childFn }
|
|
90
90
|
const result = props.children(1)
|
|
91
|
-
expect((result as VNode).type).toBe(
|
|
91
|
+
expect((result as VNode).type).toBe('li')
|
|
92
92
|
expect((result as VNode).key).toBe(1)
|
|
93
93
|
})
|
|
94
94
|
})
|