@pyreon/machine 0.11.5 → 0.11.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/README.md +18 -14
- package/lib/index.js.map +1 -1
- package/package.json +14 -14
- package/src/index.ts +2 -2
- package/src/machine.ts +3 -3
- package/src/tests/api.test.ts +137 -137
- package/src/tests/guards.test.ts +62 -62
- package/src/tests/listeners.test.ts +98 -98
- package/src/tests/machine.test.ts +281 -281
package/src/tests/guards.test.ts
CHANGED
|
@@ -1,108 +1,108 @@
|
|
|
1
|
-
import { signal } from
|
|
2
|
-
import { describe, expect, it, vi } from
|
|
3
|
-
import { createMachine } from
|
|
1
|
+
import { signal } from '@pyreon/reactivity'
|
|
2
|
+
import { describe, expect, it, vi } from 'vitest'
|
|
3
|
+
import { createMachine } from '../index'
|
|
4
4
|
|
|
5
|
-
describe(
|
|
6
|
-
it(
|
|
5
|
+
describe('createMachine — guard conditions', () => {
|
|
6
|
+
it('transitions when guard returns true', () => {
|
|
7
7
|
const m = createMachine({
|
|
8
|
-
initial:
|
|
8
|
+
initial: 'editing',
|
|
9
9
|
states: {
|
|
10
10
|
editing: {
|
|
11
|
-
on: { SUBMIT: { target:
|
|
11
|
+
on: { SUBMIT: { target: 'submitting', guard: () => true } },
|
|
12
12
|
},
|
|
13
13
|
submitting: {},
|
|
14
14
|
},
|
|
15
15
|
})
|
|
16
|
-
m.send(
|
|
17
|
-
expect(m()).toBe(
|
|
16
|
+
m.send('SUBMIT')
|
|
17
|
+
expect(m()).toBe('submitting')
|
|
18
18
|
})
|
|
19
19
|
|
|
20
|
-
it(
|
|
20
|
+
it('blocks transition when guard returns false', () => {
|
|
21
21
|
const m = createMachine({
|
|
22
|
-
initial:
|
|
22
|
+
initial: 'editing',
|
|
23
23
|
states: {
|
|
24
24
|
editing: {
|
|
25
|
-
on: { SUBMIT: { target:
|
|
25
|
+
on: { SUBMIT: { target: 'submitting', guard: () => false } },
|
|
26
26
|
},
|
|
27
27
|
submitting: {},
|
|
28
28
|
},
|
|
29
29
|
})
|
|
30
|
-
m.send(
|
|
31
|
-
expect(m()).toBe(
|
|
30
|
+
m.send('SUBMIT')
|
|
31
|
+
expect(m()).toBe('editing')
|
|
32
32
|
})
|
|
33
33
|
|
|
34
|
-
it(
|
|
34
|
+
it('guard receives event payload', () => {
|
|
35
35
|
const guardFn = vi.fn((payload?: unknown) => {
|
|
36
36
|
return (payload as any)?.amount > 0
|
|
37
37
|
})
|
|
38
38
|
|
|
39
39
|
const m = createMachine({
|
|
40
|
-
initial:
|
|
40
|
+
initial: 'idle',
|
|
41
41
|
states: {
|
|
42
42
|
idle: {
|
|
43
|
-
on: { PAY: { target:
|
|
43
|
+
on: { PAY: { target: 'processing', guard: guardFn } },
|
|
44
44
|
},
|
|
45
45
|
processing: {},
|
|
46
46
|
},
|
|
47
47
|
})
|
|
48
48
|
|
|
49
|
-
m.send(
|
|
50
|
-
expect(m()).toBe(
|
|
49
|
+
m.send('PAY', { amount: 0 })
|
|
50
|
+
expect(m()).toBe('idle')
|
|
51
51
|
expect(guardFn).toHaveBeenCalledWith({ amount: 0 })
|
|
52
52
|
|
|
53
|
-
m.send(
|
|
54
|
-
expect(m()).toBe(
|
|
53
|
+
m.send('PAY', { amount: 100 })
|
|
54
|
+
expect(m()).toBe('processing')
|
|
55
55
|
expect(guardFn).toHaveBeenCalledWith({ amount: 100 })
|
|
56
56
|
})
|
|
57
57
|
|
|
58
|
-
it(
|
|
58
|
+
it('guard without payload receives undefined', () => {
|
|
59
59
|
const guardFn = vi.fn(() => true)
|
|
60
60
|
|
|
61
61
|
const m = createMachine({
|
|
62
|
-
initial:
|
|
62
|
+
initial: 'idle',
|
|
63
63
|
states: {
|
|
64
64
|
idle: {
|
|
65
|
-
on: { GO: { target:
|
|
65
|
+
on: { GO: { target: 'active', guard: guardFn } },
|
|
66
66
|
},
|
|
67
67
|
active: {},
|
|
68
68
|
},
|
|
69
69
|
})
|
|
70
70
|
|
|
71
|
-
m.send(
|
|
71
|
+
m.send('GO')
|
|
72
72
|
expect(guardFn).toHaveBeenCalledWith(undefined)
|
|
73
73
|
})
|
|
74
74
|
|
|
75
|
-
it(
|
|
75
|
+
it('guard with reactive signal dependency', () => {
|
|
76
76
|
const isValid = signal(false)
|
|
77
77
|
|
|
78
78
|
const m = createMachine({
|
|
79
|
-
initial:
|
|
79
|
+
initial: 'editing',
|
|
80
80
|
states: {
|
|
81
81
|
editing: {
|
|
82
82
|
on: {
|
|
83
|
-
SUBMIT: { target:
|
|
83
|
+
SUBMIT: { target: 'submitting', guard: () => isValid.peek() },
|
|
84
84
|
},
|
|
85
85
|
},
|
|
86
86
|
submitting: {},
|
|
87
87
|
},
|
|
88
88
|
})
|
|
89
89
|
|
|
90
|
-
m.send(
|
|
91
|
-
expect(m()).toBe(
|
|
90
|
+
m.send('SUBMIT')
|
|
91
|
+
expect(m()).toBe('editing') // guard blocks
|
|
92
92
|
|
|
93
93
|
isValid.set(true)
|
|
94
|
-
m.send(
|
|
95
|
-
expect(m()).toBe(
|
|
94
|
+
m.send('SUBMIT')
|
|
95
|
+
expect(m()).toBe('submitting') // guard passes
|
|
96
96
|
})
|
|
97
97
|
|
|
98
|
-
it(
|
|
98
|
+
it('guard can check multiple conditions', () => {
|
|
99
99
|
const m = createMachine({
|
|
100
|
-
initial:
|
|
100
|
+
initial: 'idle',
|
|
101
101
|
states: {
|
|
102
102
|
idle: {
|
|
103
103
|
on: {
|
|
104
104
|
START: {
|
|
105
|
-
target:
|
|
105
|
+
target: 'running',
|
|
106
106
|
guard: (payload?: unknown) => {
|
|
107
107
|
const p = payload as { ready: boolean; count: number } | undefined
|
|
108
108
|
return p !== undefined && p.ready && p.count > 0
|
|
@@ -114,24 +114,24 @@ describe("createMachine — guard conditions", () => {
|
|
|
114
114
|
},
|
|
115
115
|
})
|
|
116
116
|
|
|
117
|
-
m.send(
|
|
118
|
-
expect(m()).toBe(
|
|
117
|
+
m.send('START', { ready: false, count: 5 })
|
|
118
|
+
expect(m()).toBe('idle')
|
|
119
119
|
|
|
120
|
-
m.send(
|
|
121
|
-
expect(m()).toBe(
|
|
120
|
+
m.send('START', { ready: true, count: 0 })
|
|
121
|
+
expect(m()).toBe('idle')
|
|
122
122
|
|
|
123
|
-
m.send(
|
|
124
|
-
expect(m()).toBe(
|
|
123
|
+
m.send('START', { ready: true, count: 5 })
|
|
124
|
+
expect(m()).toBe('running')
|
|
125
125
|
})
|
|
126
126
|
|
|
127
|
-
it(
|
|
127
|
+
it('different events on same state can have different guards', () => {
|
|
128
128
|
const m = createMachine({
|
|
129
|
-
initial:
|
|
129
|
+
initial: 'editing',
|
|
130
130
|
states: {
|
|
131
131
|
editing: {
|
|
132
132
|
on: {
|
|
133
|
-
SAVE: { target:
|
|
134
|
-
PUBLISH: { target:
|
|
133
|
+
SAVE: { target: 'saved', guard: () => true },
|
|
134
|
+
PUBLISH: { target: 'published', guard: () => false },
|
|
135
135
|
},
|
|
136
136
|
},
|
|
137
137
|
saved: {},
|
|
@@ -139,40 +139,40 @@ describe("createMachine — guard conditions", () => {
|
|
|
139
139
|
},
|
|
140
140
|
})
|
|
141
141
|
|
|
142
|
-
m.send(
|
|
143
|
-
expect(m()).toBe(
|
|
142
|
+
m.send('PUBLISH')
|
|
143
|
+
expect(m()).toBe('editing') // guard blocks
|
|
144
144
|
|
|
145
|
-
m.send(
|
|
146
|
-
expect(m()).toBe(
|
|
145
|
+
m.send('SAVE')
|
|
146
|
+
expect(m()).toBe('saved') // guard passes
|
|
147
147
|
})
|
|
148
148
|
|
|
149
|
-
it(
|
|
149
|
+
it('guard blocks do not fire onEnter or onTransition', () => {
|
|
150
150
|
const m = createMachine({
|
|
151
|
-
initial:
|
|
151
|
+
initial: 'a',
|
|
152
152
|
states: {
|
|
153
|
-
a: { on: { GO: { target:
|
|
153
|
+
a: { on: { GO: { target: 'b', guard: () => false } } },
|
|
154
154
|
b: {},
|
|
155
155
|
},
|
|
156
156
|
})
|
|
157
157
|
|
|
158
158
|
const enterFn = vi.fn()
|
|
159
159
|
const transitionFn = vi.fn()
|
|
160
|
-
m.onEnter(
|
|
160
|
+
m.onEnter('b', enterFn)
|
|
161
161
|
m.onTransition(transitionFn)
|
|
162
162
|
|
|
163
|
-
m.send(
|
|
163
|
+
m.send('GO')
|
|
164
164
|
expect(enterFn).not.toHaveBeenCalled()
|
|
165
165
|
expect(transitionFn).not.toHaveBeenCalled()
|
|
166
166
|
})
|
|
167
167
|
|
|
168
|
-
it(
|
|
168
|
+
it('mixing guarded and non-guarded transitions', () => {
|
|
169
169
|
const m = createMachine({
|
|
170
|
-
initial:
|
|
170
|
+
initial: 'idle',
|
|
171
171
|
states: {
|
|
172
172
|
idle: {
|
|
173
173
|
on: {
|
|
174
|
-
FETCH:
|
|
175
|
-
ADMIN: { target:
|
|
174
|
+
FETCH: 'loading', // no guard
|
|
175
|
+
ADMIN: { target: 'admin', guard: () => false }, // guarded
|
|
176
176
|
},
|
|
177
177
|
},
|
|
178
178
|
loading: {},
|
|
@@ -180,10 +180,10 @@ describe("createMachine — guard conditions", () => {
|
|
|
180
180
|
},
|
|
181
181
|
})
|
|
182
182
|
|
|
183
|
-
m.send(
|
|
184
|
-
expect(m()).toBe(
|
|
183
|
+
m.send('ADMIN')
|
|
184
|
+
expect(m()).toBe('idle') // blocked
|
|
185
185
|
|
|
186
|
-
m.send(
|
|
187
|
-
expect(m()).toBe(
|
|
186
|
+
m.send('FETCH')
|
|
187
|
+
expect(m()).toBe('loading') // no guard, passes
|
|
188
188
|
})
|
|
189
189
|
})
|
|
@@ -1,105 +1,105 @@
|
|
|
1
|
-
import { describe, expect, it, vi } from
|
|
2
|
-
import { createMachine } from
|
|
1
|
+
import { describe, expect, it, vi } from 'vitest'
|
|
2
|
+
import { createMachine } from '../index'
|
|
3
3
|
|
|
4
|
-
describe(
|
|
5
|
-
it(
|
|
4
|
+
describe('createMachine — onEnter callbacks', () => {
|
|
5
|
+
it('fires callback when entering the specified state', () => {
|
|
6
6
|
const m = createMachine({
|
|
7
|
-
initial:
|
|
7
|
+
initial: 'idle',
|
|
8
8
|
states: {
|
|
9
|
-
idle: { on: { LOAD:
|
|
10
|
-
loading: { on: { DONE:
|
|
9
|
+
idle: { on: { LOAD: 'loading' } },
|
|
10
|
+
loading: { on: { DONE: 'idle' } },
|
|
11
11
|
},
|
|
12
12
|
})
|
|
13
13
|
const entered: string[] = []
|
|
14
14
|
|
|
15
|
-
m.onEnter(
|
|
15
|
+
m.onEnter('loading', (event) => {
|
|
16
16
|
entered.push(event.type)
|
|
17
17
|
})
|
|
18
18
|
|
|
19
|
-
m.send(
|
|
20
|
-
expect(entered).toEqual([
|
|
19
|
+
m.send('LOAD')
|
|
20
|
+
expect(entered).toEqual(['LOAD'])
|
|
21
21
|
})
|
|
22
22
|
|
|
23
|
-
it(
|
|
23
|
+
it('does not fire for other state entries', () => {
|
|
24
24
|
const m = createMachine({
|
|
25
|
-
initial:
|
|
25
|
+
initial: 'a',
|
|
26
26
|
states: {
|
|
27
|
-
a: { on: { GO:
|
|
28
|
-
b: { on: { GO:
|
|
27
|
+
a: { on: { GO: 'b' } },
|
|
28
|
+
b: { on: { GO: 'c' } },
|
|
29
29
|
c: {},
|
|
30
30
|
},
|
|
31
31
|
})
|
|
32
32
|
const fn = vi.fn()
|
|
33
|
-
m.onEnter(
|
|
33
|
+
m.onEnter('c', fn)
|
|
34
34
|
|
|
35
|
-
m.send(
|
|
35
|
+
m.send('GO') // a -> b
|
|
36
36
|
expect(fn).not.toHaveBeenCalled()
|
|
37
37
|
|
|
38
|
-
m.send(
|
|
38
|
+
m.send('GO') // b -> c
|
|
39
39
|
expect(fn).toHaveBeenCalledOnce()
|
|
40
40
|
})
|
|
41
41
|
|
|
42
|
-
it(
|
|
42
|
+
it('receives event payload', () => {
|
|
43
43
|
const m = createMachine({
|
|
44
|
-
initial:
|
|
44
|
+
initial: 'idle',
|
|
45
45
|
states: {
|
|
46
|
-
idle: { on: { SELECT:
|
|
46
|
+
idle: { on: { SELECT: 'selected' } },
|
|
47
47
|
selected: {},
|
|
48
48
|
},
|
|
49
49
|
})
|
|
50
50
|
let received: unknown = null
|
|
51
51
|
|
|
52
|
-
m.onEnter(
|
|
52
|
+
m.onEnter('selected', (event) => {
|
|
53
53
|
received = event.payload
|
|
54
54
|
})
|
|
55
55
|
|
|
56
|
-
m.send(
|
|
57
|
-
expect(received).toEqual({ id: 42, name:
|
|
56
|
+
m.send('SELECT', { id: 42, name: 'item' })
|
|
57
|
+
expect(received).toEqual({ id: 42, name: 'item' })
|
|
58
58
|
})
|
|
59
59
|
|
|
60
|
-
it(
|
|
60
|
+
it('fires on self-transitions', () => {
|
|
61
61
|
const m = createMachine({
|
|
62
|
-
initial:
|
|
62
|
+
initial: 'counting',
|
|
63
63
|
states: {
|
|
64
|
-
counting: { on: { INC:
|
|
64
|
+
counting: { on: { INC: 'counting' } },
|
|
65
65
|
},
|
|
66
66
|
})
|
|
67
67
|
const fn = vi.fn()
|
|
68
|
-
m.onEnter(
|
|
68
|
+
m.onEnter('counting', fn)
|
|
69
69
|
|
|
70
|
-
m.send(
|
|
71
|
-
m.send(
|
|
72
|
-
m.send(
|
|
70
|
+
m.send('INC')
|
|
71
|
+
m.send('INC')
|
|
72
|
+
m.send('INC')
|
|
73
73
|
|
|
74
74
|
expect(fn).toHaveBeenCalledTimes(3)
|
|
75
75
|
})
|
|
76
76
|
|
|
77
|
-
it(
|
|
77
|
+
it('unsubscribe function stops future callbacks', () => {
|
|
78
78
|
const m = createMachine({
|
|
79
|
-
initial:
|
|
79
|
+
initial: 'a',
|
|
80
80
|
states: {
|
|
81
|
-
a: { on: { GO:
|
|
82
|
-
b: { on: { GO:
|
|
81
|
+
a: { on: { GO: 'b' } },
|
|
82
|
+
b: { on: { GO: 'a' } },
|
|
83
83
|
},
|
|
84
84
|
})
|
|
85
85
|
const fn = vi.fn()
|
|
86
|
-
const unsub = m.onEnter(
|
|
86
|
+
const unsub = m.onEnter('b', fn)
|
|
87
87
|
|
|
88
|
-
m.send(
|
|
88
|
+
m.send('GO') // a -> b
|
|
89
89
|
expect(fn).toHaveBeenCalledOnce()
|
|
90
90
|
|
|
91
91
|
unsub()
|
|
92
92
|
|
|
93
|
-
m.send(
|
|
94
|
-
m.send(
|
|
93
|
+
m.send('GO') // b -> a
|
|
94
|
+
m.send('GO') // a -> b again
|
|
95
95
|
expect(fn).toHaveBeenCalledOnce() // not called again
|
|
96
96
|
})
|
|
97
97
|
|
|
98
|
-
it(
|
|
98
|
+
it('multiple listeners for same state all fire', () => {
|
|
99
99
|
const m = createMachine({
|
|
100
|
-
initial:
|
|
100
|
+
initial: 'idle',
|
|
101
101
|
states: {
|
|
102
|
-
idle: { on: { GO:
|
|
102
|
+
idle: { on: { GO: 'active' } },
|
|
103
103
|
active: {},
|
|
104
104
|
},
|
|
105
105
|
})
|
|
@@ -107,40 +107,40 @@ describe("createMachine — onEnter callbacks", () => {
|
|
|
107
107
|
const fn2 = vi.fn()
|
|
108
108
|
const fn3 = vi.fn()
|
|
109
109
|
|
|
110
|
-
m.onEnter(
|
|
111
|
-
m.onEnter(
|
|
112
|
-
m.onEnter(
|
|
110
|
+
m.onEnter('active', fn1)
|
|
111
|
+
m.onEnter('active', fn2)
|
|
112
|
+
m.onEnter('active', fn3)
|
|
113
113
|
|
|
114
|
-
m.send(
|
|
114
|
+
m.send('GO')
|
|
115
115
|
expect(fn1).toHaveBeenCalledOnce()
|
|
116
116
|
expect(fn2).toHaveBeenCalledOnce()
|
|
117
117
|
expect(fn3).toHaveBeenCalledOnce()
|
|
118
118
|
})
|
|
119
119
|
|
|
120
|
-
it(
|
|
120
|
+
it('onEnter for a state that is never entered is never called', () => {
|
|
121
121
|
const m = createMachine({
|
|
122
|
-
initial:
|
|
122
|
+
initial: 'idle',
|
|
123
123
|
states: {
|
|
124
|
-
idle: { on: { GO:
|
|
124
|
+
idle: { on: { GO: 'active' } },
|
|
125
125
|
active: {},
|
|
126
126
|
unreachable: {},
|
|
127
127
|
},
|
|
128
128
|
})
|
|
129
129
|
const fn = vi.fn()
|
|
130
|
-
m.onEnter(
|
|
130
|
+
m.onEnter('unreachable', fn)
|
|
131
131
|
|
|
132
|
-
m.send(
|
|
132
|
+
m.send('GO')
|
|
133
133
|
expect(fn).not.toHaveBeenCalled()
|
|
134
134
|
})
|
|
135
135
|
})
|
|
136
136
|
|
|
137
|
-
describe(
|
|
138
|
-
it(
|
|
137
|
+
describe('createMachine — onTransition callbacks', () => {
|
|
138
|
+
it('fires on every state transition', () => {
|
|
139
139
|
const m = createMachine({
|
|
140
|
-
initial:
|
|
140
|
+
initial: 'a',
|
|
141
141
|
states: {
|
|
142
|
-
a: { on: { NEXT:
|
|
143
|
-
b: { on: { NEXT:
|
|
142
|
+
a: { on: { NEXT: 'b' } },
|
|
143
|
+
b: { on: { NEXT: 'c' } },
|
|
144
144
|
c: {},
|
|
145
145
|
},
|
|
146
146
|
})
|
|
@@ -150,36 +150,36 @@ describe("createMachine — onTransition callbacks", () => {
|
|
|
150
150
|
transitions.push([from, to, event.type])
|
|
151
151
|
})
|
|
152
152
|
|
|
153
|
-
m.send(
|
|
154
|
-
m.send(
|
|
153
|
+
m.send('NEXT') // a -> b
|
|
154
|
+
m.send('NEXT') // b -> c
|
|
155
155
|
|
|
156
156
|
expect(transitions).toEqual([
|
|
157
|
-
[
|
|
158
|
-
[
|
|
157
|
+
['a', 'b', 'NEXT'],
|
|
158
|
+
['b', 'c', 'NEXT'],
|
|
159
159
|
])
|
|
160
160
|
})
|
|
161
161
|
|
|
162
|
-
it(
|
|
162
|
+
it('does not fire when event is ignored (no valid transition)', () => {
|
|
163
163
|
const m = createMachine({
|
|
164
|
-
initial:
|
|
164
|
+
initial: 'idle',
|
|
165
165
|
states: {
|
|
166
|
-
idle: { on: { START:
|
|
166
|
+
idle: { on: { START: 'running' } },
|
|
167
167
|
running: {},
|
|
168
168
|
},
|
|
169
169
|
})
|
|
170
170
|
const fn = vi.fn()
|
|
171
171
|
m.onTransition(fn)
|
|
172
172
|
|
|
173
|
-
m.send(
|
|
173
|
+
m.send('STOP' as any) // invalid
|
|
174
174
|
expect(fn).not.toHaveBeenCalled()
|
|
175
175
|
})
|
|
176
176
|
|
|
177
|
-
it(
|
|
177
|
+
it('does not fire when guard blocks transition', () => {
|
|
178
178
|
const m = createMachine({
|
|
179
|
-
initial:
|
|
179
|
+
initial: 'idle',
|
|
180
180
|
states: {
|
|
181
181
|
idle: {
|
|
182
|
-
on: { GO: { target:
|
|
182
|
+
on: { GO: { target: 'active', guard: () => false } },
|
|
183
183
|
},
|
|
184
184
|
active: {},
|
|
185
185
|
},
|
|
@@ -187,15 +187,15 @@ describe("createMachine — onTransition callbacks", () => {
|
|
|
187
187
|
const fn = vi.fn()
|
|
188
188
|
m.onTransition(fn)
|
|
189
189
|
|
|
190
|
-
m.send(
|
|
190
|
+
m.send('GO')
|
|
191
191
|
expect(fn).not.toHaveBeenCalled()
|
|
192
192
|
})
|
|
193
193
|
|
|
194
|
-
it(
|
|
194
|
+
it('receives event payload', () => {
|
|
195
195
|
const m = createMachine({
|
|
196
|
-
initial:
|
|
196
|
+
initial: 'idle',
|
|
197
197
|
states: {
|
|
198
|
-
idle: { on: { LOAD:
|
|
198
|
+
idle: { on: { LOAD: 'loading' } },
|
|
199
199
|
loading: {},
|
|
200
200
|
},
|
|
201
201
|
})
|
|
@@ -205,56 +205,56 @@ describe("createMachine — onTransition callbacks", () => {
|
|
|
205
205
|
receivedPayload = event.payload
|
|
206
206
|
})
|
|
207
207
|
|
|
208
|
-
m.send(
|
|
209
|
-
expect(receivedPayload).toEqual({ url:
|
|
208
|
+
m.send('LOAD', { url: '/api/data' })
|
|
209
|
+
expect(receivedPayload).toEqual({ url: '/api/data' })
|
|
210
210
|
})
|
|
211
211
|
|
|
212
|
-
it(
|
|
212
|
+
it('fires on self-transitions', () => {
|
|
213
213
|
const m = createMachine({
|
|
214
|
-
initial:
|
|
214
|
+
initial: 'counting',
|
|
215
215
|
states: {
|
|
216
|
-
counting: { on: { INC:
|
|
216
|
+
counting: { on: { INC: 'counting' } },
|
|
217
217
|
},
|
|
218
218
|
})
|
|
219
219
|
const fn = vi.fn()
|
|
220
220
|
m.onTransition(fn)
|
|
221
221
|
|
|
222
|
-
m.send(
|
|
223
|
-
m.send(
|
|
222
|
+
m.send('INC')
|
|
223
|
+
m.send('INC')
|
|
224
224
|
|
|
225
225
|
expect(fn).toHaveBeenCalledTimes(2)
|
|
226
226
|
expect(fn).toHaveBeenCalledWith(
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
expect.objectContaining({ type:
|
|
227
|
+
'counting',
|
|
228
|
+
'counting',
|
|
229
|
+
expect.objectContaining({ type: 'INC' }),
|
|
230
230
|
)
|
|
231
231
|
})
|
|
232
232
|
|
|
233
|
-
it(
|
|
233
|
+
it('unsubscribe stops future callbacks', () => {
|
|
234
234
|
const m = createMachine({
|
|
235
|
-
initial:
|
|
235
|
+
initial: 'a',
|
|
236
236
|
states: {
|
|
237
|
-
a: { on: { GO:
|
|
238
|
-
b: { on: { GO:
|
|
237
|
+
a: { on: { GO: 'b' } },
|
|
238
|
+
b: { on: { GO: 'a' } },
|
|
239
239
|
},
|
|
240
240
|
})
|
|
241
241
|
const fn = vi.fn()
|
|
242
242
|
const unsub = m.onTransition(fn)
|
|
243
243
|
|
|
244
|
-
m.send(
|
|
244
|
+
m.send('GO')
|
|
245
245
|
expect(fn).toHaveBeenCalledOnce()
|
|
246
246
|
|
|
247
247
|
unsub()
|
|
248
|
-
m.send(
|
|
249
|
-
m.send(
|
|
248
|
+
m.send('GO')
|
|
249
|
+
m.send('GO')
|
|
250
250
|
expect(fn).toHaveBeenCalledOnce()
|
|
251
251
|
})
|
|
252
252
|
|
|
253
|
-
it(
|
|
253
|
+
it('multiple transition listeners all fire', () => {
|
|
254
254
|
const m = createMachine({
|
|
255
|
-
initial:
|
|
255
|
+
initial: 'idle',
|
|
256
256
|
states: {
|
|
257
|
-
idle: { on: { GO:
|
|
257
|
+
idle: { on: { GO: 'active' } },
|
|
258
258
|
active: {},
|
|
259
259
|
},
|
|
260
260
|
})
|
|
@@ -264,25 +264,25 @@ describe("createMachine — onTransition callbacks", () => {
|
|
|
264
264
|
m.onTransition(fn1)
|
|
265
265
|
m.onTransition(fn2)
|
|
266
266
|
|
|
267
|
-
m.send(
|
|
267
|
+
m.send('GO')
|
|
268
268
|
expect(fn1).toHaveBeenCalledOnce()
|
|
269
269
|
expect(fn2).toHaveBeenCalledOnce()
|
|
270
270
|
})
|
|
271
271
|
|
|
272
|
-
it(
|
|
272
|
+
it('onTransition fires before onEnter', () => {
|
|
273
273
|
const m = createMachine({
|
|
274
|
-
initial:
|
|
274
|
+
initial: 'idle',
|
|
275
275
|
states: {
|
|
276
|
-
idle: { on: { GO:
|
|
276
|
+
idle: { on: { GO: 'active' } },
|
|
277
277
|
active: {},
|
|
278
278
|
},
|
|
279
279
|
})
|
|
280
280
|
const order: string[] = []
|
|
281
281
|
|
|
282
|
-
m.onTransition(() => order.push(
|
|
283
|
-
m.onEnter(
|
|
282
|
+
m.onTransition(() => order.push('transition'))
|
|
283
|
+
m.onEnter('active', () => order.push('enter'))
|
|
284
284
|
|
|
285
|
-
m.send(
|
|
286
|
-
expect(order).toEqual([
|
|
285
|
+
m.send('GO')
|
|
286
|
+
expect(order).toEqual(['transition', 'enter'])
|
|
287
287
|
})
|
|
288
288
|
})
|