aberdeen 0.2.1 → 0.2.3

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.
Files changed (51) hide show
  1. package/dist/aberdeen.d.ts +573 -0
  2. package/dist/aberdeen.js +1756 -0
  3. package/dist/aberdeen.js.map +1 -0
  4. package/dist/prediction.d.ts +29 -0
  5. package/dist/prediction.js +110 -0
  6. package/dist/prediction.js.map +1 -0
  7. package/dist/route.d.ts +16 -0
  8. package/dist/route.js +119 -0
  9. package/dist/route.js.map +1 -0
  10. package/dist/transitions.d.ts +18 -0
  11. package/dist/transitions.js +67 -0
  12. package/dist/transitions.js.map +1 -0
  13. package/package.json +5 -2
  14. package/.github/workflows/deploy.yml +0 -43
  15. package/.vscode/launch.json +0 -23
  16. package/examples/input/index.html +0 -8
  17. package/examples/input/input.css +0 -56
  18. package/examples/input/input.js +0 -66
  19. package/examples/list/index.html +0 -7
  20. package/examples/list/list.js +0 -47
  21. package/examples/router/index.html +0 -8
  22. package/examples/router/page-home.js +0 -12
  23. package/examples/router/page-list.js +0 -35
  24. package/examples/router/page-settings.js +0 -6
  25. package/examples/router/router.js +0 -76
  26. package/examples/router/style.css +0 -88
  27. package/examples/tic-tac-toe/index.html +0 -8
  28. package/examples/tic-tac-toe/tic-tac-toe.css +0 -50
  29. package/examples/tic-tac-toe/tic-tac-toe.js +0 -90
  30. package/tests/_fakedom.js +0 -255
  31. package/tests/_init.js +0 -81
  32. package/tests/array.js +0 -109
  33. package/tests/binding.js +0 -106
  34. package/tests/browsers.js +0 -22
  35. package/tests/clean.js +0 -26
  36. package/tests/count.js +0 -105
  37. package/tests/create.js +0 -92
  38. package/tests/destroy.js +0 -270
  39. package/tests/dom.js +0 -219
  40. package/tests/errors.js +0 -114
  41. package/tests/immediate.js +0 -87
  42. package/tests/map.js +0 -76
  43. package/tests/objmap.js +0 -40
  44. package/tests/onEach.js +0 -392
  45. package/tests/prediction.js +0 -97
  46. package/tests/props.js +0 -49
  47. package/tests/schedule.js +0 -44
  48. package/tests/scope.js +0 -277
  49. package/tests/sort.js +0 -105
  50. package/tests/store.js +0 -254
  51. package/tsconfig.json +0 -67
