sprae 2.14.2 → 3.0.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/test/test.js CHANGED
@@ -1,922 +1,2 @@
1
- // import { signal } from 'usignal/sync'
2
- import { signal } from '@preact/signals-core'
3
- import test, {is, any, throws} from 'tst'
4
- import {tick, time} from 'wait-please'
5
- import sprae from '../src/index.js'
6
- import h from 'hyperf'
7
-
8
- Object.defineProperty(DocumentFragment.prototype, 'outerHTML', {
9
- get() {
10
- let s = ''
11
- this.childNodes.forEach(n => {
12
- s += n.nodeType === 3 ? n.textContent : n.outerHTML != null ? n.outerHTML : ''
13
- })
14
- return s
15
- }
16
- })
17
-
18
- test.skip('autoinit', async () => {
19
- is(window.x.innerHTML, '1')
20
- })
21
-
22
- test('hidden: core', async () => {
23
- let el = h`<div :hidden="hidden"></div>`
24
- let params = sprae(el, {hidden:true})
25
- is(el.outerHTML, `<div hidden=""></div>`)
26
- params.hidden = false
27
- is(el.outerHTML, `<div></div>`)
28
- })
29
-
30
- test('hidden: reactive', async () => {
31
- const hidden = signal(true)
32
- let el = h`<div :hidden="hidden"></div>`
33
- sprae(el, {hidden})
34
- is(el.outerHTML, `<div hidden=""></div>`)
35
- hidden.value = false
36
- is(el.outerHTML, `<div></div>`)
37
- })
38
-
39
- test('common: reactive', async () => {
40
- let el = h`<label :for="name" :text="name" ></label><input :id="name" :name="name" :type="name" :disabled="!name"/><a :href="url"></a><img :src="url"/>`
41
- let params = sprae(el, {name:'text', url:'//google.com'})
42
- is(el.outerHTML, `<label for="text">text</label><input id="text" name="text" type="text"><a href="//google.com"></a><img src="//google.com">`)
43
- params.name = 'email'
44
- is(el.outerHTML, `<label for="email">email</label><input id="email" name="email" type="email"><a href="//google.com"></a><img src="//google.com">`)
45
- })
46
-
47
- test('common: empty strings', async () => {
48
- let el = h`<x :="" :x=""></x>`
49
- sprae(el)
50
- is(el.outerHTML, `<x></x>`)
51
- })
52
-
53
- test('common: comments', async () => {
54
- let el = h`<x :="/* */" :x="/* */"></x>`
55
- sprae(el)
56
- is(el.outerHTML, `<x></x>`)
57
- })
58
-
59
- test('common: newlines', async () => {
60
- let el = h`<x :text="
61
- x
62
- "></x>`
63
- sprae(el, {x:1})
64
- is(el.outerHTML, `<x>1</x>`)
65
- })
66
-
67
- test('common: const in on', async () => {
68
- let el = h`<div :onx="() => {const x=1; y=x+1}"></div>`
69
- let state = sprae(el, {y:0})
70
- el.dispatchEvent(new CustomEvent('x'))
71
- is(state.y, 2)
72
- })
73
-
74
- test('style', async () => {
75
- let el = h`<x style="left: 1px" :style="style"></x>`
76
- let params = sprae(el, {style: "top: 1px"})
77
- is(el.outerHTML, `<x style="left: 1px; top: 1px"></x>`)
78
- params.style = {top: '2px'}
79
- is(el.outerHTML, `<x style="left: 1px; top: 2px;"></x>`)
80
-
81
- params.style = {'--x': 123}
82
- is(el.style.getPropertyValue('--x'), '123')
83
-
84
- params.style = {top:'1px', bottom:'2px'}
85
- is(el.outerHTML, `<x style="left: 1px; top: 1px; bottom: 2px;"></x>`)
86
-
87
- params.style = {top:'2px', bottom: null}
88
- // FIXME
89
- is(el.outerHTML, `<x style="left: 1px; top: 2px;"></x>`)
90
- })
91
-
92
- test('class', async () => {
93
- let el = h`<x class="base" :class="a"></x><y :class="[b, c]"></y><z :class="{b:true, c:d}"></z>`
94
- const c = signal('z')
95
- let params = sprae(el, {a:'x', b:'y', c, d:false});
96
- is(el.outerHTML, `<x class="base x"></x><y class="y z"></y><z class="b"></z>`);
97
- params.d = true;
98
- is(el.outerHTML, `<x class="base x"></x><y class="y z"></y><z class="b c"></z>`);
99
- c.value = 'w'
100
- is(el.outerHTML, `<x class="base x"></x><y class="y w"></y><z class="b c"></z>`);
101
- })
102
-
103
- test('props: base', async () => {
104
- let el = h`<input :id="0" :="{for:1, title:2, help:3, type:4, placeholder: 5, value: 6, aB: 8}" :value="7"/>`
105
- let params = sprae(el)
106
- is(el.outerHTML, `<input id="0" for="1" title="2" help="3" type="4" placeholder="5" value="7" a-b="8">`)
107
- })
108
-
109
- test('props: sets prop', async () => {
110
- let el = h`<x :x="this.x=1" :y="this.y='abc'"></x>`
111
- sprae(el)
112
- is(el.x, 1)
113
- is(el.y, 'abc')
114
- })
115
-
116
- test('props: multiprop', async () => {
117
- let el = h`<input :id:name:for="0" />`
118
- let params = sprae(el)
119
- is(el.outerHTML, `<input id="0" name="0" for="0">`)
120
- })
121
-
122
- // FIXME: this must work without return
123
- test('props: calculation', async () => {
124
- let el = h`<x :x="let a = 5; return Array.from({length: x}, (_,i)=>i).join('')"></x>`
125
- let state = sprae(el, {x:3});
126
- is(el.outerHTML, `<x x="012"></x>`)
127
- state.x = 4
128
- is(el.outerHTML, `<x x="0123"></x>`)
129
- })
130
-
131
- test.todo('props: semicols in expression', async () => {
132
- let el = h`<x :x="0; return Array.from({length: x}, (_,i)=>i).join('')"></x>`
133
- let state = sprae(el, {x:3});
134
- is(el.outerHTML, `<x x="012"></x>`)
135
- state.x = 4
136
- is(el.outerHTML, `<x x="0123"></x>`)
137
- })
138
-
139
-
140
- test('data: base', async () => {
141
- let el = h`<input :data="{a:1, fooBar:2}"/>`
142
- let params = sprae(el)
143
- is(el.outerHTML, `<input data-a="1" data-foo-bar="2">`)
144
- })
145
-
146
- test('aria: base', async () => {
147
- let el = h`<input type="text" id="jokes" role="combobox" :aria="{controls:'joketypes', autocomplete:'list', expanded:false, activeOption:'item1', activedescendant:'', xxx:null}"/>`
148
- sprae(el)
149
- is(el.outerHTML, `<input type="text" id="jokes" role="combobox" aria-controls="joketypes" aria-autocomplete="list" aria-expanded="false" aria-active-option="item1" aria-activedescendant="">`)
150
- })
151
-
152
- test('value: direct', async () => {
153
- let el = h`<input :value="a" />`
154
- let state = sprae(el, {a:1})
155
- is(el.value, '1')
156
- is(el.outerHTML, `<input value="1">`)
157
- state.a = 2
158
- is(el.value, '2')
159
- is(el.outerHTML, `<input value="2">`)
160
-
161
- el.value = 3
162
- // el.dispatchEvent(new window.Event('change'))
163
- // is(state.a, '3')
164
- })
165
-
166
- test('value: textarea', async () => {
167
- let el = h`<textarea :value="a"></textarea>`
168
- let state = sprae(el, {a: 'abcdefgh'})
169
- is(el.selectionStart, 8)
170
- is(el.selectionEnd, 8)
171
- el.setSelectionRange(1, 4)
172
- is(el.selectionStart, 1)
173
- is(el.selectionEnd, 4)
174
- state.a = 'xyzyvw'
175
- is(el.selectionStart, 1)
176
- is(el.selectionEnd, 4)
177
- })
178
-
179
- test('text: core', async () => {
180
- let el = h`<div :text="text"></div>`
181
- let params = sprae(el, {text:'abc'})
182
- is(el.outerHTML, `<div>abc</div>`)
183
- params.text = null
184
- is(el.outerHTML, `<div></div>`)
185
- })
186
-
187
- test('if: base', async () => {
188
- let el = h`<p>
189
- <span :if="a==1">a</span>
190
- <span :else :if="a==2">b</span>
191
- <span :else >c</span>
192
- </p>`
193
-
194
- const params = sprae(el, { a: 1 })
195
-
196
- is(el.innerHTML, '<span>a</span>')
197
- params.a = 2
198
- is(el.innerHTML, '<span>b</span>')
199
- params.a = 3
200
- is(el.innerHTML, '<span>c</span>')
201
- params.a = null
202
- is(el.innerHTML, '<span>c</span>')
203
- })
204
-
205
- test('if: short with insertions', async () => {
206
- let el = h`<p>
207
- <span :if="a==1" :text="'1:'+a"></span>
208
- <span :else :if="a==2" :text="'2:'+a"></span>
209
- <span :else :text="a"></span>
210
- </p>`
211
-
212
- const params = sprae(el, { a: 1 })
213
-
214
- is(el.innerHTML, '<span>1:1</span>')
215
- params.a = 2
216
- is(el.innerHTML, '<span>2:2</span>')
217
- params.a = 3
218
- is(el.innerHTML, '<span>3</span>')
219
- params.a = 4
220
- is(el.innerHTML, '<span>4</span>')
221
-
222
- params.a = 1
223
- is(el.innerHTML, '<span>1:1</span>')
224
- params.a = 4
225
- is(el.innerHTML, '<span>4</span>')
226
-
227
- params.a = null
228
- })
229
-
230
- test('if: reactive values', async () => {
231
- let el = h`<p>
232
- <span :if="a==1" :text="'1:'+a"></span>
233
- <span :else :if="a==2" :text="'2:'+a"></span>
234
- <span :else :text="a"></span>
235
- </p>`
236
-
237
- const a = signal(1)
238
- sprae(el, { a })
239
-
240
- is(el.innerHTML, '<span>1:1</span>')
241
- a.value = 2
242
- is(el.innerHTML, '<span>2:2</span>')
243
- a.value = 3
244
- is(el.innerHTML, '<span>3</span>')
245
- a.value = 4
246
- is(el.innerHTML, '<span>4</span>')
247
-
248
- a.value = 1
249
- is(el.innerHTML, '<span>1:1</span>')
250
- a.value = 4
251
- is(el.innerHTML, '<span>4</span>')
252
- })
253
-
254
- test('if: (#3) subsequent content is not abandoned', async () => {
255
- let x = h`<x><y :if="!!y"></y><z :text="123"></z></x>`
256
- sprae(x, {y: false})
257
- is(x.outerHTML, `<x><z>123</z></x>`)
258
- })
259
-
260
- test('if: + :with doesnt prevent secondary effects from happening', () => {
261
- let el = h`<div><x :if="x" :with="{}" :text="x"></x></div>`
262
- let state = sprae(el, {x:''})
263
- is(el.innerHTML, ``)
264
- state.x = '123'
265
- is(el.innerHTML, `<x>123</x>`)
266
-
267
- // NOTE: we ignore this case
268
- // let el2 = h`<div><x :if="x" :with="{x:cond}" :text="x"></x></div>`
269
- // let state2 = sprae(el, {cond:''})
270
- // is(el2.innerHTML, ``)
271
- // state2.cond = '123'
272
- // is(el2.innerHTML, `<x>123</x>`)
273
- })
274
-
275
- test('each: array', async () => {
276
- // FIXME: in some conspicuous reason jsdom fails to update text nodes somehow
277
- let el = h`<p>
278
- <span :each="a in b" :text="a"></span>
279
- </p>`
280
-
281
- const params = sprae(el, { b: [] })
282
-
283
- is(el.innerHTML, '')
284
- console.log('set 1,2')
285
- params.b = [1,2]
286
- is(el.innerHTML, '<span>1</span><span>2</span>')
287
- params.b = []
288
- is(el.innerHTML, '')
289
- params.b = null
290
- is(el.innerHTML, '')
291
- })
292
-
293
- test('each: object', async () => {
294
- // FIXME: in some conspicuous reason jsdom fails to update text nodes somehow
295
- let el = h`<p>
296
- <span :each="x,key in b" :text="[key,x]"></span>
297
- </p>`
298
-
299
- const params = sprae(el, { b: null })
300
-
301
- is(el.innerHTML, '')
302
- console.log('set 1,2')
303
- params.b = { x:1, y:2 }
304
- is(el.innerHTML, '<span>x,1</span><span>y,2</span>')
305
- params.b = []
306
- is(el.innerHTML, '')
307
- params.b = null
308
- is(el.innerHTML, '')
309
- })
310
-
311
- test('each: loop within loop', async () => {
312
- let el = h`<p>
313
- <x :each="b in c"><y :each="a in b" :text="a"></y></x>
314
- </p>`
315
-
316
- const params = sprae(el, { c: [[1,2], [3,4]] })
317
-
318
- is(el.innerHTML, '<x><y>1</y><y>2</y></x><x><y>3</y><y>4</y></x>')
319
- params.c = [[5,6], [3,4]]
320
- is(el.innerHTML, '<x><y>5</y><y>6</y></x><x><y>3</y><y>4</y></x>')
321
- // params.c[1] = [7,8]
322
- params.c = [params.c[0], [7,8]]
323
- is(el.innerHTML, '<x><y>5</y><y>6</y></x><x><y>7</y><y>8</y></x>')
324
- // is(el.innerHTML, '<span>1</span><span>2</span>')
325
- params.c = []
326
- is(el.innerHTML, '')
327
- // params.b = null
328
- // is(el.innerHTML, '')
329
- })
330
-
331
- test('each: reactive values', async () => {
332
- let el = h`<p>
333
- <span :each="a in b" :text="a"></span>
334
- </p>`
335
-
336
- const b = signal([])
337
- const params = sprae(el, { b })
338
-
339
- is(el.innerHTML, '')
340
- b.value = [1,2]
341
- is(el.innerHTML, '<span>1</span><span>2</span>')
342
- b.value = []
343
- is(el.innerHTML, '')
344
- params.b = null
345
- is(el.innerHTML, '')
346
- })
347
-
348
- test('each: loop with condition', async () => {
349
- // NOTE: there doesn't seem to be much value in exactly that
350
- // also it creates confusion with :else directive
351
- // prohibitin that allows in-order directives init
352
- let el = h`<p>
353
- <span :each="a in b" :text="a" :if="c"></span>
354
- </p>`
355
-
356
- const params = sprae(el, { b: [1,2], c: false })
357
-
358
- is(el.innerHTML, '')
359
- params.c = true
360
- is(el.innerHTML, '<span>1</span><span>2</span>')
361
- params.b = [1]
362
- is(el.innerHTML, '<span>1</span>')
363
- params.b = null
364
- is(el.innerHTML, '')
365
- })
366
-
367
- test('each: condition with loop', async () => {
368
- let el = h`<p>
369
- <span :if="c" :each="a in b" :text="a"></span>
370
- <span :else :text="c"></span>
371
- </p>`
372
-
373
- const params = sprae(el, { b: [1,2], c: false })
374
-
375
- is(el.innerHTML, '<span>false</span>')
376
- params.c = true
377
- is(el.innerHTML, '<span>1</span><span>2</span>')
378
- params.b = [1]
379
- is(el.innerHTML, '<span>1</span>')
380
- params.b = null
381
- is(el.innerHTML, '')
382
- console.log('c=false')
383
- params.c = false
384
- is(el.innerHTML, '<span>false</span>')
385
- })
386
-
387
- test('each: loop within condition', async () => {
388
- let el = h`<p>
389
- <x :if="a==1"><y :each="i in a" :text="i"></y></x>
390
- <x :else :if="a==2"><y :each="i in a" :text="-i"></y></x>
391
- </p>`
392
-
393
- const params = sprae(el, { a: 1 })
394
-
395
- is(el.innerHTML, '<x><y>1</y></x>')
396
- params.a = 2
397
- is(el.innerHTML, '<x><y>-1</y><y>-2</y></x>')
398
- params.a = 0
399
- is(el.innerHTML, '')
400
- })
401
-
402
- test('each: condition within loop', async () => {
403
- let el = h`<p>
404
- <x :each="a in b">
405
- <y :if="a==1" :text="'1:'+a"></y>
406
- <y :else :if="a==2" :text="'2:'+a"></y>
407
- <y :else :text="a"></y>
408
- </x>
409
- </p>`
410
-
411
- const params = sprae(el, { b: [1,2,3] })
412
-
413
- is(el.innerHTML, '<x><y>1:1</y></x><x><y>2:2</y></x><x><y>3</y></x>')
414
- params.b = [2]
415
- is(el.innerHTML, '<x><y>2:2</y></x>')
416
- params.b = null
417
- is(el.innerHTML, '')
418
- })
419
-
420
- test('each: next items have own "this", not single one', async () => {
421
- // FIXME: let el = h`<x :each="x in 3"></x>`
422
- let el = h`<div><x :each="x in 3" :data="{x}" :x="log.push(x, this.dataset.x)"></x></div>`
423
- let log = []
424
- let state = sprae(el, {log})
425
- is(state.log, [1,'1',2,'2',3,'3'])
426
- })
427
-
428
- test('each: unkeyed', async () => {
429
- let el = h`<div><x :each="x in xs" :text="x"></x></div>`
430
- let state = sprae(el, {xs:[1,2,3]})
431
- is(el.children.length, 3)
432
- is(el.textContent, '123')
433
- // let first = el.firstChild
434
- state.xs = [1,3,2]
435
- // is(el.firstChild, first)
436
- is(el.textContent, '132')
437
- state.xs = [3,3,3]
438
- is(el.textContent, '333')
439
- // is(el.firstChild, first)
440
- })
441
-
442
- test('each: keyed', async () => {
443
- // keyed
444
- let el = h`<div><x :each="x in xs" :text="x" :key="x"></x></div>`
445
- let state = sprae(el, {xs:[1,2,3]})
446
- is(el.children.length, 3)
447
- is(el.textContent, '123')
448
- let first = el.firstChild
449
- state.xs = [1,3,2]
450
- is(el.firstChild, first)
451
- is(el.textContent, '132')
452
- state.xs = [3,3,3]
453
- is(el.textContent, '3')
454
- // is(el.firstChild, first)
455
- })
456
-
457
- test('each: wrapped source', async () => {
458
- let el = h`<div><x :each="i in (x || 2)" :text="i"></x></div>`
459
- sprae(el, {x:0})
460
- is(el.innerHTML, `<x>1</x><x>2</x>`)
461
- })
462
-
463
- test.todo('each: unmounted elements remove listeners', async () => {
464
- // let's hope they get removed without memory leaks :')
465
- })
466
-
467
- test('each: internal children get updated by state update, also: update by running again', () => {
468
- let el = h`<><x :each="item, idx in items" :text="item" :key="idx"></x></>`
469
- let state = sprae(el, { items: [1,2,3] })
470
- is(el.textContent, '123')
471
- state.items = [2, 2, 3]
472
- is(el.textContent, '223')
473
- state = sprae(el, { items: [0,2,3] })
474
- is(el.textContent, '023')
475
- // NOTE: this doesn't update items, since they're new array
476
- console.log('set items')
477
- state.items[0] = 1
478
- state.items = [...state.items]
479
- is(el.textContent, '123')
480
- })
481
-
482
- test('each: :id and others must receive value from context', () => {
483
- let el = h`<div><x :id="idx" :each="item, idx in items"></x></div>`
484
- sprae(el, {items:[1,2,3]})
485
- is(el.innerHTML,`<x id="1"></x><x id="2"></x><x id="3"></x>`)
486
- })
487
-
488
- test('each: key-based caching is in-sync with direct elements', () => {
489
- let el = h`<ul><li :each="i in x" :key="i" :id="i"></li></ul>`
490
- let el2 = h`<ul><li :each="i in x" :id="i"></li></ul>`
491
- let state = sprae(el, {x:2})
492
- let state2 = sprae(el2, {x:2})
493
- is(el.outerHTML, el2.outerHTML)
494
- el.firstChild.after(el.firstChild.cloneNode(true))
495
- el2.firstChild.after(el2.firstChild.cloneNode(true))
496
- state.x = 3
497
- state2.x = 3
498
- is(el.outerHTML, el2.outerHTML)
499
- })
500
-
501
- test('on: base', () => {
502
- let el = h`<div :on="{click(e){log.push('click')}, x}"></div>`
503
- let log = signal([])
504
- let params = sprae(el, {x(){log.value.push('x')}, log})
505
-
506
- is(el.outerHTML, `<div></div>`);
507
- el.dispatchEvent(new window.Event('click'));
508
- is(log.value, ['click'])
509
- el.dispatchEvent(new window.Event('x'));
510
- is(log.value, ['click','x'])
511
-
512
- params.x = function(){log.value.push('xx')}
513
- el.dispatchEvent(new window.Event('x'));
514
- is(log.value, ['click','x','xx']);
515
-
516
- console.log('make null')
517
- params.x = null;
518
- el.dispatchEvent(new window.Event('x'));
519
- is(log.value, ['click','x','xx']);
520
- })
521
-
522
- test('onevt: this context', e => {
523
- let el = h`<div :onx="function(){log.push(this)}"></div>`
524
- let state = sprae(el, {log: []})
525
- el.dispatchEvent(new window.Event('x'));
526
- is(state.log, [el])
527
- })
528
-
529
- test('on: this in chains refers to el', () => {
530
- let el = h`<div :ona..onb="function(e){x=this; log.push(1); return () => log.push(2)}"></div>`
531
- let state = sprae(el, {log:[], x:null})
532
- el.dispatchEvent(new window.Event('a'));
533
- is(state.log, [1])
534
- })
535
-
536
- test('on: multiple events', e => {
537
- let el = h`<div :onscroll:onclick:onx="e=>log.push(e.type)"></div>`
538
- let state = sprae(el, {log:[]})
539
-
540
- el.dispatchEvent(new window.Event('click'));
541
- is(state.log, ['click'])
542
- el.dispatchEvent(new window.Event('scroll'));
543
- is(state.log, ['click','scroll'])
544
- el.dispatchEvent(new window.Event('x'));
545
- is(state.log, ['click','scroll','x'])
546
- })
547
-
548
- test('on: in-out events', e => {
549
- // let el = document.createElement('x');
550
- // el.setAttribute(':onmousedown..onmouseup', 'e=>(log.push(e.type),e=>log.push(e.type))')
551
- let el = h`<x :onmousedown..onmouseup="(e) => { x=this; log.push(e.type); return e=>log.push(e.type); }"></x>`
552
-
553
- let state = sprae(el, {log:[],x:null})
554
- el.dispatchEvent(new window.Event('mousedown'));
555
- is(state.x, el);
556
- is(state.log, ['mousedown'])
557
- el.dispatchEvent(new window.Event('mouseup'));
558
- is(state.log, ['mousedown','mouseup'])
559
- })
560
-
561
- test('on: in-out side-effects', e => {
562
- let log = []
563
-
564
- // 1. skip in event and do directly out
565
- let el = h`<x :onin..onout="io"></x>`
566
- sprae(el, { io(e) {
567
- log.push(e.type)
568
- return (e) => (log.push(e.type), [1,2,3])
569
- } })
570
-
571
- el.dispatchEvent(new window.Event('out'));
572
- is(log, [])
573
-
574
- // 2. Some nonsensical return is fine
575
- el.dispatchEvent(new window.Event('in'));
576
- is(log, ['in'])
577
- el.dispatchEvent(new window.Event('out'));
578
- is(log, ['in','out'], 'out triggers right')
579
- el.dispatchEvent(new window.Event('out'));
580
- is(log, ['in','out'])
581
- el.dispatchEvent(new window.Event('in'));
582
- is(log, ['in','out','in'])
583
- el.dispatchEvent(new window.Event('in'));
584
- is(log, ['in','out','in', 'in'])
585
- el.dispatchEvent(new window.Event('out'));
586
- is(log, ['in','out','in','in','out','out'])
587
- el.dispatchEvent(new window.Event('out'));
588
- is(log, ['in','out','in','in','out','out'])
589
- })
590
-
591
- test('on: chain of events', e => {
592
- let el = h`<div :onmousedown..onmousemove..onmouseup="e=>(log.push(e.type),e=>(log.push(e.type),e=>log.push(e.type)))"></div>`
593
- let state = sprae(el, {log:[]})
594
-
595
- el.dispatchEvent(new window.Event('mousedown'));
596
- is(state.log, ['mousedown'])
597
- el.dispatchEvent(new window.Event('mousemove'));
598
- is(state.log, ['mousedown','mousemove'])
599
- el.dispatchEvent(new window.Event('mouseup'));
600
- is(state.log, ['mousedown','mousemove','mouseup'])
601
- el.dispatchEvent(new window.Event('mouseup'));
602
- is(state.log, ['mousedown','mousemove','mouseup'])
603
- el.dispatchEvent(new window.Event('mousedown'));
604
- is(state.log, ['mousedown','mousemove','mouseup','mousedown'])
605
- })
606
-
607
- test('on: parallel chains', e => {
608
- let el = h`<div :onx..ony..onz="e=>('x',log.push(e.type),e=>('y',log.push(e.type),e=>('z',log.push(e.type))))"></div>`
609
- let state = sprae(el, {log:[]})
610
-
611
- console.log('emit x')
612
- el.dispatchEvent(new window.Event('x'));
613
- is(state.log, ['x'])
614
- console.log('emit x')
615
- el.dispatchEvent(new window.Event('x'));
616
- is(state.log, ['x','x'])
617
- console.log('emit y')
618
- el.dispatchEvent(new window.Event('y'));
619
- is(state.log, ['x','x','y','y'])
620
- console.log('emit y')
621
- el.dispatchEvent(new window.Event('y'));
622
- is(state.log, ['x','x','y','y'])
623
- console.log('emit z')
624
- el.dispatchEvent(new window.Event('z'));
625
- console.log('emit y')
626
- el.dispatchEvent(new window.Event('y'));
627
- is(state.log, ['x','x','y','y','z','z'])
628
- el.dispatchEvent(new window.Event('z'));
629
- is(state.log, ['x','x','y','y','z','z']);
630
- el.dispatchEvent(new window.Event('x'));
631
- is(state.log, ['x','x','y','y','z','z','x']);
632
- })
633
-
634
- test('on: state changes between chain of events', e => {
635
- let el = h`<x :on="{'x..y':fn}"></x>`
636
- let log = []
637
- let state = sprae(el, {log, fn: () => (log.push('x1'), ()=>log.push('y1'))})
638
- console.log('emit x')
639
- el.dispatchEvent(new window.Event('x'));
640
- is(log, ['x1'])
641
- console.log('update fn')
642
- state.fn = () => (log.push('x2'), () => log.push('y2'))
643
- is(log, ['x1'])
644
- // console.log('xx')
645
- // NOTE: state update registers new chain listener before finishing prev chain
646
- // el.dispatchEvent(new window.Event('x'));
647
- // el.dispatchEvent(new window.Event('x'));
648
- // is(log, [1])
649
- console.log('emit y, y')
650
- el.dispatchEvent(new window.Event('y'));
651
- el.dispatchEvent(new window.Event('y'));
652
- is(log, ['x1', 'y1'])
653
- console.log('emit x')
654
- el.dispatchEvent(new window.Event('x'));
655
- is(log, ['x1','y1','x2'])
656
- console.log('emit y, y')
657
- el.dispatchEvent(new window.Event('y'));
658
- el.dispatchEvent(new window.Event('y'));
659
- is(log, ['x1','y1','x2','y2'])
660
- })
661
-
662
- test('on: once', e => {
663
- // NOTE: if callback updates it's still rebound
664
- let el = h`<x :onx.once="(e=>x&&log.push(this))" ></x>`
665
- let log = []
666
- let state = sprae(el, {log, x:1})
667
- el.dispatchEvent(new window.Event('x'));
668
- is(log, [el])
669
- el.dispatchEvent(new window.Event('x'));
670
- is(log, [el])
671
- state.x = 2
672
- el.dispatchEvent(new window.Event('x'));
673
- el.dispatchEvent(new window.Event('x'));
674
- is(log, [el])
675
- })
676
-
677
- test('on: capture, stop, prevent', e => {
678
- let el = h`<x :onx.capture="e=>log.push(1)"><y :onx="e=>log.push(2)"></y></x>`
679
- let state = sprae(el, {log:[]})
680
- el.firstChild.dispatchEvent(new window.Event('x', {bubbles:true}));
681
- is(state.log, [1,2])
682
-
683
- let el2 = h`<x :onx="e=>log.push(1)"><y :onx.stop="e=>log.push(2)"></y></x>`
684
- let state2 = sprae(el2, {log:[]})
685
- el2.firstChild.dispatchEvent(new window.Event('x', {bubbles:true}));
686
- is(state2.log, [2])
687
- })
688
-
689
- test('on: window, self', e => {
690
- let el = h`<x :onx.self="e=>log.push(1)"><y :onx.window="e=>log.push(2)"></y></x>`
691
- let state = sprae(el, {log:[]})
692
- el.firstChild.dispatchEvent(new window.Event('x', {bubbles:true}));
693
- is(state.log, [])
694
- el.dispatchEvent(new window.Event('x', {bubbles:true}));
695
- is(state.log, [1])
696
- window.dispatchEvent(new window.Event('x', {bubbles:true}));
697
- is(state.log, [1,2])
698
- })
699
-
700
- test('on: keys', e => {
701
- let el = h`<x :onkeydown.enter="e=>log.push(1)"></x>`
702
- let state = {log:[]}
703
- sprae(el, state)
704
- el.dispatchEvent(new window.KeyboardEvent('keydown', { key: '' }));
705
- is(state.log,[])
706
- el.dispatchEvent(new window.KeyboardEvent('keydown', { key: 'Enter' }));
707
- el.dispatchEvent(new window.KeyboardEvent('keydown', { key: '' }));
708
- is(state.log,[1])
709
- el.dispatchEvent(new window.KeyboardEvent('keydown', { key: 'Enter' }));
710
- is(state.log,[1,1])
711
- })
712
-
713
- test('on: key combinations', e => {
714
- let el = h`<x :onkeydown.ctrl-enter="e=>log.push(1)"></x>`
715
- let state = {log:[]}
716
- sprae(el, state)
717
- el.dispatchEvent(new window.KeyboardEvent('keydown', { key: '' }));
718
- is(state.log,[])
719
- el.dispatchEvent(new window.KeyboardEvent('keydown', { key: 'Enter' }));
720
- el.dispatchEvent(new window.KeyboardEvent('keydown', { key: '' }));
721
- is(state.log,[])
722
- el.dispatchEvent(new window.KeyboardEvent('keydown', { key: 'Enter', ctrlKey: true }));
723
- el.dispatchEvent(new window.KeyboardEvent('keydown', { key: 'Enter' }));
724
- is(state.log,[1])
725
- el.dispatchEvent(new window.KeyboardEvent('keydown', { key: 'Enter' }));
726
- el.dispatchEvent(new window.KeyboardEvent('keydown', { key: 'Enter', ctrlKey: true }));
727
- is(state.log,[1,1])
728
- let el2 = h`<x :onkeydown.ctrl-alt-enter="e=>log.push(1)"></x>`
729
- })
730
-
731
- test('on: keys with prevent', e => {
732
- let el = h`<y :onkeydown="e=>log.push(e.key)"><x :ref="x" :onkeydown.enter.stop></x></y>`
733
- let state = sprae(el, {log:[]})
734
- state.x.dispatchEvent(new window.KeyboardEvent('keydown', { key: 'x', bubbles: true }));
735
- console.log('enter')
736
- state.x.dispatchEvent(new window.KeyboardEvent('keydown', { key: 'Enter', bubbles: true }));
737
- is(state.log,['x'])
738
- })
739
-
740
- test('on: debounce', async e => {
741
- let el = h`<x :onkeydown.debounce-1="e=>log.push(e.key)"></x>`
742
- let state = sprae(el, {log:[]})
743
- el.dispatchEvent(new window.KeyboardEvent('keydown', { key: 'x', bubbles: true }));
744
- is(state.log, [])
745
- await time(2)
746
- is(state.log, ['x'])
747
- })
748
-
749
- test('on: debounce 0', async e => {
750
- let el = h`<x :onkeydown.debounce-0="e=>log.push(e.key)"></x>`
751
- let state = sprae(el, {log:[]})
752
- el.dispatchEvent(new window.KeyboardEvent('keydown', { key: 'x', bubbles: true }));
753
- is(state.log, [])
754
- await time(2)
755
- is(state.log, ['x'])
756
- })
757
-
758
- test('on: throttle', async e => {
759
- let el = h`<x :onkeydown.throttle-10="e=>log.push(e.key)"></x>`
760
- let state = sprae(el, {log:[]})
761
- el.dispatchEvent(new window.KeyboardEvent('keydown', { key: 'x', bubbles: true }));
762
- el.dispatchEvent(new window.KeyboardEvent('keydown', { key: 'x', bubbles: true }));
763
- is(state.log, ['x'])
764
- await time(5)
765
- el.dispatchEvent(new window.KeyboardEvent('keydown', { key: 'x', bubbles: true }));
766
- is(state.log, ['x'])
767
- await time(10)
768
- is(state.log, ['x', 'x'])
769
- await time(10)
770
- is(state.log, ['x', 'x'])
771
- el.dispatchEvent(new window.KeyboardEvent('keydown', { key: 'x', bubbles: true }));
772
- is(state.log, ['x', 'x', 'x'])
773
- })
774
-
775
- test('on: toggle', async e => {
776
- let el = h`<x :onx.toggle="e=>(log.push(1),e=>log.push(2))"></x>`
777
- let state = sprae(el, {log:[]})
778
- el.dispatchEvent(new window.KeyboardEvent('x'));
779
- is(state.log, [1])
780
- el.dispatchEvent(new window.KeyboardEvent('x'));
781
- is(state.log, [1,2])
782
- el.dispatchEvent(new window.KeyboardEvent('x'));
783
- is(state.log, [1,2,1])
784
- el.dispatchEvent(new window.KeyboardEvent('x'));
785
- is(state.log, [1,2,1,2])
786
- })
787
-
788
- test.skip('on: nexttick', async e => {
789
- let el = h`<x :onkeydown.nexttick="e=>log.push(e.key)"></x>`
790
- let state = sprae(el, {log:[]})
791
- el.dispatchEvent(new window.KeyboardEvent('keydown', { key: 'x', bubbles: true }));
792
- el.dispatchEvent(new window.KeyboardEvent('keydown', { key: 'x', bubbles: true }));
793
- is(state.log, [])
794
- await time()
795
- is(state.log, ['x', 'x'])
796
- })
797
-
798
- test('on: modifiers chain', async e => {
799
- let el = h`<x :onkeydown.letter..onkeyup.letter="e=>(log.push(e.key),(e)=>log.push(e.key))"></x>`
800
- let state = sprae(el, {log:[]})
801
- el.dispatchEvent(new window.KeyboardEvent('keydown', { key: 'x', bubbles: true }));
802
- el.dispatchEvent(new window.KeyboardEvent('keydown', { key: 'Enter', bubbles: true }));
803
- is(state.log,['x'])
804
- el.dispatchEvent(new window.KeyboardEvent('keyup', { key: 'Enter', bubbles: true }));
805
- is(state.log,['x'])
806
- el.dispatchEvent(new window.KeyboardEvent('keyup', { key: 'x', bubbles: true }));
807
- is(state.log,['x', 'x'])
808
- })
809
-
810
- test('with: inline', () => {
811
- let el = h`<x :with="{foo:'bar'}"><y :text="foo + baz"></y></x>`
812
- let state = sprae(el, {baz: 'qux'})
813
- // FIXME: this doesn't inherit root scope baz property and instead uses hard-initialized one
814
- is(el.innerHTML, `<y>barqux</y>`)
815
- state.baz = 'quux'
816
- is(el.innerHTML, `<y>barquux</y>`)
817
- })
818
- test('with: inline reactive', () => {
819
- let el = h`<x :with="{foo:'bar'}"><y :text="foo + baz"></y></x>`
820
- let baz = signal('qux')
821
- sprae(el, {baz})
822
- // FIXME: this doesn't inherit root scope baz property and instead uses hard-initialized one
823
- is(el.innerHTML, `<y>barqux</y>`)
824
- baz.value = 'quux'
825
- is(el.innerHTML, `<y>barquux</y>`)
826
- })
827
- test('with: data', () => {
828
- let el = h`<x :with="x"><y :text="foo"></y></x>`
829
- let state = sprae(el, {x:{foo:'bar'}})
830
- is(el.innerHTML, `<y>bar</y>`)
831
- console.log('update')
832
- state.x.foo = 'baz'
833
- // Object.assign(state, {x:{foo:'baz'}})
834
- is(el.innerHTML, `<y>baz</y>`)
835
- })
836
- test('with: transparency', () => {
837
- // NOTE: y:text initializes through directive, not through parent
838
- // therefore by default :text uses parent's state, not defined by element itself
839
- let el = h`<x :with="{foo:'foo'}"><y :with="b" :text="foo+bar"></y></x>`
840
- let params = sprae(el, {b:{bar:'bar'}})
841
- is(el.innerHTML, `<y>foobar</y>`)
842
- params.b.bar = 'baz'
843
- is(el.innerHTML, `<y>foobaz</y>`)
844
- })
845
- test('with: reactive transparency', () => {
846
- let el = h`<x :with="{foo:1}"><y :with="b.c" :text="foo+bar"></y></x>`
847
- const bar = signal('2')
848
- sprae(el, {b:{c:{bar}}})
849
- is(el.innerHTML, `<y>12</y>`)
850
- bar.value = '3'
851
- is(el.innerHTML, `<y>13</y>`)
852
- })
853
- test('with: writes to state', () => {
854
- let a = h`<x :with="{a:1}"><y :on="{x(){a++}}" :text="a"></y></x>`
855
- sprae(a)
856
- is(a.innerHTML, `<y>1</y>`)
857
- a.firstChild.dispatchEvent(new window.Event('x'))
858
- is(a.innerHTML, `<y>2</y>`)
859
- a.firstChild.dispatchEvent(new window.Event('x'))
860
- is(a.innerHTML, `<y>3</y>`)
861
- })
862
-
863
- test('ref: base', () => {
864
- let a = h`<a :ref="a" :init="log.push(a), null" :text="b"></a>`
865
- let state = sprae(a, {log:[], b:1})
866
- is(state.log[0], a)
867
- is(a.outerHTML, `<a>1</a>`)
868
- state.b = 2
869
- is(a.outerHTML, `<a>2</a>`)
870
- is(state.a, a, 'Exposes to the state');
871
- })
872
-
873
- test('ref: with :each', () => {
874
- let a = h`<y><x :ref="x" :each="item in items" :text="log.push(x), item"/></y>`
875
- let state = sprae(a, {log: [], items: [1,2]})
876
- is(a.innerHTML, `<x>1</x><x>2</x>`)
877
- is(state.log, [...a.children])
878
- })
879
-
880
- test(':: reactive values', async () => {
881
- let a = new Promise((ok) => setTimeout(() => ok(2), 10))
882
-
883
- let el = h`<x :text="a">1</x>`
884
- sprae(el, {a})
885
- is(el.outerHTML, `<x></x>`)
886
-
887
- await time(20)
888
- is(el.outerHTML, `<x>2</x>`)
889
- })
890
-
891
- test(':: null result does nothing', async () => {
892
- let a = h`<x :="undefined"></x>`
893
- sprae(a)
894
- is(a.outerHTML, `<x></x>`)
895
- })
896
-
897
- test(':: scope refers to current element', async () => {
898
- let el = h`<x :text="log.push(this)"></x>`
899
- let state = sprae(el, {log:[]})
900
- is(state.log, [el])
901
- })
902
-
903
- test.todo(':: scope directives must come first', async () => {
904
- // NOTE: we init attributes in order of definition
905
- let a = h`<x :text="y" :with="{y:1}" :ref="x"></x>`
906
- sprae(a, {})
907
- is(a.outerHTML, `<x>1</x>`)
908
- })
909
-
910
- test.todo('getters', async () => {
911
- let x = h`<x>
912
- <h2 :if="doubledCount > 10">YAY!</h2>
913
- <button :text="count" :on="{click:increment}"/>
914
- <button :text="doubledCount" :on="{click:increment}"/>
915
- </x>`
916
- document.body.appendChild(x)
917
- let state = sprae(x, {
918
- count:0,
919
- get doubledCount(){ console.log(this); return this.count * 2},
920
- increment(){ this.count++ }
921
- })
922
- })
1
+ import './state.js'
2
+ import './dom.js'