zero-query 0.9.9 → 1.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/README.md +33 -32
- package/cli/args.js +1 -1
- package/cli/commands/build.js +2 -2
- package/cli/commands/bundle.js +15 -15
- package/cli/commands/create.js +2 -2
- package/cli/commands/dev/devtools/index.js +1 -1
- package/cli/commands/dev/devtools/js/core.js +14 -14
- package/cli/commands/dev/devtools/js/elements.js +4 -4
- package/cli/commands/dev/devtools/js/stats.js +1 -1
- package/cli/commands/dev/devtools/styles.css +2 -2
- package/cli/commands/dev/index.js +2 -2
- package/cli/commands/dev/logger.js +1 -1
- package/cli/commands/dev/overlay.js +21 -14
- package/cli/commands/dev/server.js +5 -5
- package/cli/commands/dev/validator.js +7 -7
- package/cli/commands/dev/watcher.js +6 -6
- package/cli/help.js +3 -3
- package/cli/index.js +2 -2
- package/cli/scaffold/default/app/app.js +17 -18
- package/cli/scaffold/default/app/components/about.js +9 -9
- package/cli/scaffold/default/app/components/api-demo.js +6 -6
- package/cli/scaffold/default/app/components/contact-card.js +4 -4
- package/cli/scaffold/default/app/components/contacts/contacts.css +2 -2
- package/cli/scaffold/default/app/components/contacts/contacts.html +3 -3
- package/cli/scaffold/default/app/components/contacts/contacts.js +11 -11
- package/cli/scaffold/default/app/components/counter.js +8 -8
- package/cli/scaffold/default/app/components/home.js +13 -13
- package/cli/scaffold/default/app/components/not-found.js +1 -1
- package/cli/scaffold/default/app/components/playground/playground.css +1 -1
- package/cli/scaffold/default/app/components/playground/playground.html +11 -11
- package/cli/scaffold/default/app/components/playground/playground.js +11 -11
- package/cli/scaffold/default/app/components/todos.js +8 -8
- package/cli/scaffold/default/app/components/toolkit/toolkit.css +1 -1
- package/cli/scaffold/default/app/components/toolkit/toolkit.html +4 -4
- package/cli/scaffold/default/app/components/toolkit/toolkit.js +7 -7
- package/cli/scaffold/default/app/routes.js +1 -1
- package/cli/scaffold/default/app/store.js +1 -1
- package/cli/scaffold/default/global.css +2 -2
- package/cli/scaffold/default/index.html +2 -2
- package/cli/scaffold/minimal/app/app.js +6 -7
- package/cli/scaffold/minimal/app/components/about.js +5 -5
- package/cli/scaffold/minimal/app/components/counter.js +6 -6
- package/cli/scaffold/minimal/app/components/home.js +8 -8
- package/cli/scaffold/minimal/app/components/not-found.js +1 -1
- package/cli/scaffold/minimal/app/routes.js +1 -1
- package/cli/scaffold/minimal/app/store.js +1 -1
- package/cli/scaffold/minimal/global.css +2 -2
- package/cli/scaffold/minimal/index.html +1 -1
- package/cli/scaffold/ssr/app/app.js +1 -2
- package/cli/scaffold/ssr/app/components/about.js +5 -5
- package/cli/scaffold/ssr/app/components/home.js +2 -2
- package/cli/scaffold/ssr/app/components/not-found.js +1 -1
- package/cli/scaffold/ssr/app/routes.js +1 -1
- package/cli/scaffold/ssr/global.css +2 -2
- package/cli/scaffold/ssr/index.html +2 -2
- package/cli/scaffold/ssr/server/index.js +4 -4
- package/cli/utils.js +6 -6
- package/dist/zquery.dist.zip +0 -0
- package/dist/zquery.js +508 -227
- package/dist/zquery.min.js +2 -2
- package/index.d.ts +16 -13
- package/index.js +7 -5
- package/package.json +2 -2
- package/src/component.js +64 -63
- package/src/core.js +15 -15
- package/src/diff.js +38 -38
- package/src/errors.js +17 -17
- package/src/expression.js +15 -17
- package/src/http.js +4 -4
- package/src/reactive.js +75 -9
- package/src/router.js +104 -24
- package/src/ssr.js +28 -28
- package/src/store.js +103 -21
- package/src/utils.js +64 -12
- package/tests/audit.test.js +143 -15
- package/tests/cli.test.js +20 -20
- package/tests/component.test.js +121 -121
- package/tests/core.test.js +56 -56
- package/tests/diff.test.js +42 -42
- package/tests/errors.test.js +5 -5
- package/tests/expression.test.js +58 -53
- package/tests/http.test.js +20 -20
- package/tests/reactive.test.js +185 -24
- package/tests/router.test.js +501 -74
- package/tests/ssr.test.js +15 -13
- package/tests/store.test.js +264 -23
- package/tests/utils.test.js +163 -26
- package/types/collection.d.ts +2 -2
- package/types/component.d.ts +5 -5
- package/types/errors.d.ts +3 -3
- package/types/http.d.ts +3 -3
- package/types/misc.d.ts +9 -9
- package/types/reactive.d.ts +25 -3
- package/types/router.d.ts +10 -6
- package/types/ssr.d.ts +2 -2
- package/types/store.d.ts +40 -5
- package/types/utils.d.ts +1 -1
package/tests/component.test.js
CHANGED
|
@@ -15,7 +15,7 @@ beforeEach(() => {
|
|
|
15
15
|
// Component registration
|
|
16
16
|
// ---------------------------------------------------------------------------
|
|
17
17
|
|
|
18
|
-
describe('component()
|
|
18
|
+
describe('component() - registration', () => {
|
|
19
19
|
it('registers a component', () => {
|
|
20
20
|
component('test-comp', {
|
|
21
21
|
state: () => ({ count: 0 }),
|
|
@@ -79,7 +79,7 @@ describe('mount()', () => {
|
|
|
79
79
|
// Lifecycle hooks
|
|
80
80
|
// ---------------------------------------------------------------------------
|
|
81
81
|
|
|
82
|
-
describe('component
|
|
82
|
+
describe('component - lifecycle', () => {
|
|
83
83
|
it('calls init on creation', () => {
|
|
84
84
|
const initFn = vi.fn();
|
|
85
85
|
component('life-init', {
|
|
@@ -144,7 +144,7 @@ describe('component — lifecycle', () => {
|
|
|
144
144
|
// Reactive state
|
|
145
145
|
// ---------------------------------------------------------------------------
|
|
146
146
|
|
|
147
|
-
describe('component
|
|
147
|
+
describe('component - reactive state', () => {
|
|
148
148
|
it('re-renders on state change', async () => {
|
|
149
149
|
component('react-state', {
|
|
150
150
|
state: () => ({ count: 0 }),
|
|
@@ -186,7 +186,7 @@ describe('component — reactive state', () => {
|
|
|
186
186
|
// Props
|
|
187
187
|
// ---------------------------------------------------------------------------
|
|
188
188
|
|
|
189
|
-
describe('component
|
|
189
|
+
describe('component - props', () => {
|
|
190
190
|
it('receives props', () => {
|
|
191
191
|
component('prop-test', {
|
|
192
192
|
render() { return `<span class="prop">${this.props.label}</span>`; },
|
|
@@ -211,7 +211,7 @@ describe('component — props', () => {
|
|
|
211
211
|
// Computed properties
|
|
212
212
|
// ---------------------------------------------------------------------------
|
|
213
213
|
|
|
214
|
-
describe('component
|
|
214
|
+
describe('component - computed', () => {
|
|
215
215
|
it('derives values from state', () => {
|
|
216
216
|
component('comp-computed', {
|
|
217
217
|
state: () => ({ count: 5 }),
|
|
@@ -248,7 +248,7 @@ describe('component — computed', () => {
|
|
|
248
248
|
// User methods
|
|
249
249
|
// ---------------------------------------------------------------------------
|
|
250
250
|
|
|
251
|
-
describe('component
|
|
251
|
+
describe('component - methods', () => {
|
|
252
252
|
it('binds user methods to instance', () => {
|
|
253
253
|
let captured;
|
|
254
254
|
component('method-test', {
|
|
@@ -268,7 +268,7 @@ describe('component — methods', () => {
|
|
|
268
268
|
// setState
|
|
269
269
|
// ---------------------------------------------------------------------------
|
|
270
270
|
|
|
271
|
-
describe('component
|
|
271
|
+
describe('component - setState', () => {
|
|
272
272
|
it('batch updates state', async () => {
|
|
273
273
|
component('set-state', {
|
|
274
274
|
state: () => ({ a: 1, b: 2 }),
|
|
@@ -302,7 +302,7 @@ describe('component — setState', () => {
|
|
|
302
302
|
// emit
|
|
303
303
|
// ---------------------------------------------------------------------------
|
|
304
304
|
|
|
305
|
-
describe('component
|
|
305
|
+
describe('component - emit', () => {
|
|
306
306
|
it('dispatches custom event', () => {
|
|
307
307
|
component('emit-test', {
|
|
308
308
|
render() { return '<div>emit</div>'; },
|
|
@@ -324,7 +324,7 @@ describe('component — emit', () => {
|
|
|
324
324
|
// destroy
|
|
325
325
|
// ---------------------------------------------------------------------------
|
|
326
326
|
|
|
327
|
-
describe('component
|
|
327
|
+
describe('component - destroy', () => {
|
|
328
328
|
it('clears innerHTML and removes from registry', () => {
|
|
329
329
|
component('destroy-test', {
|
|
330
330
|
render() { return '<div class="will-die">alive</div>'; },
|
|
@@ -400,7 +400,7 @@ describe('mountAll()', () => {
|
|
|
400
400
|
// z-if / z-else-if / z-else
|
|
401
401
|
// ---------------------------------------------------------------------------
|
|
402
402
|
|
|
403
|
-
describe('component
|
|
403
|
+
describe('component - z-if directive', () => {
|
|
404
404
|
it('shows element when condition is true', () => {
|
|
405
405
|
component('zif-true', {
|
|
406
406
|
state: () => ({ show: true }),
|
|
@@ -446,7 +446,7 @@ describe('component — z-if directive', () => {
|
|
|
446
446
|
// z-show
|
|
447
447
|
// ---------------------------------------------------------------------------
|
|
448
448
|
|
|
449
|
-
describe('component
|
|
449
|
+
describe('component - z-show directive', () => {
|
|
450
450
|
it('sets display none when falsy', () => {
|
|
451
451
|
component('zshow-hide', {
|
|
452
452
|
state: () => ({ visible: false }),
|
|
@@ -473,7 +473,7 @@ describe('component — z-show directive', () => {
|
|
|
473
473
|
// z-for
|
|
474
474
|
// ---------------------------------------------------------------------------
|
|
475
475
|
|
|
476
|
-
describe('component
|
|
476
|
+
describe('component - z-for directive', () => {
|
|
477
477
|
it('renders list items', () => {
|
|
478
478
|
component('zfor-list', {
|
|
479
479
|
state: () => ({ items: ['red', 'green', 'blue'] }),
|
|
@@ -530,7 +530,7 @@ describe('component — z-for directive', () => {
|
|
|
530
530
|
// z-bind / :attr
|
|
531
531
|
// ---------------------------------------------------------------------------
|
|
532
532
|
|
|
533
|
-
describe('component
|
|
533
|
+
describe('component - z-bind directive', () => {
|
|
534
534
|
it('binds attribute dynamically with :attr', () => {
|
|
535
535
|
component('zbind-attr', {
|
|
536
536
|
state: () => ({ cls: 'active' }),
|
|
@@ -567,7 +567,7 @@ describe('component — z-bind directive', () => {
|
|
|
567
567
|
// z-class
|
|
568
568
|
// ---------------------------------------------------------------------------
|
|
569
569
|
|
|
570
|
-
describe('component
|
|
570
|
+
describe('component - z-class directive', () => {
|
|
571
571
|
it('adds classes from object', () => {
|
|
572
572
|
component('zclass-obj', {
|
|
573
573
|
state: () => ({ isActive: true, isHidden: false }),
|
|
@@ -586,7 +586,7 @@ describe('component — z-class directive', () => {
|
|
|
586
586
|
// z-text
|
|
587
587
|
// ---------------------------------------------------------------------------
|
|
588
588
|
|
|
589
|
-
describe('component
|
|
589
|
+
describe('component - z-text directive', () => {
|
|
590
590
|
it('sets textContent safely', () => {
|
|
591
591
|
component('ztext-test', {
|
|
592
592
|
state: () => ({ msg: 'Hello <b>world</b>' }),
|
|
@@ -605,7 +605,7 @@ describe('component — z-text directive', () => {
|
|
|
605
605
|
// z-html
|
|
606
606
|
// ---------------------------------------------------------------------------
|
|
607
607
|
|
|
608
|
-
describe('component
|
|
608
|
+
describe('component - z-html directive', () => {
|
|
609
609
|
it('sets innerHTML', () => {
|
|
610
610
|
component('zhtml-test', {
|
|
611
611
|
state: () => ({ content: '<strong>bold</strong>' }),
|
|
@@ -622,7 +622,7 @@ describe('component — z-html directive', () => {
|
|
|
622
622
|
// z-ref
|
|
623
623
|
// ---------------------------------------------------------------------------
|
|
624
624
|
|
|
625
|
-
describe('component
|
|
625
|
+
describe('component - z-ref', () => {
|
|
626
626
|
it('populates refs on mount', () => {
|
|
627
627
|
component('zref-test', {
|
|
628
628
|
render() { return '<input z-ref="myInput" type="text">'; },
|
|
@@ -639,7 +639,7 @@ describe('component — z-ref', () => {
|
|
|
639
639
|
// z-model (two-way binding)
|
|
640
640
|
// ---------------------------------------------------------------------------
|
|
641
641
|
|
|
642
|
-
describe('component
|
|
642
|
+
describe('component - z-model', () => {
|
|
643
643
|
it('syncs input value from state on mount', () => {
|
|
644
644
|
component('zmodel-init', {
|
|
645
645
|
state: () => ({ name: 'Tony' }),
|
|
@@ -702,7 +702,7 @@ describe('component — z-model', () => {
|
|
|
702
702
|
// z-cloak
|
|
703
703
|
// ---------------------------------------------------------------------------
|
|
704
704
|
|
|
705
|
-
describe('component
|
|
705
|
+
describe('component - z-cloak', () => {
|
|
706
706
|
it('removes z-cloak attribute after render', () => {
|
|
707
707
|
component('zcloak-test', {
|
|
708
708
|
render() { return '<div z-cloak>content</div>'; },
|
|
@@ -718,7 +718,7 @@ describe('component — z-cloak', () => {
|
|
|
718
718
|
// z-pre
|
|
719
719
|
// ---------------------------------------------------------------------------
|
|
720
720
|
|
|
721
|
-
describe('component
|
|
721
|
+
describe('component - z-pre', () => {
|
|
722
722
|
it('skips directive processing inside z-pre', () => {
|
|
723
723
|
component('zpre-test', {
|
|
724
724
|
state: () => ({ x: 42 }),
|
|
@@ -740,7 +740,7 @@ describe('component — z-pre', () => {
|
|
|
740
740
|
// z-style
|
|
741
741
|
// ---------------------------------------------------------------------------
|
|
742
742
|
|
|
743
|
-
describe('component
|
|
743
|
+
describe('component - z-style directive', () => {
|
|
744
744
|
it('applies object styles', () => {
|
|
745
745
|
component('zstyle-obj', {
|
|
746
746
|
state: () => ({ color: 'red' }),
|
|
@@ -757,7 +757,7 @@ describe('component — z-style directive', () => {
|
|
|
757
757
|
// Event binding (@event)
|
|
758
758
|
// ---------------------------------------------------------------------------
|
|
759
759
|
|
|
760
|
-
describe('component
|
|
760
|
+
describe('component - event binding', () => {
|
|
761
761
|
it('handles @click events', () => {
|
|
762
762
|
component('evt-click', {
|
|
763
763
|
state: () => ({ count: 0 }),
|
|
@@ -812,7 +812,7 @@ describe('component — event binding', () => {
|
|
|
812
812
|
// Watchers
|
|
813
813
|
// ---------------------------------------------------------------------------
|
|
814
814
|
|
|
815
|
-
describe('component
|
|
815
|
+
describe('component - watchers', () => {
|
|
816
816
|
it('fires watcher when state key changes', async () => {
|
|
817
817
|
const watchFn = vi.fn();
|
|
818
818
|
component('watch-test', {
|
|
@@ -834,7 +834,7 @@ describe('component — watchers', () => {
|
|
|
834
834
|
// Slots
|
|
835
835
|
// ---------------------------------------------------------------------------
|
|
836
836
|
|
|
837
|
-
describe('component
|
|
837
|
+
describe('component - slots', () => {
|
|
838
838
|
it('distributes default slot content', () => {
|
|
839
839
|
component('slot-test', {
|
|
840
840
|
render() { return '<div><slot>fallback</slot></div>'; },
|
|
@@ -1049,7 +1049,7 @@ describe('component — slots', () => {
|
|
|
1049
1049
|
const inst = mount('#srr', 'slot-rerender');
|
|
1050
1050
|
expect(document.querySelector('#srr .slotted span').textContent).toBe('Projected!');
|
|
1051
1051
|
|
|
1052
|
-
// Trigger re-render via state change (batched
|
|
1052
|
+
// Trigger re-render via state change (batched - need microtask flush)
|
|
1053
1053
|
inst.state.count = 5;
|
|
1054
1054
|
await new Promise(r => queueMicrotask(r));
|
|
1055
1055
|
expect(document.querySelector('#srr p').textContent).toBe('Count: 5');
|
|
@@ -1068,7 +1068,7 @@ describe('component — slots', () => {
|
|
|
1068
1068
|
expect(p.textContent).toContain('& <tag> "quotes"');
|
|
1069
1069
|
});
|
|
1070
1070
|
|
|
1071
|
-
// --- No slot tag in template
|
|
1071
|
+
// --- No slot tag in template - content is replaced entirely ---
|
|
1072
1072
|
|
|
1073
1073
|
it('discards projected content when template has no slot', () => {
|
|
1074
1074
|
component('slot-nosite', {
|
|
@@ -1131,7 +1131,7 @@ describe('component — slots', () => {
|
|
|
1131
1131
|
el.appendChild(document.createComment('this is a comment'));
|
|
1132
1132
|
document.body.appendChild(el);
|
|
1133
1133
|
mount('#scm', 'slot-comments');
|
|
1134
|
-
// Only a comment
|
|
1134
|
+
// Only a comment - should use fallback
|
|
1135
1135
|
expect(document.querySelector('#scm div').textContent).toBe('fallback');
|
|
1136
1136
|
});
|
|
1137
1137
|
|
|
@@ -1302,7 +1302,7 @@ describe('component — slots', () => {
|
|
|
1302
1302
|
// Scoped styles
|
|
1303
1303
|
// ---------------------------------------------------------------------------
|
|
1304
1304
|
|
|
1305
|
-
describe('component
|
|
1305
|
+
describe('component - scoped styles', () => {
|
|
1306
1306
|
it('injects scoped style tag', () => {
|
|
1307
1307
|
component('style-test', {
|
|
1308
1308
|
styles: '.box { color: red; }',
|
|
@@ -1321,7 +1321,7 @@ describe('component — scoped styles', () => {
|
|
|
1321
1321
|
// z-if toggling on state change
|
|
1322
1322
|
// ---------------------------------------------------------------------------
|
|
1323
1323
|
|
|
1324
|
-
describe('component
|
|
1324
|
+
describe('component - z-if reactive toggle', () => {
|
|
1325
1325
|
it('shows/hides element on state change', async () => {
|
|
1326
1326
|
component('zif-toggle', {
|
|
1327
1327
|
state: () => ({ visible: false }),
|
|
@@ -1368,7 +1368,7 @@ describe('component — z-if reactive toggle', () => {
|
|
|
1368
1368
|
// z-show reactive toggle
|
|
1369
1369
|
// ---------------------------------------------------------------------------
|
|
1370
1370
|
|
|
1371
|
-
describe('component
|
|
1371
|
+
describe('component - z-show reactive toggle', () => {
|
|
1372
1372
|
it('toggles display on state change', async () => {
|
|
1373
1373
|
component('zshow-toggle', {
|
|
1374
1374
|
state: () => ({ vis: true }),
|
|
@@ -1389,7 +1389,7 @@ describe('component — z-show reactive toggle', () => {
|
|
|
1389
1389
|
// z-for advanced
|
|
1390
1390
|
// ---------------------------------------------------------------------------
|
|
1391
1391
|
|
|
1392
|
-
describe('component
|
|
1392
|
+
describe('component - z-for advanced', () => {
|
|
1393
1393
|
it('re-renders list on state change', async () => {
|
|
1394
1394
|
component('zfor-rerender', {
|
|
1395
1395
|
state: () => ({ items: ['a', 'b'] }),
|
|
@@ -1459,7 +1459,7 @@ describe('component — z-for advanced', () => {
|
|
|
1459
1459
|
// z-bind advanced
|
|
1460
1460
|
// ---------------------------------------------------------------------------
|
|
1461
1461
|
|
|
1462
|
-
describe('component
|
|
1462
|
+
describe('component - z-bind advanced', () => {
|
|
1463
1463
|
it('binds data attribute dynamically', () => {
|
|
1464
1464
|
component('zbind-data', {
|
|
1465
1465
|
state: () => ({ val: '42' }),
|
|
@@ -1486,7 +1486,7 @@ describe('component — z-bind advanced', () => {
|
|
|
1486
1486
|
// z-class advanced
|
|
1487
1487
|
// ---------------------------------------------------------------------------
|
|
1488
1488
|
|
|
1489
|
-
describe('component
|
|
1489
|
+
describe('component - z-class advanced', () => {
|
|
1490
1490
|
it('handles multiple truthy/falsy classes', () => {
|
|
1491
1491
|
component('zclass-multi', {
|
|
1492
1492
|
state: () => ({ a: true, b: false, c: true }),
|
|
@@ -1506,7 +1506,7 @@ describe('component — z-class advanced', () => {
|
|
|
1506
1506
|
// z-style advanced
|
|
1507
1507
|
// ---------------------------------------------------------------------------
|
|
1508
1508
|
|
|
1509
|
-
describe('component
|
|
1509
|
+
describe('component - z-style advanced', () => {
|
|
1510
1510
|
it('applies multiple style properties', () => {
|
|
1511
1511
|
component('zstyle-multi', {
|
|
1512
1512
|
state: () => ({ bg: 'blue', size: '20px' }),
|
|
@@ -1525,7 +1525,7 @@ describe('component — z-style advanced', () => {
|
|
|
1525
1525
|
// z-model advanced
|
|
1526
1526
|
// ---------------------------------------------------------------------------
|
|
1527
1527
|
|
|
1528
|
-
describe('component
|
|
1528
|
+
describe('component - z-model advanced', () => {
|
|
1529
1529
|
it('handles z-model with select element', () => {
|
|
1530
1530
|
component('zmodel-select', {
|
|
1531
1531
|
state: () => ({ choice: 'b' }),
|
|
@@ -1572,7 +1572,7 @@ describe('component — z-model advanced', () => {
|
|
|
1572
1572
|
// z-ref advanced
|
|
1573
1573
|
// ---------------------------------------------------------------------------
|
|
1574
1574
|
|
|
1575
|
-
describe('component
|
|
1575
|
+
describe('component - z-ref advanced', () => {
|
|
1576
1576
|
it('collects multiple refs', () => {
|
|
1577
1577
|
component('zref-multi', {
|
|
1578
1578
|
render() { return '<input z-ref="first" type="text"><input z-ref="second" type="email">'; },
|
|
@@ -1591,7 +1591,7 @@ describe('component — z-ref advanced', () => {
|
|
|
1591
1591
|
// Event binding advanced
|
|
1592
1592
|
// ---------------------------------------------------------------------------
|
|
1593
1593
|
|
|
1594
|
-
describe('component
|
|
1594
|
+
describe('component - event binding advanced', () => {
|
|
1595
1595
|
it('handles multiple event bindings on different elements', () => {
|
|
1596
1596
|
let clickCount = 0, focusCount = 0;
|
|
1597
1597
|
component('evt-multi', {
|
|
@@ -1637,7 +1637,7 @@ describe('component — event binding advanced', () => {
|
|
|
1637
1637
|
// Watcher advanced
|
|
1638
1638
|
// ---------------------------------------------------------------------------
|
|
1639
1639
|
|
|
1640
|
-
describe('component
|
|
1640
|
+
describe('component - watchers advanced', () => {
|
|
1641
1641
|
it('receives correct old and new values', async () => {
|
|
1642
1642
|
let oldVal, newVal;
|
|
1643
1643
|
component('watch-vals', {
|
|
@@ -1675,7 +1675,7 @@ describe('component — watchers advanced', () => {
|
|
|
1675
1675
|
// Component state with no initial state
|
|
1676
1676
|
// ---------------------------------------------------------------------------
|
|
1677
1677
|
|
|
1678
|
-
describe('component
|
|
1678
|
+
describe('component - no state', () => {
|
|
1679
1679
|
it('works without state property', () => {
|
|
1680
1680
|
component('no-state', {
|
|
1681
1681
|
render() { return '<div>stateless</div>'; },
|
|
@@ -1692,7 +1692,7 @@ describe('component — no state', () => {
|
|
|
1692
1692
|
// Mount with element reference
|
|
1693
1693
|
// ---------------------------------------------------------------------------
|
|
1694
1694
|
|
|
1695
|
-
describe('mount
|
|
1695
|
+
describe('mount - with Element reference', () => {
|
|
1696
1696
|
it('accepts an Element directly instead of selector', () => {
|
|
1697
1697
|
component('mount-elem', {
|
|
1698
1698
|
state: () => ({ v: 'direct' }),
|
|
@@ -1711,7 +1711,7 @@ describe('mount — with Element reference', () => {
|
|
|
1711
1711
|
// Component interpolation edge cases
|
|
1712
1712
|
// ---------------------------------------------------------------------------
|
|
1713
1713
|
|
|
1714
|
-
describe('component
|
|
1714
|
+
describe('component - interpolation', () => {
|
|
1715
1715
|
it('handles template literal with arithmetic', () => {
|
|
1716
1716
|
component('interp-math', {
|
|
1717
1717
|
state: () => ({ a: 3, b: 4 }),
|
|
@@ -1758,7 +1758,7 @@ describe('component — interpolation', () => {
|
|
|
1758
1758
|
// Component emit with no listeners
|
|
1759
1759
|
// ---------------------------------------------------------------------------
|
|
1760
1760
|
|
|
1761
|
-
describe('component
|
|
1761
|
+
describe('component - emit edge cases', () => {
|
|
1762
1762
|
it('emit with no listeners does not throw', () => {
|
|
1763
1763
|
component('emit-noop', {
|
|
1764
1764
|
render() { return '<div>noop</div>'; },
|
|
@@ -1786,7 +1786,7 @@ describe('component — emit edge cases', () => {
|
|
|
1786
1786
|
// Component computed edge cases
|
|
1787
1787
|
// ---------------------------------------------------------------------------
|
|
1788
1788
|
|
|
1789
|
-
describe('component
|
|
1789
|
+
describe('component - computed edge cases', () => {
|
|
1790
1790
|
it('multiple computed properties', () => {
|
|
1791
1791
|
component('comp-multi-computed', {
|
|
1792
1792
|
state: () => ({ x: 2, y: 3 }),
|
|
@@ -1821,7 +1821,7 @@ describe('component — computed edge cases', () => {
|
|
|
1821
1821
|
// Slots advanced
|
|
1822
1822
|
// ---------------------------------------------------------------------------
|
|
1823
1823
|
|
|
1824
|
-
describe('component
|
|
1824
|
+
describe('component - slots advanced', () => {
|
|
1825
1825
|
it('handles multiple children in default slot', () => {
|
|
1826
1826
|
component('slot-multi', {
|
|
1827
1827
|
render() { return '<div><slot>fallback</slot></div>'; },
|
|
@@ -1851,7 +1851,7 @@ describe('component — slots advanced', () => {
|
|
|
1851
1851
|
// Component re-render preserves DOM via morphing
|
|
1852
1852
|
// ---------------------------------------------------------------------------
|
|
1853
1853
|
|
|
1854
|
-
describe('component
|
|
1854
|
+
describe('component - DOM morphing on re-render', () => {
|
|
1855
1855
|
it('preserves unchanged DOM nodes on re-render', async () => {
|
|
1856
1856
|
component('morph-preserve', {
|
|
1857
1857
|
state: () => ({ title: 'old', count: 0 }),
|
|
@@ -1889,7 +1889,7 @@ describe('component — DOM morphing on re-render', () => {
|
|
|
1889
1889
|
// MEMORY: destroy clears pending debounce/throttle timers
|
|
1890
1890
|
// ---------------------------------------------------------------------------
|
|
1891
1891
|
|
|
1892
|
-
describe('Component
|
|
1892
|
+
describe('Component - destroy clears pending timers', () => {
|
|
1893
1893
|
it('clears debounce timers for child elements on destroy', () => {
|
|
1894
1894
|
component('timer-clear', {
|
|
1895
1895
|
state: () => ({ val: 0 }),
|
|
@@ -1901,7 +1901,7 @@ describe('Component — destroy clears pending timers', () => {
|
|
|
1901
1901
|
document.body.innerHTML = '<timer-clear id="tcl"></timer-clear>';
|
|
1902
1902
|
const inst = mount('#tcl', 'timer-clear');
|
|
1903
1903
|
|
|
1904
|
-
// Click to start a debounce timer (5s
|
|
1904
|
+
// Click to start a debounce timer (5s - won't fire during test)
|
|
1905
1905
|
document.querySelector('#tcl button').click();
|
|
1906
1906
|
|
|
1907
1907
|
// Verify the state hasn't changed yet (debounced)
|
|
@@ -1917,7 +1917,7 @@ describe('Component — destroy clears pending timers', () => {
|
|
|
1917
1917
|
// z-model modifiers
|
|
1918
1918
|
// ===========================================================================
|
|
1919
1919
|
|
|
1920
|
-
describe('component
|
|
1920
|
+
describe('component - z-model z-lazy modifier', () => {
|
|
1921
1921
|
it('uses change event instead of input when z-lazy is present', () => {
|
|
1922
1922
|
component('zmodel-lazy', {
|
|
1923
1923
|
state: () => ({ val: '' }),
|
|
@@ -1939,7 +1939,7 @@ describe('component — z-model z-lazy modifier', () => {
|
|
|
1939
1939
|
});
|
|
1940
1940
|
});
|
|
1941
1941
|
|
|
1942
|
-
describe('component
|
|
1942
|
+
describe('component - z-model z-trim modifier', () => {
|
|
1943
1943
|
it('trims whitespace from input value', () => {
|
|
1944
1944
|
component('zmodel-trim', {
|
|
1945
1945
|
state: () => ({ val: '' }),
|
|
@@ -1954,7 +1954,7 @@ describe('component — z-model z-trim modifier', () => {
|
|
|
1954
1954
|
});
|
|
1955
1955
|
});
|
|
1956
1956
|
|
|
1957
|
-
describe('component
|
|
1957
|
+
describe('component - z-model z-number modifier', () => {
|
|
1958
1958
|
it('converts input value to number', () => {
|
|
1959
1959
|
component('zmodel-num', {
|
|
1960
1960
|
state: () => ({ val: 0 }),
|
|
@@ -1998,7 +1998,7 @@ describe('component — z-model z-number modifier', () => {
|
|
|
1998
1998
|
});
|
|
1999
1999
|
});
|
|
2000
2000
|
|
|
2001
|
-
describe('component
|
|
2001
|
+
describe('component - z-model contenteditable', () => {
|
|
2002
2002
|
it('reads and writes textContent for contenteditable elements', () => {
|
|
2003
2003
|
component('zmodel-ce', {
|
|
2004
2004
|
state: () => ({ val: 'initial' }),
|
|
@@ -2023,7 +2023,7 @@ describe('component — z-model contenteditable', () => {
|
|
|
2023
2023
|
// z-model z-debounce modifier
|
|
2024
2024
|
// ===========================================================================
|
|
2025
2025
|
|
|
2026
|
-
describe('component
|
|
2026
|
+
describe('component - z-model z-debounce modifier', () => {
|
|
2027
2027
|
it('delays state update by the specified ms', () => {
|
|
2028
2028
|
vi.useFakeTimers();
|
|
2029
2029
|
component('zmodel-debounce', {
|
|
@@ -2096,7 +2096,7 @@ describe('component — z-model z-debounce modifier', () => {
|
|
|
2096
2096
|
// z-model z-uppercase / z-lowercase modifiers
|
|
2097
2097
|
// ===========================================================================
|
|
2098
2098
|
|
|
2099
|
-
describe('component
|
|
2099
|
+
describe('component - z-model z-uppercase modifier', () => {
|
|
2100
2100
|
it('converts input value to uppercase before writing to state', () => {
|
|
2101
2101
|
component('zmodel-upper', {
|
|
2102
2102
|
state: () => ({ val: '' }),
|
|
@@ -2126,7 +2126,7 @@ describe('component — z-model z-uppercase modifier', () => {
|
|
|
2126
2126
|
});
|
|
2127
2127
|
});
|
|
2128
2128
|
|
|
2129
|
-
describe('component
|
|
2129
|
+
describe('component - z-model z-lowercase modifier', () => {
|
|
2130
2130
|
it('converts input value to lowercase before writing to state', () => {
|
|
2131
2131
|
component('zmodel-lower', {
|
|
2132
2132
|
state: () => ({ val: '' }),
|
|
@@ -2157,7 +2157,7 @@ describe('component — z-model z-lowercase modifier', () => {
|
|
|
2157
2157
|
});
|
|
2158
2158
|
|
|
2159
2159
|
|
|
2160
|
-
describe('component
|
|
2160
|
+
describe('component - z-model radio', () => {
|
|
2161
2161
|
it('checks the radio matching state value and writes back on change', () => {
|
|
2162
2162
|
component('zmodel-radio', {
|
|
2163
2163
|
state: () => ({ color: 'green' }),
|
|
@@ -2183,7 +2183,7 @@ describe('component — z-model radio', () => {
|
|
|
2183
2183
|
});
|
|
2184
2184
|
});
|
|
2185
2185
|
|
|
2186
|
-
describe('component
|
|
2186
|
+
describe('component - z-model multi-select', () => {
|
|
2187
2187
|
it('syncs multiple selected options to an array', () => {
|
|
2188
2188
|
component('zmodel-multisel', {
|
|
2189
2189
|
state: () => ({ chosen: ['b', 'c'] }),
|
|
@@ -2213,7 +2213,7 @@ describe('component — z-model multi-select', () => {
|
|
|
2213
2213
|
});
|
|
2214
2214
|
});
|
|
2215
2215
|
|
|
2216
|
-
describe('component
|
|
2216
|
+
describe('component - z-model dot-path keys', () => {
|
|
2217
2217
|
it('reads and writes nested state via dot path', () => {
|
|
2218
2218
|
component('zmodel-dot', {
|
|
2219
2219
|
state: () => ({ form: { name: 'Alice' } }),
|
|
@@ -2233,7 +2233,7 @@ describe('component — z-model dot-path keys', () => {
|
|
|
2233
2233
|
});
|
|
2234
2234
|
});
|
|
2235
2235
|
|
|
2236
|
-
describe('component
|
|
2236
|
+
describe('component - z-model does not duplicate listeners on re-render', () => {
|
|
2237
2237
|
it('handler fires only once per event after multiple re-renders', async () => {
|
|
2238
2238
|
let callCount = 0;
|
|
2239
2239
|
const origSetPath = null;
|
|
@@ -2263,7 +2263,7 @@ describe('component — z-model does not duplicate listeners on re-render', () =
|
|
|
2263
2263
|
// Event modifiers
|
|
2264
2264
|
// ===========================================================================
|
|
2265
2265
|
|
|
2266
|
-
describe('component
|
|
2266
|
+
describe('component - event modifier .prevent', () => {
|
|
2267
2267
|
it('calls preventDefault on the event', () => {
|
|
2268
2268
|
component('evt-prevent', {
|
|
2269
2269
|
handler() {},
|
|
@@ -2279,7 +2279,7 @@ describe('component — event modifier .prevent', () => {
|
|
|
2279
2279
|
});
|
|
2280
2280
|
});
|
|
2281
2281
|
|
|
2282
|
-
describe('component
|
|
2282
|
+
describe('component - event modifier .stop', () => {
|
|
2283
2283
|
it('calls stopPropagation on the event', () => {
|
|
2284
2284
|
let stopped = false;
|
|
2285
2285
|
component('evt-stop', {
|
|
@@ -2297,7 +2297,7 @@ describe('component — event modifier .stop', () => {
|
|
|
2297
2297
|
});
|
|
2298
2298
|
});
|
|
2299
2299
|
|
|
2300
|
-
describe('component
|
|
2300
|
+
describe('component - event modifier .self', () => {
|
|
2301
2301
|
it('only fires handler when target matches element', () => {
|
|
2302
2302
|
let count = 0;
|
|
2303
2303
|
component('evt-self', {
|
|
@@ -2319,7 +2319,7 @@ describe('component — event modifier .self', () => {
|
|
|
2319
2319
|
});
|
|
2320
2320
|
});
|
|
2321
2321
|
|
|
2322
|
-
describe('component
|
|
2322
|
+
describe('component - event modifier .once', () => {
|
|
2323
2323
|
it('only fires handler on the first event', () => {
|
|
2324
2324
|
let count = 0;
|
|
2325
2325
|
component('evt-once', {
|
|
@@ -2338,7 +2338,7 @@ describe('component — event modifier .once', () => {
|
|
|
2338
2338
|
});
|
|
2339
2339
|
});
|
|
2340
2340
|
|
|
2341
|
-
describe('component
|
|
2341
|
+
describe('component - event modifier .debounce', () => {
|
|
2342
2342
|
it('delays handler execution', () => {
|
|
2343
2343
|
vi.useFakeTimers();
|
|
2344
2344
|
let count = 0;
|
|
@@ -2382,7 +2382,7 @@ describe('component — event modifier .debounce', () => {
|
|
|
2382
2382
|
});
|
|
2383
2383
|
});
|
|
2384
2384
|
|
|
2385
|
-
describe('component
|
|
2385
|
+
describe('component - event modifier .throttle', () => {
|
|
2386
2386
|
it('fires immediately then blocks until window passes', () => {
|
|
2387
2387
|
vi.useFakeTimers();
|
|
2388
2388
|
let count = 0;
|
|
@@ -2408,7 +2408,7 @@ describe('component — event modifier .throttle', () => {
|
|
|
2408
2408
|
});
|
|
2409
2409
|
});
|
|
2410
2410
|
|
|
2411
|
-
describe('component
|
|
2411
|
+
describe('component - combined event modifiers', () => {
|
|
2412
2412
|
it('handles .prevent.stop together', () => {
|
|
2413
2413
|
component('evt-combo', {
|
|
2414
2414
|
handler() {},
|
|
@@ -2428,10 +2428,10 @@ describe('component — combined event modifiers', () => {
|
|
|
2428
2428
|
|
|
2429
2429
|
|
|
2430
2430
|
// ===========================================================================
|
|
2431
|
-
// Key modifiers
|
|
2431
|
+
// Key modifiers - .enter, .escape, .tab, .space, .delete, .up, .down, .left, .right
|
|
2432
2432
|
// ===========================================================================
|
|
2433
2433
|
|
|
2434
|
-
describe('component
|
|
2434
|
+
describe('component - key modifier .enter', () => {
|
|
2435
2435
|
it('fires handler only on Enter key', () => {
|
|
2436
2436
|
let count = 0;
|
|
2437
2437
|
component('key-enter', {
|
|
@@ -2452,7 +2452,7 @@ describe('component — key modifier .enter', () => {
|
|
|
2452
2452
|
});
|
|
2453
2453
|
});
|
|
2454
2454
|
|
|
2455
|
-
describe('component
|
|
2455
|
+
describe('component - key modifier .escape', () => {
|
|
2456
2456
|
it('fires handler only on Escape key', () => {
|
|
2457
2457
|
let count = 0;
|
|
2458
2458
|
component('key-esc', {
|
|
@@ -2471,7 +2471,7 @@ describe('component — key modifier .escape', () => {
|
|
|
2471
2471
|
});
|
|
2472
2472
|
});
|
|
2473
2473
|
|
|
2474
|
-
describe('component
|
|
2474
|
+
describe('component - key modifier .tab', () => {
|
|
2475
2475
|
it('fires handler only on Tab key', () => {
|
|
2476
2476
|
let count = 0;
|
|
2477
2477
|
component('key-tab', {
|
|
@@ -2490,7 +2490,7 @@ describe('component — key modifier .tab', () => {
|
|
|
2490
2490
|
});
|
|
2491
2491
|
});
|
|
2492
2492
|
|
|
2493
|
-
describe('component
|
|
2493
|
+
describe('component - key modifier .space', () => {
|
|
2494
2494
|
it('fires handler only on Space key', () => {
|
|
2495
2495
|
let count = 0;
|
|
2496
2496
|
component('key-space', {
|
|
@@ -2509,7 +2509,7 @@ describe('component — key modifier .space', () => {
|
|
|
2509
2509
|
});
|
|
2510
2510
|
});
|
|
2511
2511
|
|
|
2512
|
-
describe('component
|
|
2512
|
+
describe('component - key modifier .delete', () => {
|
|
2513
2513
|
it('fires handler on Delete and Backspace keys', () => {
|
|
2514
2514
|
let count = 0;
|
|
2515
2515
|
component('key-del', {
|
|
@@ -2531,7 +2531,7 @@ describe('component — key modifier .delete', () => {
|
|
|
2531
2531
|
});
|
|
2532
2532
|
});
|
|
2533
2533
|
|
|
2534
|
-
describe('component
|
|
2534
|
+
describe('component - key modifier arrow keys', () => {
|
|
2535
2535
|
it('.up fires only on ArrowUp', () => {
|
|
2536
2536
|
let count = 0;
|
|
2537
2537
|
component('key-up', {
|
|
@@ -2593,7 +2593,7 @@ describe('component — key modifier arrow keys', () => {
|
|
|
2593
2593
|
});
|
|
2594
2594
|
});
|
|
2595
2595
|
|
|
2596
|
-
describe('component
|
|
2596
|
+
describe('component - key modifiers combined with other modifiers', () => {
|
|
2597
2597
|
it('.enter.prevent prevents default only on Enter', () => {
|
|
2598
2598
|
component('key-enter-prev', {
|
|
2599
2599
|
handler() {},
|
|
@@ -2618,10 +2618,10 @@ describe('component — key modifiers combined with other modifiers', () => {
|
|
|
2618
2618
|
|
|
2619
2619
|
|
|
2620
2620
|
// ===========================================================================
|
|
2621
|
-
// System key modifiers
|
|
2621
|
+
// System key modifiers - .ctrl, .shift, .alt, .meta
|
|
2622
2622
|
// ===========================================================================
|
|
2623
2623
|
|
|
2624
|
-
describe('component
|
|
2624
|
+
describe('component - system key modifier .ctrl', () => {
|
|
2625
2625
|
it('fires only when Ctrl is held', () => {
|
|
2626
2626
|
let count = 0;
|
|
2627
2627
|
component('sys-ctrl', {
|
|
@@ -2632,17 +2632,17 @@ describe('component — system key modifier .ctrl', () => {
|
|
|
2632
2632
|
mount('#sc', 'sys-ctrl');
|
|
2633
2633
|
const input = document.querySelector('#sc input');
|
|
2634
2634
|
|
|
2635
|
-
// Without Ctrl
|
|
2635
|
+
// Without Ctrl - should NOT fire
|
|
2636
2636
|
input.dispatchEvent(new KeyboardEvent('keydown', { key: 'a', bubbles: true, ctrlKey: false }));
|
|
2637
2637
|
expect(count).toBe(0);
|
|
2638
2638
|
|
|
2639
|
-
// With Ctrl
|
|
2639
|
+
// With Ctrl - should fire
|
|
2640
2640
|
input.dispatchEvent(new KeyboardEvent('keydown', { key: 'a', bubbles: true, ctrlKey: true }));
|
|
2641
2641
|
expect(count).toBe(1);
|
|
2642
2642
|
});
|
|
2643
2643
|
});
|
|
2644
2644
|
|
|
2645
|
-
describe('component
|
|
2645
|
+
describe('component - system key modifier .shift', () => {
|
|
2646
2646
|
it('fires only when Shift is held', () => {
|
|
2647
2647
|
let count = 0;
|
|
2648
2648
|
component('sys-shift', {
|
|
@@ -2661,7 +2661,7 @@ describe('component — system key modifier .shift', () => {
|
|
|
2661
2661
|
});
|
|
2662
2662
|
});
|
|
2663
2663
|
|
|
2664
|
-
describe('component
|
|
2664
|
+
describe('component - system key modifier .alt', () => {
|
|
2665
2665
|
it('fires only when Alt is held', () => {
|
|
2666
2666
|
let count = 0;
|
|
2667
2667
|
component('sys-alt', {
|
|
@@ -2680,7 +2680,7 @@ describe('component — system key modifier .alt', () => {
|
|
|
2680
2680
|
});
|
|
2681
2681
|
});
|
|
2682
2682
|
|
|
2683
|
-
describe('component
|
|
2683
|
+
describe('component - system key modifier .meta', () => {
|
|
2684
2684
|
it('fires only when Meta (Cmd/Win) is held', () => {
|
|
2685
2685
|
let count = 0;
|
|
2686
2686
|
component('sys-meta', {
|
|
@@ -2699,7 +2699,7 @@ describe('component — system key modifier .meta', () => {
|
|
|
2699
2699
|
});
|
|
2700
2700
|
});
|
|
2701
2701
|
|
|
2702
|
-
describe('component
|
|
2702
|
+
describe('component - combined key + system modifiers', () => {
|
|
2703
2703
|
it('.ctrl.enter fires only on Ctrl+Enter', () => {
|
|
2704
2704
|
let count = 0;
|
|
2705
2705
|
component('sys-ctrl-enter', {
|
|
@@ -2743,10 +2743,10 @@ describe('component — combined key + system modifiers', () => {
|
|
|
2743
2743
|
|
|
2744
2744
|
|
|
2745
2745
|
// ===========================================================================
|
|
2746
|
-
// .outside modifier
|
|
2746
|
+
// .outside modifier - fire when event target is outside the element
|
|
2747
2747
|
// ===========================================================================
|
|
2748
2748
|
|
|
2749
|
-
describe('component
|
|
2749
|
+
describe('component - .outside event modifier', () => {
|
|
2750
2750
|
it('fires handler when clicking outside the element', () => {
|
|
2751
2751
|
let count = 0;
|
|
2752
2752
|
component('evt-outside', {
|
|
@@ -2756,15 +2756,15 @@ describe('component — .outside event modifier', () => {
|
|
|
2756
2756
|
document.body.innerHTML = '<evt-outside id="eo"></evt-outside>';
|
|
2757
2757
|
mount('#eo', 'evt-outside');
|
|
2758
2758
|
|
|
2759
|
-
// Click inside the dropdown
|
|
2759
|
+
// Click inside the dropdown - should NOT fire
|
|
2760
2760
|
document.querySelector('#eo .dropdown').click();
|
|
2761
2761
|
expect(count).toBe(0);
|
|
2762
2762
|
|
|
2763
|
-
// Click inside a child
|
|
2763
|
+
// Click inside a child - should NOT fire
|
|
2764
2764
|
document.querySelector('#eo span').click();
|
|
2765
2765
|
expect(count).toBe(0);
|
|
2766
2766
|
|
|
2767
|
-
// Click on the component root (outside the dropdown)
|
|
2767
|
+
// Click on the component root (outside the dropdown) - should fire
|
|
2768
2768
|
document.querySelector('#eo').dispatchEvent(new Event('click', { bubbles: true }));
|
|
2769
2769
|
expect(count).toBe(1);
|
|
2770
2770
|
});
|
|
@@ -2798,10 +2798,10 @@ describe('component — .outside event modifier', () => {
|
|
|
2798
2798
|
expect(count).toBe(0);
|
|
2799
2799
|
});
|
|
2800
2800
|
});
|
|
2801
|
-
// z-for advanced
|
|
2801
|
+
// z-for advanced - object, iterable, range, null
|
|
2802
2802
|
// ===========================================================================
|
|
2803
2803
|
|
|
2804
|
-
describe('component
|
|
2804
|
+
describe('component - z-for object iteration', () => {
|
|
2805
2805
|
it('iterates over object entries with key/value', () => {
|
|
2806
2806
|
component('zfor-obj', {
|
|
2807
2807
|
state: () => ({ data: { name: 'Alice', age: 30 } }),
|
|
@@ -2818,7 +2818,7 @@ describe('component — z-for object iteration', () => {
|
|
|
2818
2818
|
});
|
|
2819
2819
|
});
|
|
2820
2820
|
|
|
2821
|
-
describe('component
|
|
2821
|
+
describe('component - z-for numeric range', () => {
|
|
2822
2822
|
it('generates items from 1 to n', () => {
|
|
2823
2823
|
component('zfor-range', {
|
|
2824
2824
|
state: () => ({ count: 5 }),
|
|
@@ -2833,7 +2833,7 @@ describe('component — z-for numeric range', () => {
|
|
|
2833
2833
|
});
|
|
2834
2834
|
});
|
|
2835
2835
|
|
|
2836
|
-
describe('component
|
|
2836
|
+
describe('component - z-for null/undefined list removes element', () => {
|
|
2837
2837
|
it('removes element when list is null', () => {
|
|
2838
2838
|
component('zfor-null', {
|
|
2839
2839
|
state: () => ({ items: null }),
|
|
@@ -2847,7 +2847,7 @@ describe('component — z-for null/undefined list removes element', () => {
|
|
|
2847
2847
|
});
|
|
2848
2848
|
});
|
|
2849
2849
|
|
|
2850
|
-
describe('component
|
|
2850
|
+
describe('component - z-for with empty array', () => {
|
|
2851
2851
|
it('renders nothing with an empty array', () => {
|
|
2852
2852
|
component('zfor-empty', {
|
|
2853
2853
|
state: () => ({ items: [] }),
|
|
@@ -2859,7 +2859,7 @@ describe('component — z-for with empty array', () => {
|
|
|
2859
2859
|
});
|
|
2860
2860
|
});
|
|
2861
2861
|
|
|
2862
|
-
describe('component
|
|
2862
|
+
describe('component - z-for with $index', () => {
|
|
2863
2863
|
it('exposes $index as default index variable', () => {
|
|
2864
2864
|
component('zfor-idx', {
|
|
2865
2865
|
state: () => ({ items: ['a', 'b', 'c'] }),
|
|
@@ -2874,7 +2874,7 @@ describe('component — z-for with $index', () => {
|
|
|
2874
2874
|
});
|
|
2875
2875
|
});
|
|
2876
2876
|
|
|
2877
|
-
describe('component
|
|
2877
|
+
describe('component - z-for with invalid expression', () => {
|
|
2878
2878
|
it('gracefully handles invalid z-for syntax', () => {
|
|
2879
2879
|
component('zfor-invalid', {
|
|
2880
2880
|
state: () => ({}),
|
|
@@ -2887,10 +2887,10 @@ describe('component — z-for with invalid expression', () => {
|
|
|
2887
2887
|
|
|
2888
2888
|
|
|
2889
2889
|
// ===========================================================================
|
|
2890
|
-
// z-class
|
|
2890
|
+
// z-class - string and array forms
|
|
2891
2891
|
// ===========================================================================
|
|
2892
2892
|
|
|
2893
|
-
describe('component
|
|
2893
|
+
describe('component - z-class string form', () => {
|
|
2894
2894
|
it('adds classes from a space-separated string', () => {
|
|
2895
2895
|
component('zclass-str', {
|
|
2896
2896
|
state: () => ({ cls: 'foo bar baz' }),
|
|
@@ -2905,7 +2905,7 @@ describe('component — z-class string form', () => {
|
|
|
2905
2905
|
});
|
|
2906
2906
|
});
|
|
2907
2907
|
|
|
2908
|
-
describe('component
|
|
2908
|
+
describe('component - z-class array form', () => {
|
|
2909
2909
|
it('adds classes from an array, filtering falsy values', () => {
|
|
2910
2910
|
component('zclass-arr', {
|
|
2911
2911
|
state: () => ({ classes: ['active', null, 'visible', '', 'large'] }),
|
|
@@ -2923,10 +2923,10 @@ describe('component — z-class array form', () => {
|
|
|
2923
2923
|
|
|
2924
2924
|
|
|
2925
2925
|
// ===========================================================================
|
|
2926
|
-
// z-style
|
|
2926
|
+
// z-style - string form
|
|
2927
2927
|
// ===========================================================================
|
|
2928
2928
|
|
|
2929
|
-
describe('component
|
|
2929
|
+
describe('component - z-style string form', () => {
|
|
2930
2930
|
it('appends CSS text from a string expression', () => {
|
|
2931
2931
|
component('zstyle-str', {
|
|
2932
2932
|
state: () => ({ s: 'color: red; font-weight: bold' }),
|
|
@@ -2945,7 +2945,7 @@ describe('component — z-style string form', () => {
|
|
|
2945
2945
|
// z-text and z-html edge cases
|
|
2946
2946
|
// ===========================================================================
|
|
2947
2947
|
|
|
2948
|
-
describe('component
|
|
2948
|
+
describe('component - z-text with null/undefined', () => {
|
|
2949
2949
|
it('sets empty textContent for null state', () => {
|
|
2950
2950
|
component('ztext-null', {
|
|
2951
2951
|
state: () => ({ val: null }),
|
|
@@ -2957,7 +2957,7 @@ describe('component — z-text with null/undefined', () => {
|
|
|
2957
2957
|
});
|
|
2958
2958
|
});
|
|
2959
2959
|
|
|
2960
|
-
describe('component
|
|
2960
|
+
describe('component - z-html with null', () => {
|
|
2961
2961
|
it('sets empty innerHTML for null state', () => {
|
|
2962
2962
|
component('zhtml-null', {
|
|
2963
2963
|
state: () => ({ content: null }),
|
|
@@ -2969,7 +2969,7 @@ describe('component — z-html with null', () => {
|
|
|
2969
2969
|
});
|
|
2970
2970
|
});
|
|
2971
2971
|
|
|
2972
|
-
describe('component
|
|
2972
|
+
describe('component - z-text expression evaluation', () => {
|
|
2973
2973
|
it('evaluates expressions, not just state keys', () => {
|
|
2974
2974
|
component('ztext-expr', {
|
|
2975
2975
|
state: () => ({ a: 3, b: 4 }),
|
|
@@ -2986,7 +2986,7 @@ describe('component — z-text expression evaluation', () => {
|
|
|
2986
2986
|
// mountAll with props and dynamic expressions
|
|
2987
2987
|
// ===========================================================================
|
|
2988
2988
|
|
|
2989
|
-
describe('mountAll
|
|
2989
|
+
describe('mountAll - static props', () => {
|
|
2990
2990
|
it('passes attribute values as props to components', () => {
|
|
2991
2991
|
component('prop-child', {
|
|
2992
2992
|
render() { return `<span>${this.props.label}</span>`; },
|
|
@@ -3007,7 +3007,7 @@ describe('mountAll — static props', () => {
|
|
|
3007
3007
|
});
|
|
3008
3008
|
});
|
|
3009
3009
|
|
|
3010
|
-
describe('mountAll
|
|
3010
|
+
describe('mountAll - skips already-mounted instances', () => {
|
|
3011
3011
|
it('does not double-mount the same element', () => {
|
|
3012
3012
|
let mountCount = 0;
|
|
3013
3013
|
component('mount-once', {
|
|
@@ -3024,10 +3024,10 @@ describe('mountAll — skips already-mounted instances', () => {
|
|
|
3024
3024
|
|
|
3025
3025
|
|
|
3026
3026
|
// ===========================================================================
|
|
3027
|
-
// Component
|
|
3027
|
+
// Component - scoped styles edge cases
|
|
3028
3028
|
// ===========================================================================
|
|
3029
3029
|
|
|
3030
|
-
describe('component
|
|
3030
|
+
describe('component - scoped styles injection', () => {
|
|
3031
3031
|
it('creates a <style> tag with scoped CSS', () => {
|
|
3032
3032
|
component('scoped-css', {
|
|
3033
3033
|
styles: 'p { color: blue; }',
|
|
@@ -3044,10 +3044,10 @@ describe('component — scoped styles injection', () => {
|
|
|
3044
3044
|
|
|
3045
3045
|
|
|
3046
3046
|
// ===========================================================================
|
|
3047
|
-
// Component
|
|
3047
|
+
// Component - event handler with state.* arg passthrough
|
|
3048
3048
|
// ===========================================================================
|
|
3049
3049
|
|
|
3050
|
-
describe('component
|
|
3050
|
+
describe('component - event handler receives state.* arguments', () => {
|
|
3051
3051
|
it('passes state values via state.propName in event args', () => {
|
|
3052
3052
|
let received = null;
|
|
3053
3053
|
component('evt-statearg', {
|
|
@@ -3062,7 +3062,7 @@ describe('component — event handler receives state.* arguments', () => {
|
|
|
3062
3062
|
});
|
|
3063
3063
|
});
|
|
3064
3064
|
|
|
3065
|
-
describe('component
|
|
3065
|
+
describe('component - event handler with boolean/null args', () => {
|
|
3066
3066
|
it('parses true, false, null literals in event arguments', () => {
|
|
3067
3067
|
let args = [];
|
|
3068
3068
|
component('evt-literals', {
|
|
@@ -3078,10 +3078,10 @@ describe('component — event handler with boolean/null args', () => {
|
|
|
3078
3078
|
|
|
3079
3079
|
|
|
3080
3080
|
// ===========================================================================
|
|
3081
|
-
// Component
|
|
3081
|
+
// Component - z-bind edge cases
|
|
3082
3082
|
// ===========================================================================
|
|
3083
3083
|
|
|
3084
|
-
describe('component
|
|
3084
|
+
describe('component - z-bind with false removes boolean attr', () => {
|
|
3085
3085
|
it('removes disabled attribute when value is false', () => {
|
|
3086
3086
|
component('zbind-false', {
|
|
3087
3087
|
state: () => ({ isDisabled: false }),
|
|
@@ -3105,10 +3105,10 @@ describe('component — z-bind with false removes boolean attr', () => {
|
|
|
3105
3105
|
|
|
3106
3106
|
|
|
3107
3107
|
// ===========================================================================
|
|
3108
|
-
// Component
|
|
3108
|
+
// Component - z-if / z-show with computed
|
|
3109
3109
|
// ===========================================================================
|
|
3110
3110
|
|
|
3111
|
-
describe('component
|
|
3111
|
+
describe('component - z-if with computed expression', () => {
|
|
3112
3112
|
it('evaluates computed values in z-if', () => {
|
|
3113
3113
|
component('zif-computed', {
|
|
3114
3114
|
state: () => ({ items: [1, 2, 3] }),
|
|
@@ -3124,7 +3124,7 @@ describe('component — z-if with computed expression', () => {
|
|
|
3124
3124
|
});
|
|
3125
3125
|
});
|
|
3126
3126
|
|
|
3127
|
-
describe('component
|
|
3127
|
+
describe('component - z-show with computed expression', () => {
|
|
3128
3128
|
it('toggles visibility based on computed', () => {
|
|
3129
3129
|
component('zshow-computed', {
|
|
3130
3130
|
state: () => ({ count: 0 }),
|
|
@@ -3142,10 +3142,10 @@ describe('component — z-show with computed expression', () => {
|
|
|
3142
3142
|
|
|
3143
3143
|
|
|
3144
3144
|
// ===========================================================================
|
|
3145
|
-
// Component
|
|
3145
|
+
// Component - interpolation edge cases
|
|
3146
3146
|
// ===========================================================================
|
|
3147
3147
|
|
|
3148
|
-
describe('component
|
|
3148
|
+
describe('component - interpolation edge cases', () => {
|
|
3149
3149
|
it('handles nested object access via template literal', () => {
|
|
3150
3150
|
component('interp-deep', {
|
|
3151
3151
|
state: () => ({ user: { profile: { name: 'Zoe' } } }),
|
|
@@ -3179,10 +3179,10 @@ describe('component — interpolation edge cases', () => {
|
|
|
3179
3179
|
|
|
3180
3180
|
|
|
3181
3181
|
// ===========================================================================
|
|
3182
|
-
// Component
|
|
3182
|
+
// Component - lifecycle edge cases
|
|
3183
3183
|
// ===========================================================================
|
|
3184
3184
|
|
|
3185
|
-
describe('component
|
|
3185
|
+
describe('component - destroy removes from instance map', () => {
|
|
3186
3186
|
it('getInstance returns undefined after destroy', () => {
|
|
3187
3187
|
component('destroy-map', {
|
|
3188
3188
|
state: () => ({ x: 0 }),
|
|
@@ -3198,7 +3198,7 @@ describe('component — destroy removes from instance map', () => {
|
|
|
3198
3198
|
});
|
|
3199
3199
|
});
|
|
3200
3200
|
|
|
3201
|
-
describe('component
|
|
3201
|
+
describe('component - multiple instances of same component', () => {
|
|
3202
3202
|
it('each has independent state', () => {
|
|
3203
3203
|
component('multi-inst', {
|
|
3204
3204
|
state: () => ({ val: 0 }),
|
|
@@ -3214,10 +3214,10 @@ describe('component — multiple instances of same component', () => {
|
|
|
3214
3214
|
|
|
3215
3215
|
|
|
3216
3216
|
// ===========================================================================
|
|
3217
|
-
// Custom HTML tag
|
|
3217
|
+
// Custom HTML tag - "drop it anywhere" behavior
|
|
3218
3218
|
// ===========================================================================
|
|
3219
3219
|
|
|
3220
|
-
describe('custom HTML tag
|
|
3220
|
+
describe('custom HTML tag - drop-in component mounting', () => {
|
|
3221
3221
|
|
|
3222
3222
|
// -- Basic rendering -------------------------------------------------------
|
|
3223
3223
|
|
|
@@ -3429,7 +3429,7 @@ describe('custom HTML tag — drop-in component mounting', () => {
|
|
|
3429
3429
|
},
|
|
3430
3430
|
});
|
|
3431
3431
|
|
|
3432
|
-
// Drop it anywhere
|
|
3432
|
+
// Drop it anywhere - just like the docs say
|
|
3433
3433
|
document.body.innerHTML = `
|
|
3434
3434
|
<h1>My Page</h1>
|
|
3435
3435
|
<click-counter></click-counter>
|