@zeix/cause-effect 0.12.3 → 0.13.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/.prettierrc +7 -0
- package/README.md +140 -94
- package/index.d.ts +5 -4
- package/index.js +1 -1
- package/index.ts +6 -6
- package/lib/computed.d.ts +16 -7
- package/lib/computed.ts +100 -44
- package/lib/effect.d.ts +17 -4
- package/lib/effect.ts +49 -17
- package/lib/scheduler.d.ts +9 -6
- package/lib/scheduler.ts +18 -9
- package/lib/signal.d.ts +22 -31
- package/lib/signal.ts +55 -81
- package/lib/state.d.ts +3 -3
- package/lib/state.ts +29 -22
- package/lib/util.d.ts +8 -5
- package/lib/util.ts +18 -11
- package/package.json +3 -3
- package/test/batch.test.ts +43 -42
- package/test/benchmark.test.ts +315 -319
- package/test/computed.test.ts +341 -242
- package/test/effect.test.ts +136 -119
- package/test/state.test.ts +126 -118
package/test/effect.test.ts
CHANGED
|
@@ -10,157 +10,174 @@ const wait = (ms: number) => new Promise(resolve => setTimeout(resolve, ms))
|
|
|
10
10
|
describe('Effect', function () {
|
|
11
11
|
|
|
12
12
|
test('should be triggered after a state change', function() {
|
|
13
|
-
const cause = state('foo')
|
|
14
|
-
let count = 0
|
|
15
|
-
|
|
16
|
-
count
|
|
17
|
-
}
|
|
18
|
-
expect(count).toBe(1)
|
|
19
|
-
cause.set('bar')
|
|
20
|
-
expect(count).toBe(2)
|
|
21
|
-
})
|
|
13
|
+
const cause = state('foo')
|
|
14
|
+
let count = 0
|
|
15
|
+
cause.tap(() => {
|
|
16
|
+
count++
|
|
17
|
+
})
|
|
18
|
+
expect(count).toBe(1)
|
|
19
|
+
cause.set('bar')
|
|
20
|
+
expect(count).toBe(2)
|
|
21
|
+
})
|
|
22
22
|
|
|
23
23
|
test('should be triggered after computed async signals resolve without waterfalls', async function() {
|
|
24
24
|
const a = computed(async () => {
|
|
25
|
-
await wait(100)
|
|
26
|
-
return 10
|
|
27
|
-
})
|
|
25
|
+
await wait(100)
|
|
26
|
+
return 10
|
|
27
|
+
})
|
|
28
28
|
const b = computed(async () => {
|
|
29
|
-
await wait(100)
|
|
30
|
-
return 20
|
|
31
|
-
})
|
|
32
|
-
let result = 0
|
|
33
|
-
let count = 0
|
|
34
|
-
effect(
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
expect(result).toBe(
|
|
42
|
-
expect(count).toBe(
|
|
43
|
-
|
|
29
|
+
await wait(100)
|
|
30
|
+
return 20
|
|
31
|
+
})
|
|
32
|
+
let result = 0
|
|
33
|
+
let count = 0
|
|
34
|
+
effect({
|
|
35
|
+
signals: [a, b],
|
|
36
|
+
ok: (aValue, bValue) => {
|
|
37
|
+
result = aValue + bValue
|
|
38
|
+
count++
|
|
39
|
+
}
|
|
40
|
+
})
|
|
41
|
+
expect(result).toBe(0)
|
|
42
|
+
expect(count).toBe(0)
|
|
43
|
+
await wait(110)
|
|
44
|
+
expect(result).toBe(30)
|
|
45
|
+
expect(count).toBe(1)
|
|
46
|
+
})
|
|
44
47
|
|
|
45
48
|
test('should be triggered repeatedly after repeated state change', async function() {
|
|
46
|
-
const cause = state(0)
|
|
47
|
-
let result = 0
|
|
48
|
-
let count = 0
|
|
49
|
-
|
|
50
|
-
result = res
|
|
51
|
-
count
|
|
52
|
-
}
|
|
49
|
+
const cause = state(0)
|
|
50
|
+
let result = 0
|
|
51
|
+
let count = 0
|
|
52
|
+
cause.tap(res => {
|
|
53
|
+
result = res
|
|
54
|
+
count++
|
|
55
|
+
})
|
|
53
56
|
for (let i = 0; i < 10; i++) {
|
|
54
|
-
cause.set(i)
|
|
55
|
-
expect(result).toBe(i)
|
|
57
|
+
cause.set(i)
|
|
58
|
+
expect(result).toBe(i)
|
|
56
59
|
expect(count).toBe(i + 1); // + 1 for effect initialization
|
|
57
60
|
}
|
|
58
|
-
})
|
|
61
|
+
})
|
|
59
62
|
|
|
60
63
|
test('should handle errors in effects', function() {
|
|
61
|
-
const a = state(1)
|
|
62
|
-
const b =
|
|
63
|
-
if (
|
|
64
|
-
return
|
|
65
|
-
})
|
|
66
|
-
let normalCallCount = 0
|
|
67
|
-
let errorCallCount = 0
|
|
68
|
-
|
|
69
|
-
ok: (
|
|
70
|
-
// console.log('Normal effect:',
|
|
71
|
-
normalCallCount
|
|
64
|
+
const a = state(1)
|
|
65
|
+
const b = a.map(v => {
|
|
66
|
+
if (v > 5) throw new Error('Value too high')
|
|
67
|
+
return v * 2
|
|
68
|
+
})
|
|
69
|
+
let normalCallCount = 0
|
|
70
|
+
let errorCallCount = 0
|
|
71
|
+
b.tap({
|
|
72
|
+
ok: () => {
|
|
73
|
+
// console.log('Normal effect:', value)
|
|
74
|
+
normalCallCount++
|
|
72
75
|
},
|
|
73
|
-
err:
|
|
74
|
-
// console.log('Error effect:', error)
|
|
75
|
-
errorCallCount
|
|
76
|
-
expect(error.message).toBe('Value too high')
|
|
76
|
+
err: error => {
|
|
77
|
+
// console.log('Error effect:', error)
|
|
78
|
+
errorCallCount++
|
|
79
|
+
expect(error.message).toBe('Value too high')
|
|
77
80
|
}
|
|
78
|
-
}
|
|
81
|
+
})
|
|
79
82
|
|
|
80
83
|
// Normal case
|
|
81
|
-
a.set(2)
|
|
82
|
-
expect(normalCallCount).toBe(2)
|
|
83
|
-
expect(errorCallCount).toBe(0)
|
|
84
|
+
a.set(2)
|
|
85
|
+
expect(normalCallCount).toBe(2)
|
|
86
|
+
expect(errorCallCount).toBe(0)
|
|
84
87
|
|
|
85
88
|
// Error case
|
|
86
|
-
a.set(6)
|
|
87
|
-
expect(normalCallCount).toBe(2)
|
|
88
|
-
expect(errorCallCount).toBe(1)
|
|
89
|
+
a.set(6)
|
|
90
|
+
expect(normalCallCount).toBe(2)
|
|
91
|
+
expect(errorCallCount).toBe(1)
|
|
89
92
|
|
|
90
93
|
// Back to normal
|
|
91
|
-
a.set(3)
|
|
92
|
-
expect(normalCallCount).toBe(3)
|
|
93
|
-
expect(errorCallCount).toBe(1)
|
|
94
|
-
})
|
|
94
|
+
a.set(3)
|
|
95
|
+
expect(normalCallCount).toBe(3)
|
|
96
|
+
expect(errorCallCount).toBe(1)
|
|
97
|
+
})
|
|
95
98
|
|
|
96
99
|
test('should handle UNSET values in effects', async function() {
|
|
97
100
|
const a = computed(async () => {
|
|
98
|
-
await wait(100)
|
|
99
|
-
return 42
|
|
100
|
-
})
|
|
101
|
-
let normalCallCount = 0
|
|
102
|
-
let nilCount = 0
|
|
103
|
-
|
|
104
|
-
ok:
|
|
105
|
-
normalCallCount
|
|
106
|
-
expect(aValue).toBe(42)
|
|
101
|
+
await wait(100)
|
|
102
|
+
return 42
|
|
103
|
+
})
|
|
104
|
+
let normalCallCount = 0
|
|
105
|
+
let nilCount = 0
|
|
106
|
+
a.tap({
|
|
107
|
+
ok: aValue => {
|
|
108
|
+
normalCallCount++
|
|
109
|
+
expect(aValue).toBe(42)
|
|
107
110
|
},
|
|
108
111
|
nil: () => {
|
|
109
112
|
nilCount++
|
|
110
113
|
}
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
expect(normalCallCount).toBe(0)
|
|
114
|
-
expect(nilCount).toBe(1)
|
|
115
|
-
expect(a.get()).toBe(UNSET)
|
|
116
|
-
await wait(110)
|
|
117
|
-
expect(normalCallCount).toBe(
|
|
118
|
-
expect(
|
|
119
|
-
|
|
114
|
+
})
|
|
115
|
+
|
|
116
|
+
expect(normalCallCount).toBe(0)
|
|
117
|
+
expect(nilCount).toBe(1)
|
|
118
|
+
expect(a.get()).toBe(UNSET)
|
|
119
|
+
await wait(110)
|
|
120
|
+
expect(normalCallCount).toBe(1)
|
|
121
|
+
expect(nilCount).toBe(1)
|
|
122
|
+
expect(a.get()).toBe(42)
|
|
123
|
+
})
|
|
120
124
|
|
|
121
125
|
test('should log error to console when error is not handled', () => {
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
126
|
+
// Mock console.error
|
|
127
|
+
const originalConsoleError = console.error
|
|
128
|
+
const mockConsoleError = mock(() => {})
|
|
129
|
+
console.error = mockConsoleError
|
|
130
|
+
|
|
131
|
+
try {
|
|
132
|
+
const a = state(1)
|
|
133
|
+
const b = a.map(v => {
|
|
134
|
+
if (v > 5) throw new Error('Value too high')
|
|
135
|
+
return v * 2
|
|
136
|
+
})
|
|
137
|
+
|
|
138
|
+
// Create an effect without explicit error handling
|
|
139
|
+
b.tap(() => {})
|
|
140
|
+
|
|
141
|
+
// This should trigger the error
|
|
142
|
+
a.set(6)
|
|
143
|
+
|
|
144
|
+
// Check if console.error was called with the error
|
|
145
|
+
expect(mockConsoleError).toHaveBeenCalledWith(
|
|
146
|
+
expect.any(Error)
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
// Check the error message
|
|
150
|
+
const error = (mockConsoleError as ReturnType<typeof mock>).mock.calls[0][0] as Error
|
|
151
|
+
expect(error.message).toBe('Value too high')
|
|
152
|
+
|
|
153
|
+
} finally {
|
|
154
|
+
// Restore the original console.error
|
|
155
|
+
console.error = originalConsoleError
|
|
156
|
+
}
|
|
157
|
+
})
|
|
158
|
+
|
|
159
|
+
test('should clean up subscriptions when disposed', () => {
|
|
160
|
+
const count = state(42)
|
|
161
|
+
let received = 0
|
|
162
|
+
|
|
163
|
+
const cleanup = count.tap(value => {
|
|
164
|
+
received = value
|
|
165
|
+
})
|
|
166
|
+
|
|
167
|
+
count.set(43)
|
|
168
|
+
expect(received).toBe(43)
|
|
169
|
+
|
|
170
|
+
cleanup()
|
|
171
|
+
count.set(44)
|
|
172
|
+
expect(received).toBe(43) // Should not update after dispose
|
|
173
|
+
})
|
|
157
174
|
|
|
158
175
|
test('should detect and throw error for circular dependencies in effects', () => {
|
|
159
176
|
let okCount = 0
|
|
160
177
|
let errCount = 0
|
|
161
178
|
const count = state(0)
|
|
162
179
|
|
|
163
|
-
|
|
180
|
+
count.tap({
|
|
164
181
|
ok: () => {
|
|
165
182
|
okCount++
|
|
166
183
|
// This effect updates the signal it depends on, creating a circular dependency
|
|
@@ -171,11 +188,11 @@ describe('Effect', function () {
|
|
|
171
188
|
expect(e).toBeInstanceOf(Error)
|
|
172
189
|
expect(e.message).toBe('Circular dependency in effect detected')
|
|
173
190
|
}
|
|
174
|
-
}
|
|
191
|
+
})
|
|
175
192
|
|
|
176
193
|
// Verify that the count was changed only once due to the circular dependency error
|
|
177
194
|
expect(count.get()).toBe(1)
|
|
178
195
|
expect(okCount).toBe(1)
|
|
179
196
|
expect(errCount).toBe(1)
|
|
180
197
|
})
|
|
181
|
-
})
|
|
198
|
+
})
|
package/test/state.test.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { describe, test, expect } from 'bun:test'
|
|
2
|
-
import {
|
|
2
|
+
import { isComputed, isState, state, UNSET } from '../'
|
|
3
3
|
|
|
4
4
|
/* === Utility Functions === */
|
|
5
5
|
|
|
@@ -9,207 +9,215 @@ const wait = (ms: number) => new Promise(resolve => setTimeout(resolve, ms))
|
|
|
9
9
|
|
|
10
10
|
describe('State', function () {
|
|
11
11
|
|
|
12
|
+
describe("State type guard", () => {
|
|
13
|
+
test("isState identifies state signals", () => {
|
|
14
|
+
const count = state(42)
|
|
15
|
+
expect(isState(count)).toBe(true)
|
|
16
|
+
expect(isComputed(count)).toBe(false)
|
|
17
|
+
})
|
|
18
|
+
})
|
|
19
|
+
|
|
12
20
|
describe('Boolean cause', function () {
|
|
13
21
|
|
|
14
22
|
test('should be boolean', function () {
|
|
15
|
-
const cause = state(false)
|
|
16
|
-
expect(typeof cause.get()).toBe('boolean')
|
|
17
|
-
})
|
|
23
|
+
const cause = state(false)
|
|
24
|
+
expect(typeof cause.get()).toBe('boolean')
|
|
25
|
+
})
|
|
18
26
|
|
|
19
27
|
test('should set initial value to false', function () {
|
|
20
|
-
const cause = state(false)
|
|
21
|
-
expect(cause.get()).toBe(false)
|
|
22
|
-
})
|
|
28
|
+
const cause = state(false)
|
|
29
|
+
expect(cause.get()).toBe(false)
|
|
30
|
+
})
|
|
23
31
|
|
|
24
32
|
test('should set initial value to true', function () {
|
|
25
|
-
const cause = state(true)
|
|
26
|
-
expect(cause.get()).toBe(true)
|
|
27
|
-
})
|
|
33
|
+
const cause = state(true)
|
|
34
|
+
expect(cause.get()).toBe(true)
|
|
35
|
+
})
|
|
28
36
|
|
|
29
37
|
test('should set new value with .set(true)', function () {
|
|
30
|
-
const cause = state(false)
|
|
31
|
-
cause.set(true)
|
|
32
|
-
expect(cause.get()).toBe(true)
|
|
33
|
-
})
|
|
38
|
+
const cause = state(false)
|
|
39
|
+
cause.set(true)
|
|
40
|
+
expect(cause.get()).toBe(true)
|
|
41
|
+
})
|
|
34
42
|
|
|
35
43
|
test('should toggle initial value with .set(v => !v)', function () {
|
|
36
|
-
const cause = state(false)
|
|
37
|
-
cause.update((v) => !v)
|
|
38
|
-
expect(cause.get()).toBe(true)
|
|
39
|
-
})
|
|
44
|
+
const cause = state(false)
|
|
45
|
+
cause.update((v) => !v)
|
|
46
|
+
expect(cause.get()).toBe(true)
|
|
47
|
+
})
|
|
40
48
|
|
|
41
|
-
})
|
|
49
|
+
})
|
|
42
50
|
|
|
43
51
|
describe('Number cause', function () {
|
|
44
52
|
|
|
45
53
|
test('should be number', function () {
|
|
46
|
-
const cause = state(0)
|
|
47
|
-
expect(typeof cause.get()).toBe('number')
|
|
48
|
-
})
|
|
54
|
+
const cause = state(0)
|
|
55
|
+
expect(typeof cause.get()).toBe('number')
|
|
56
|
+
})
|
|
49
57
|
|
|
50
58
|
test('should set initial value to 0', function () {
|
|
51
|
-
const cause = state(0)
|
|
52
|
-
expect(cause.get()).toBe(0)
|
|
53
|
-
})
|
|
59
|
+
const cause = state(0)
|
|
60
|
+
expect(cause.get()).toBe(0)
|
|
61
|
+
})
|
|
54
62
|
|
|
55
63
|
test('should set new value with .set(42)', function () {
|
|
56
|
-
const cause = state(0)
|
|
57
|
-
cause.set(42)
|
|
58
|
-
expect(cause.get()).toBe(42)
|
|
59
|
-
})
|
|
64
|
+
const cause = state(0)
|
|
65
|
+
cause.set(42)
|
|
66
|
+
expect(cause.get()).toBe(42)
|
|
67
|
+
})
|
|
60
68
|
|
|
61
69
|
test('should increment value with .set(v => ++v)', function () {
|
|
62
|
-
const cause = state(0)
|
|
63
|
-
cause.update(v => ++v)
|
|
64
|
-
expect(cause.get()).toBe(1)
|
|
65
|
-
})
|
|
70
|
+
const cause = state(0)
|
|
71
|
+
cause.update(v => ++v)
|
|
72
|
+
expect(cause.get()).toBe(1)
|
|
73
|
+
})
|
|
66
74
|
|
|
67
|
-
})
|
|
75
|
+
})
|
|
68
76
|
|
|
69
77
|
describe('String cause', function () {
|
|
70
78
|
|
|
71
79
|
test('should be string', function () {
|
|
72
|
-
const cause = state('foo')
|
|
73
|
-
expect(typeof cause.get()).toBe('string')
|
|
74
|
-
})
|
|
80
|
+
const cause = state('foo')
|
|
81
|
+
expect(typeof cause.get()).toBe('string')
|
|
82
|
+
})
|
|
75
83
|
|
|
76
84
|
test('should set initial value to "foo"', function () {
|
|
77
|
-
const cause = state('foo')
|
|
78
|
-
expect(cause.get()).toBe('foo')
|
|
79
|
-
})
|
|
85
|
+
const cause = state('foo')
|
|
86
|
+
expect(cause.get()).toBe('foo')
|
|
87
|
+
})
|
|
80
88
|
|
|
81
89
|
test('should set new value with .set("bar")', function () {
|
|
82
|
-
const cause = state('foo')
|
|
83
|
-
cause.set('bar')
|
|
84
|
-
expect(cause.get()).toBe('bar')
|
|
85
|
-
})
|
|
90
|
+
const cause = state('foo')
|
|
91
|
+
cause.set('bar')
|
|
92
|
+
expect(cause.get()).toBe('bar')
|
|
93
|
+
})
|
|
86
94
|
|
|
87
95
|
test('should upper case value with .set(v => v.toUpperCase())', function () {
|
|
88
|
-
const cause = state('foo')
|
|
89
|
-
cause.update(v => v ? v.toUpperCase() : '')
|
|
90
|
-
expect(cause.get()).toBe("FOO")
|
|
91
|
-
})
|
|
96
|
+
const cause = state('foo')
|
|
97
|
+
cause.update(v => v ? v.toUpperCase() : '')
|
|
98
|
+
expect(cause.get()).toBe("FOO")
|
|
99
|
+
})
|
|
92
100
|
|
|
93
|
-
})
|
|
101
|
+
})
|
|
94
102
|
|
|
95
103
|
describe('Array cause', function () {
|
|
96
104
|
|
|
97
105
|
test('should be array', function () {
|
|
98
|
-
const cause = state([1, 2, 3])
|
|
99
|
-
expect(Array.isArray(cause.get())).toBe(true)
|
|
100
|
-
})
|
|
106
|
+
const cause = state([1, 2, 3])
|
|
107
|
+
expect(Array.isArray(cause.get())).toBe(true)
|
|
108
|
+
})
|
|
101
109
|
|
|
102
110
|
test('should set initial value to [1, 2, 3]', function () {
|
|
103
|
-
const cause = state([1, 2, 3])
|
|
104
|
-
expect(cause.get()).toEqual([1, 2, 3])
|
|
105
|
-
})
|
|
111
|
+
const cause = state([1, 2, 3])
|
|
112
|
+
expect(cause.get()).toEqual([1, 2, 3])
|
|
113
|
+
})
|
|
106
114
|
|
|
107
115
|
test('should set new value with .set([4, 5, 6])', function () {
|
|
108
|
-
const cause = state([1, 2, 3])
|
|
109
|
-
cause.set([4, 5, 6])
|
|
110
|
-
expect(cause.get()).toEqual([4, 5, 6])
|
|
111
|
-
})
|
|
116
|
+
const cause = state([1, 2, 3])
|
|
117
|
+
cause.set([4, 5, 6])
|
|
118
|
+
expect(cause.get()).toEqual([4, 5, 6])
|
|
119
|
+
})
|
|
112
120
|
|
|
113
121
|
test('should reflect current value of array after modification', function () {
|
|
114
|
-
const array = [1, 2, 3]
|
|
115
|
-
const cause = state(array)
|
|
122
|
+
const array = [1, 2, 3]
|
|
123
|
+
const cause = state(array)
|
|
116
124
|
array.push(4); // don't do this! the result will be correct, but we can't trigger effects
|
|
117
|
-
expect(cause.get()).toEqual([1, 2, 3, 4])
|
|
118
|
-
})
|
|
125
|
+
expect(cause.get()).toEqual([1, 2, 3, 4])
|
|
126
|
+
})
|
|
119
127
|
|
|
120
128
|
test('should set new value with .set([...array, 4])', function () {
|
|
121
|
-
const array = [1, 2, 3]
|
|
122
|
-
const cause = state(array)
|
|
129
|
+
const array = [1, 2, 3]
|
|
130
|
+
const cause = state(array)
|
|
123
131
|
cause.set([...array, 4]); // use destructuring instead!
|
|
124
|
-
expect(cause.get()).toEqual([1, 2, 3, 4])
|
|
125
|
-
})
|
|
132
|
+
expect(cause.get()).toEqual([1, 2, 3, 4])
|
|
133
|
+
})
|
|
126
134
|
|
|
127
|
-
})
|
|
135
|
+
})
|
|
128
136
|
|
|
129
137
|
describe('Object cause', function () {
|
|
130
138
|
|
|
131
139
|
test('should be object', function () {
|
|
132
|
-
const cause = state({ a: 'a', b: 1 })
|
|
133
|
-
expect(typeof cause.get()).toBe('object')
|
|
134
|
-
})
|
|
140
|
+
const cause = state({ a: 'a', b: 1 })
|
|
141
|
+
expect(typeof cause.get()).toBe('object')
|
|
142
|
+
})
|
|
135
143
|
|
|
136
144
|
test('should set initial value to { a: "a", b: 1 }', function () {
|
|
137
|
-
const cause = state({ a: 'a', b: 1 })
|
|
138
|
-
expect(cause.get()).toEqual({ a: 'a', b: 1 })
|
|
139
|
-
})
|
|
145
|
+
const cause = state({ a: 'a', b: 1 })
|
|
146
|
+
expect(cause.get()).toEqual({ a: 'a', b: 1 })
|
|
147
|
+
})
|
|
140
148
|
|
|
141
149
|
test('should set new value with .set({ c: true })', function () {
|
|
142
|
-
const cause = state<Record<string, any>>({ a: 'a', b: 1 })
|
|
143
|
-
cause.set({ c: true })
|
|
144
|
-
expect(cause.get()).toEqual({ c: true })
|
|
145
|
-
})
|
|
150
|
+
const cause = state<Record<string, any>>({ a: 'a', b: 1 })
|
|
151
|
+
cause.set({ c: true })
|
|
152
|
+
expect(cause.get()).toEqual({ c: true })
|
|
153
|
+
})
|
|
146
154
|
|
|
147
155
|
test('should reflect current value of object after modification', function () {
|
|
148
|
-
const obj = { a: 'a', b: 1 }
|
|
149
|
-
const cause = state<Record<string, any>>(obj)
|
|
156
|
+
const obj = { a: 'a', b: 1 }
|
|
157
|
+
const cause = state<Record<string, any>>(obj)
|
|
150
158
|
// @ts-expect-error
|
|
151
159
|
obj.c = true; // don't do this! the result will be correct, but we can't trigger effects
|
|
152
|
-
expect(cause.get()).toEqual({ a: 'a', b: 1, c: true })
|
|
153
|
-
})
|
|
160
|
+
expect(cause.get()).toEqual({ a: 'a', b: 1, c: true })
|
|
161
|
+
})
|
|
154
162
|
|
|
155
163
|
test('should set new value with .set({...obj, c: true})', function () {
|
|
156
|
-
const obj = { a: 'a', b: 1 }
|
|
157
|
-
const cause = state<Record<string, any>>(obj)
|
|
164
|
+
const obj = { a: 'a', b: 1 }
|
|
165
|
+
const cause = state<Record<string, any>>(obj)
|
|
158
166
|
cause.set({...obj, c: true}); // use destructuring instead!
|
|
159
|
-
expect(cause.get()).toEqual({ a: 'a', b: 1, c: true })
|
|
160
|
-
})
|
|
167
|
+
expect(cause.get()).toEqual({ a: 'a', b: 1, c: true })
|
|
168
|
+
})
|
|
161
169
|
|
|
162
|
-
})
|
|
170
|
+
})
|
|
163
171
|
|
|
164
172
|
describe('Map method', function () {
|
|
165
173
|
|
|
166
174
|
test('should return a computed signal', function() {
|
|
167
|
-
const cause = state(42)
|
|
168
|
-
const double = cause.map(v => v * 2)
|
|
169
|
-
expect(isComputed(double)).toBe(true)
|
|
170
|
-
expect(double.get()).toBe(84)
|
|
171
|
-
})
|
|
175
|
+
const cause = state(42)
|
|
176
|
+
const double = cause.map(v => v * 2)
|
|
177
|
+
expect(isComputed(double)).toBe(true)
|
|
178
|
+
expect(double.get()).toBe(84)
|
|
179
|
+
})
|
|
172
180
|
|
|
173
181
|
test('should return a computed signal for an async function', async function() {
|
|
174
|
-
const cause = state(42)
|
|
175
|
-
const asyncDouble = cause.map(async
|
|
176
|
-
await wait(100)
|
|
177
|
-
return
|
|
178
|
-
})
|
|
179
|
-
expect(isComputed(asyncDouble)).toBe(true)
|
|
180
|
-
expect(asyncDouble.get()).toBe(UNSET)
|
|
181
|
-
await wait(110)
|
|
182
|
-
expect(asyncDouble.get()).toBe(84)
|
|
183
|
-
})
|
|
182
|
+
const cause = state(42)
|
|
183
|
+
const asyncDouble = cause.map(async value => {
|
|
184
|
+
await wait(100)
|
|
185
|
+
return value * 2
|
|
186
|
+
})
|
|
187
|
+
expect(isComputed(asyncDouble)).toBe(true)
|
|
188
|
+
expect(asyncDouble.get()).toBe(UNSET)
|
|
189
|
+
await wait(110)
|
|
190
|
+
expect(asyncDouble.get()).toBe(84)
|
|
191
|
+
})
|
|
184
192
|
|
|
185
|
-
})
|
|
193
|
+
})
|
|
186
194
|
|
|
187
|
-
describe('
|
|
195
|
+
describe('Tap method', function () {
|
|
188
196
|
|
|
189
197
|
test('should create an effect that reacts on signal changes', function() {
|
|
190
|
-
const cause = state(42)
|
|
191
|
-
let okCount = 0
|
|
192
|
-
let nilCount = 0
|
|
193
|
-
let result = 0
|
|
194
|
-
cause.
|
|
198
|
+
const cause = state(42)
|
|
199
|
+
let okCount = 0
|
|
200
|
+
let nilCount = 0
|
|
201
|
+
let result = 0
|
|
202
|
+
cause.tap({
|
|
195
203
|
ok: v => {
|
|
196
|
-
result = v
|
|
204
|
+
result = v
|
|
197
205
|
okCount++
|
|
198
206
|
},
|
|
199
207
|
nil: () => {
|
|
200
208
|
nilCount++
|
|
201
209
|
}
|
|
202
210
|
})
|
|
203
|
-
cause.set(43)
|
|
211
|
+
cause.set(43)
|
|
204
212
|
expect(okCount).toBe(2); // + 1 for effect initialization
|
|
205
|
-
expect(nilCount).toBe(0)
|
|
206
|
-
expect(result).toBe(43)
|
|
213
|
+
expect(nilCount).toBe(0)
|
|
214
|
+
expect(result).toBe(43)
|
|
207
215
|
|
|
208
|
-
cause.set(UNSET)
|
|
209
|
-
expect(okCount).toBe(2)
|
|
210
|
-
expect(nilCount).toBe(1)
|
|
211
|
-
})
|
|
216
|
+
cause.set(UNSET)
|
|
217
|
+
expect(okCount).toBe(2)
|
|
218
|
+
expect(nilCount).toBe(1)
|
|
219
|
+
})
|
|
212
220
|
|
|
213
|
-
})
|
|
221
|
+
})
|
|
214
222
|
|
|
215
223
|
});
|