@@ -1,87 +0,0 @@
1
- describe('Immediate observe', function() {
2
-
3
- it('runs immediately', () => {
4
- let store = new Store({a: 1})
5
- let count = 0
6
- immediateObserve(() => {
7
- store.set('b', store.get('a') * 2)
8
- count++
9
- })
10
- assertEqual(store.get('b'), 2)
11
- assertEqual(count, 1)
12
-
13
- store.set('a', 3)
14
- assertEqual(store.get('b'), 6)
15
- assertEqual(count, 2)
16
-
17
- passTime() // shouldn't change anything
18
- assertEqual(store.get('b'), 6)
19
- assertEqual(count, 2)
20
- });
21
-
22
- it('stabilizes dependent values', () => {
23
- let store = new Store({num: 1})
24
- immediateObserve(() => { // num to str
25
- let num = store.get("num")
26
- if (typeof num === 'number') {
27
- store.set('str', "x".repeat(num))
28
- } else {
29
- store.set('num', 0) // will call this observer recursively
30
- }
31
- })
32
- immediateObserve(() => { // str to num
33
- let str = store.get('str')
34
- if (typeof str === 'string') {
35
- store.set('str', "x".repeat(str.length)) // replace str chars by 'x'
36
- store.set('num', str.length) // may call this observer recursively
37
- } else {
38
- store.set('str', "") // will call this observer recursively
39
- }
40
- })
41
- assertEqual(store.get(), {num: 1, str: 'x'})
42
-
43
- store.set('num', 3)
44
- assertEqual(store.get(), {num: 3, str: 'xxx'})
45
-
46
- store.set('num', '')
47
- assertEqual(store.get(), {num: 0, str: ''})
48
-
49
- store.set('str', 'af123')
50
- assertEqual(store.get(), {num: 5, str: 'xxxxx'})
51
- })
52
-
53
- it('stops when it goes out of scope', () => {
54
- let store = new Store({a: 1})
55
- observe(() => {
56
- if (store.get('stop')) return
57
- immediateObserve(() => {
58
- store.set('b', store.get('a') * 2)
59
- })
60
- })
61
- assertEqual(store.get('b'), 2)
62
-
63
- store.set('a', 3)
64
- assertEqual(store.get('b'), 6)
65
-
66
- store.set('stop', true)
67
- passTime() // allow the deferred observe to rerun, expiring the immediate observe
68
-
69
- store.set('a', 5)
70
- assertEqual(store.get('b'), 6)
71
- })
72
-
73
- it('throws an error if a loop does not stabilize', () => {
74
- let store = new Store({a: 1})
75
- immediateObserve(() => {
76
- store.set('b', store.get('a') + 1)
77
- })
78
- // This will start an infinite recursion, which should be broken up.
79
- immediateObserve(() => {
80
- store.set('a', store.get('b') + 1)
81
- })
82
- // The error for the above is to be thrown async.
83
- assertThrow('recursive updates', () => {
84
- passTime()
85
- })
86
- })
87
- })
package/tests/map.js DELETED
@@ -1,76 +0,0 @@
1
- describe('The map() and multiMap() methods', () => {
2
- it('transforms Maps to Maps', () => {
3
- let store = new Store(objToMap({a: 0, b: 2, c: 3}))
4
- let cnt1 = 0, cnt2 = 0
5
-
6
- mount(document.body, () => {
7
- let out = store.map(s => {
8
- cnt1++
9
- if (s.get()) return s.get()*10
10
- })
11
-
12
- out.onEach(s => {
13
- cnt2++
14
- text(s.index()+"="+s.get())
15
- }, s => s.index())
16
- })
17
-
18
- assertBody(`"b=20" "c=30"`)
19
- assertEqual([cnt1, cnt2], [3, 2])
20
-
21
- store.merge(objToMap({a: 1, c: undefined}))
22
- passTime()
23
- assertBody(`"a=10" "b=20"`)
24
- assertEqual([cnt1, cnt2], [4, 3])
25
- })
26
- it('transforms Arrays to Objects', () => {
27
- let store = new Store(['a', 'b'])
28
- let cnt1 = 0, cnt2 = 0
29
- let out
30
-
31
- mount(document.body, () => {
32
- out = store.multiMap(s => {
33
- cnt1++
34
- return {[s.get()]: s.index()}
35
- })
36
-
37
- out.onEach(s => {
38
- cnt2++
39
- text(s.index()+'='+s.get())
40
- }, s => -s.get())
41
- })
42
-
43
- assertEqual(out.peek(), objToMap({a: 0, b: 1}))
44
- assertBody(`"b=1" "a=0"`)
45
- assertEqual([cnt1, cnt2], [2, 2])
46
-
47
- store.set(0, 'A')
48
- store.push('c')
49
- passTime()
50
- assertEqual(store.peek(), ['A', 'b', 'c'])
51
- assertEqual(out.peek(), objToMap({A: 0, b: 1, c: 2}))
52
- assertBody(`"c=2" "b=1" "A=0"`)
53
- assertEqual([cnt1, cnt2], [4, 4])
54
- })
55
- it('transforms Objects to Maps', () => {
56
- let store = new Store({a: {a:[]}, b: new Map([[1,2], [3,4]]), c: {c:5}, d: {}, e: 123})
57
- let cnt1 = 0
58
- let out
59
-
60
- observe(() => {
61
- out = store.multiMap(s => {
62
- cnt1++
63
- return s.get()
64
- })
65
- })
66
-
67
- assertEqual(out.peek(), new Map([['a',[]], [1,2], [3,4], ['c',5]]))
68
- assertEqual(cnt1, 5)
69
-
70
- store.delete('b')
71
- store.set('a', {})
72
- passTime()
73
- assertEqual(out.peek(), new Map([['c',5]]))
74
- assertEqual(cnt1, 6)
75
- })
76
- })
package/tests/objmap.js DELETED
@@ -1,40 +0,0 @@
1
- describe('Objects and maps', () => {
2
- it('map fetches with a limited depth', () => {
3
- let store = new Store(objToMap({a: {b: {c: {d: 42}}}}))
4
-
5
- let res = store.query({depth: 2, type: 'map'})
6
- assertEqual(res.size, 1)
7
- assertEqual(res.get('a').get('b').get(), objToMap({c: {d: 42}}))
8
- })
9
- it('merges maps collapsing changes', () => {
10
- for(let converter of [objToMap, a=>a]) {
11
- let store = new Store(converter({a: 1, b: 2, c: 3, d: undefined}))
12
- assertEqual(store.count(), 3)
13
- let cnt = 0
14
- mount(document.body, () => {
15
- cnt++
16
- text(store.get('a')+store.get('a')+store.get('b'))
17
- store.get()
18
- store.get()
19
- })
20
- assertBody(`"4"`)
21
-
22
- store.set(converter({a: 3, b: 4}))
23
- passTime()
24
- assertBody(`"10"`)
25
- assertEqual(cnt, 2)
26
-
27
- store.merge(converter({c: 4}))
28
- passTime()
29
- assertBody(`"10"`)
30
- assertEqual(cnt, 3)
31
-
32
- unmount()
33
- }
34
- })
35
-
36
- it('handles invalid indexes', () => {
37
- let store = new Store({})
38
- assertThrow('Invalid object index', () => store.set(true, 1))
39
- })
40
- })
package/tests/onEach.js DELETED
@@ -1,392 +0,0 @@
1
- describe('onEach', function() {
2
-
3
- it('ignores undefined values', () => {
4
- let cnt = 0
5
- mount(document.body, () => {
6
- let store = new Store()
7
- store.onEach(() => cnt++)
8
- })
9
- assertEqual(cnt, 0)
10
- })
11
-
12
- it('handles unsuitable store values', () => {
13
- for(let value of [3, "", false]) {
14
- let cnt = 0
15
- mount(document.body, () => {
16
- let store = new Store(value)
17
- assertThrow(`onEach() attempted`, () => {
18
- store.onEach(() => cnt++)
19
- })
20
-
21
- })
22
- assertEqual(cnt, 0, "cnt mismatch for "+JSON.stringify(value))
23
- }
24
- })
25
-
26
- it('does nothing for an empty map', () => {
27
- let cnt = 0
28
- mount(document.body, () => {
29
- let store = new Store({})
30
- store.onEach(function() {
31
- cnt++
32
- })
33
- })
34
- assertEqual(cnt, 0)
35
- })
36
-
37
-
38
- it('emits a single entry', () => {
39
- let result = []
40
- mount(document.body, () => {
41
- let store = new Store({x: 3})
42
- store.onEach(function(store) {
43
- result.push([store.index(),store.get()])
44
- })
45
- })
46
- assertEqual(result, [['x', 3]])
47
- })
48
-
49
- it('emits multiple entries', () => {
50
- let result = []
51
- mount(document.body, () => {
52
- let store = new Store({x: 3, y: 4, z: 5})
53
- store.onEach(function(store) {
54
- result.push([store.index(),store.get()])
55
- })
56
- // The order is undefined, so we'll sort it
57
- result.sort((a,b) => a[1] - b[1])
58
- })
59
- assertEqual(result, [['x', 3], ['y', 4], ['z', 5]])
60
- })
61
-
62
- it('adds a single item to the DOM', () => {
63
- mount(document.body, () => {
64
- let store = new Store({x: 3})
65
- store.onEach(function(store) {
66
- node('p', {className: store.index()}, store.getNumber())
67
- })
68
- })
69
- assertBody(`p{@class="x" "3"}`)
70
- })
71
-
72
- it('adds multiple items to the DOM in default order', () => {
73
- mount(document.body, () => {
74
- let store = new Store({c: 3, a: 1, b: 2})
75
- store.onEach(function(store) {
76
- node('p', store.index())
77
- })
78
- })
79
- assertBody(`p{"a"} p{"b"} p{"c"}`)
80
- })
81
-
82
- it('maintains the last-element marker', () => {
83
- mount(document.body, () => {
84
- let store = new Store({c: 3, a: 1, b: 2})
85
- store.onEach(function(store) {
86
- node('p', store.index())
87
- })
88
- node('div')
89
- })
90
- assertBody(`p{"a"} p{"b"} p{"c"} div{}`)
91
- })
92
-
93
- it('maintains position for items', () => {
94
- let store = new Store({0: false, 1: false, 2: false, 3: false})
95
- let cnts = [0,0,0,0];
96
- mount(document.body, () => {
97
- store.onEach(item => {
98
- cnts[item.index()]++;
99
- if (item.getBoolean()) node('p', {id: item.index()})
100
- })
101
- })
102
-
103
- assertBody(``);
104
- assertEqual(cnts, [1,1,1,1]);
105
-
106
- store.merge({1: true});
107
- passTime();
108
- assertBody(`p{@id="1"}`)
109
- assertEqual(cnts, [1,2,1,1]);
110
-
111
- store.merge({0: true, 2: true, 3: true});
112
- passTime();
113
- assertBody(`p{@id="0"} p{@id="1"} p{@id="2"} p{@id="3"}`)
114
- assertEqual(cnts, [2,2,2,2]);
115
- })
116
-
117
- it('adds items in the right position', () => {
118
- let store = new Store({});
119
-
120
- mount(document.body, () => {
121
- store.onEach(item => {
122
- node(item.index())
123
- })
124
- })
125
-
126
- let items = ['d', 'a', 'b', 'f', 'c', 'e'];
127
- let seen = [];
128
-
129
- for(let item of items) {
130
- seen.push(item+'{}')
131
- seen.sort();
132
-
133
- store.set(item, true);
134
- passTime()
135
- assertBody(seen.join(' '))
136
- }
137
- })
138
-
139
- it('removes items and calls cleaners', () => {
140
- let items = ['d', 'a', 'b', 'f', 'c', 'e']
141
- let store = new Store({})
142
- for(let item of items) {
143
- store.set(item, true)
144
- }
145
- let cleaned = [];
146
-
147
- mount(document.body, () => {
148
- store.onEach(item => {
149
- node(item.index())
150
- clean(() => {
151
- cleaned.push(item.index())
152
- });
153
- })
154
- })
155
-
156
- let current = items.slice().sort();
157
-
158
- let cleanedExpected = [];
159
-
160
- for(let item of items) {
161
- current.splice(current.indexOf(item), 1)
162
-
163
- store.merge({[item]: undefined});
164
- cleanedExpected.push(item);
165
- passTime()
166
- assertBody(current.map(s => s+'{}').join(' '))
167
- assertEqual(cleaned, cleanedExpected)
168
- }
169
- })
170
-
171
- it(`removes an entire object and calls cleaners`, () => {
172
- let cleaned = {};
173
- let store = new Store({b:2,c:3,a:1})
174
- let cnt = 0
175
- mount(document.body, () => {
176
- if (store.getType()==="object") {
177
- store.onEach(item => {
178
- cnt++
179
- node(item.index())
180
- clean(() => {
181
- cleaned[item.index()] = true;
182
- })
183
- })
184
- } else {
185
- text(JSON.stringify(store.get()))
186
- }
187
- })
188
- assertBody(`a{} b{} c{}`)
189
-
190
- store.set(true)
191
- passTime()
192
- assertBody(`"true"`)
193
- assertEqual(cleaned, {a:true, b:true, c:true})
194
- assertEqual(cnt, 3)
195
- })
196
-
197
- it('should ignore on delete followed by set', () => {
198
- let store = new Store({a:1, b:2})
199
- let cnt = 0
200
- mount(document.body, () => {
201
- store.onEach(item => {
202
- node(item.index())
203
- cnt++
204
- })
205
- })
206
- assertBody(`a{} b{}`)
207
- assertEqual(cnt, 2)
208
-
209
- store.delete('a')
210
- assertEqual(store.get(), {b: 2})
211
- store.set('a', 3)
212
- passTime()
213
- assertBody(`a{} b{}`)
214
- assertEqual(cnt, 2) // should not trigger again as the value is not subscribed
215
- });
216
-
217
- it('should do nothing on set followed by delete', () => {
218
- let store = new Store({a:1})
219
- let cnt = 0
220
- mount(document.body, () => {
221
- store.onEach(item => {
222
- node(item.index())
223
- cnt++
224
- })
225
- })
226
- assertBody(`a{}`)
227
- assertEqual(cnt, 1)
228
-
229
- store.set('b', 2)
230
- assertEqual(store.get(), {a: 1, b: 2})
231
- store.delete('b')
232
- passTime()
233
- assertBody(`a{}`)
234
- assertEqual(cnt, 1)
235
- })
236
-
237
- it(`should handle items with identical sort keys`, () => {
238
- let store = new Store({a: 1, b: 1, c: 1, d: 1})
239
- mount(document.body, () => {
240
- store.onEach(item => {
241
- node(item.index())
242
- }, item => item.getNumber())
243
- })
244
- assertEqual(getBody().split(' ').sort().join(' '), `a{} b{} c{} d{}`)
245
-
246
- store.delete('b')
247
- passTime()
248
- assertEqual(getBody().split(' ').sort().join(' '), `a{} c{} d{}`)
249
-
250
- store.delete('d')
251
- passTime()
252
- assertEqual(getBody().split(' ').sort().join(' '), `a{} c{}`)
253
-
254
- store.delete('a')
255
- passTime()
256
- assertEqual(getBody().split(' ').sort().join(' '), `c{}`)
257
-
258
- })
259
-
260
- it('keeps two onEaches in order', () => {
261
- let store1 = new Store(['c1'])
262
- let store2 = new Store(['c2'])
263
- mount(document.body, () => {
264
- store1.onEach(item => {
265
- node(item.getString())
266
- })
267
- store2.onEach(item => {
268
- node(item.getString())
269
- }, item => item.getString())
270
- })
271
- assertBody(`c1{} c2{}`)
272
-
273
- store1.set(1, 'b1')
274
- passTime()
275
- assertBody(`c1{} b1{} c2{}`)
276
-
277
- store2.set(['b2', 'c2', 'd2'])
278
- passTime()
279
- assertBody(`c1{} b1{} b2{} c2{} d2{}`)
280
-
281
- store1.set([])
282
- passTime()
283
- assertBody(`b2{} c2{} d2{}`)
284
-
285
- store2.set([])
286
- passTime()
287
- assertBody(``)
288
-
289
- store2.set(['c2', 'b2'])
290
- passTime()
291
- assertBody(`b2{} c2{}`)
292
-
293
- store1.set(['c1', 'b1'])
294
- passTime()
295
- assertBody(`c1{} b1{} b2{} c2{}`)
296
- })
297
-
298
- it(`iterates arrays`, () => {
299
- let store = new Store(['e', 'b', 'a', 'd'])
300
- mount(document.body, () => {
301
- store.onEach(item => {
302
- node('h'+item.index())
303
- })
304
- store.onEach(item => {
305
- node('i'+item.index())
306
- }, item => item.getString())
307
- })
308
-
309
- assertBody(`h0{} h1{} h2{} h3{} i2{} i1{} i3{} i0{}`)
310
-
311
- store.set(4,'c')
312
-
313
- passTime()
314
- assertBody(`h0{} h1{} h2{} h3{} h4{} i2{} i1{} i4{} i3{} i0{}`)
315
- })
316
-
317
- it(`iterates arrays that are pushed into`, () => {
318
- let store = new Store(['e', 'b', 'a', 'd'])
319
- mount(document.body, () => {
320
- store.onEach(item => {
321
- node('h'+item.index())
322
- })
323
- store.onEach(item => {
324
- node('i'+item.index())
325
- }, item => item.getString())
326
- })
327
-
328
- store.push('c')
329
-
330
- passTime()
331
- assertBody(`h0{} h1{} h2{} h3{} h4{} i2{} i1{} i4{} i3{} i0{}`)
332
- })
333
-
334
- it(`removes all children before redrawing`, () => {
335
- let store = new Store({a:1, b:2})
336
- let select = new Store(1)
337
- mount(document.body, () => {
338
- select.get();
339
- store.onEach(item => {
340
- node(item.index())
341
- }, item => {
342
- if (select.get() == item.get()) {
343
- return item.index();
344
- }
345
- })
346
- })
347
- assertBody(`a{}`)
348
-
349
- select.set('2')
350
- passTime()
351
- assertBody(`b{}`)
352
- })
353
-
354
- it('should handle items that don\'t create DOM elements', () => {
355
- let store = new Store("b0 b1 c1 b2 c0 a1 a0 a2".split(" "))
356
- mount(document.body, () => {
357
- store.onEach(item => {
358
- let letter = item.get()[0]
359
- let count = 0 | item.get()[1]
360
- for(let i=0; i<count; i++) {
361
- text(item.index()+letter)
362
- }
363
- }, item => item.get()[0])
364
- })
365
- assertBody(`"7a" "7a" "5a" "3b" "3b" "1b" "2c"`) // The order within the same letter is unspecified
366
-
367
- store.set(5, undefined)
368
- store.set(3, undefined)
369
- passTime()
370
- assertBody(`"7a" "7a" "1b" "2c"`) // The order within the same letter is unspecified
371
-
372
- store.set(0, undefined)
373
- store.set(4, undefined)
374
- store.set(6, undefined)
375
- passTime()
376
- assertBody(`"7a" "7a" "1b" "2c"`) // The order within the same letter is unspecified
377
- })
378
-
379
- it('filters when there is no sort key', () => {
380
- let store = new Store(['a','b','c'])
381
- mount(document.body, () => {
382
- store.onEach(item => {
383
- node(item.get())
384
- }, item => item.get()=='b' ? undefined : item.get())
385
- })
386
- assertBody(`a{} c{}`)
387
-
388
- store.set([])
389
- passTime()
390
- assertBody(``)
391
- })
392
- })
@@ -1,97 +0,0 @@
1
- describe('Prediction', function() {
2
-
3
- it('reverts', () => {
4
- let store = new Store('a')
5
- mount(document.body, () => {
6
- node(store.get())
7
- })
8
- assertBody(`a{}`)
9
-
10
- let prediction = applyPrediction(() => store.set('b'))
11
- passTime()
12
- assertBody(`b{}`)
13
-
14
- applyCanon(undefined, [prediction])
15
- passTime()
16
- assertBody(`a{}`)
17
-
18
- // Doing this again shouldn't break anything
19
- applyCanon(undefined, [prediction])
20
- passTime()
21
- assertBody(`a{}`)
22
- });
23
-
24
- it('reverts entire patch when it can no longer apply', () => {
25
- let store = new Store({1: 'a', 2: 'x', 3: 'm'})
26
- mount(document.body, () => {
27
- node(store.get(1))
28
- node(store.get(2))
29
- node(store.get(3))
30
- })
31
- assertBody(`a{} x{} m{}`)
32
-
33
- // This prediction should be flushed out due to conflict
34
- applyPrediction(() => store.merge({1: 'b', 2: 'y'}))
35
- passTime()
36
- assertBody(`b{} y{} m{}`)
37
-
38
- // This prediction should be kept
39
- applyPrediction(() => store.set(3, 'n'))
40
- passTime()
41
- assertBody(`b{} y{} n{}`)
42
-
43
- // Create the conflict
44
- applyCanon(() => {
45
- // Check that state was reverted to pre-predictions
46
- assertEqual(store.get(1), 'a')
47
- store.set(1, 'c')
48
- })
49
-
50
- // Check that only the first prediction has been reverted as a whole
51
- passTime()
52
- assertBody(`c{} x{} n{}`)
53
- });
54
-
55
- it('forcibly reverts to canon state', () => {
56
- let store = new Store('a')
57
- mount(document.body, () => {
58
- node(store.get())
59
- })
60
- assertBody(`a{}`)
61
-
62
- let prediction = applyPrediction(() => store.set('b'))
63
- passTime()
64
- assertBody(`b{}`)
65
-
66
- store.set('z')
67
-
68
- applyCanon(undefined, [prediction])
69
-
70
- // An error should be thrown asynchronously
71
- assertThrow('Error', () => {
72
- passTime()
73
- })
74
- assertBody(`a{}`)
75
- })
76
-
77
- it('does not cause redraw when it comes true', () => {
78
- let store = new Store('a')
79
- let draws = 0
80
- mount(document.body, () => {
81
- node(store.get())
82
- draws++
83
- })
84
- assertBody(`a{}`)
85
- assertEqual(draws, 1)
86
-
87
- let prediction = applyPrediction(() => store.set('b'))
88
- passTime()
89
- assertBody(`b{}`)
90
- assertEqual(draws, 2)
91
-
92
- applyCanon(() => store.set('b'), [prediction])
93
- passTime()
94
- assertBody(`b{}`)
95
- assertEqual(draws, 2)
96
- })
97
- })