microui-wc 0.1.0 → 0.1.2
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/AGENTS.md +71 -71
- package/CHANGELOG.md +1 -1
- package/README.md +14 -9
- package/dist/AGENTS.md +116 -116
- package/dist/README.md +21 -16
- package/dist/components.css +1 -1
- package/dist/microui.css +1 -1
- package/dist/microui.esm.js.map +1 -1
- package/dist/microui.min.js.map +1 -1
- package/dist/styles/components/switch.css +1 -1
- package/docs/getting-started.md +3 -3
- package/package.json +38 -10
- package/src/components/mu-schema-form.js +1 -1
- package/src/styles/components/switch.css +7 -8
- package/src/styles/components.css +6 -6
- package/.github/ISSUE_TEMPLATE/bug_report.md +0 -40
- package/.github/ISSUE_TEMPLATE/feature_request.md +0 -33
- package/.github/PULL_REQUEST_TEMPLATE.md +0 -28
- package/.github/workflows/ci.yml +0 -42
- package/.github/workflows/deploy-pages.yml +0 -112
- package/CODE_OF_CONDUCT.md +0 -59
- package/CONTRIBUTING.md +0 -156
- package/SECURITY.md +0 -58
- package/app/.generated/routes/alerts.js +0 -8
- package/app/.generated/routes/avatars.js +0 -8
- package/app/.generated/routes/badges.js +0 -8
- package/app/.generated/routes/buttons.js +0 -10
- package/app/.generated/routes/cards.js +0 -10
- package/app/.generated/routes/checkboxes.js +0 -9
- package/app/.generated/routes/chips.js +0 -8
- package/app/.generated/routes/dropdowns.js +0 -9
- package/app/.generated/routes/home.js +0 -7
- package/app/.generated/routes/icons.js +0 -9
- package/app/.generated/routes/inputs.js +0 -10
- package/app/.generated/routes/installation.js +0 -7
- package/app/.generated/routes/layout.js +0 -9
- package/app/.generated/routes/modals.js +0 -9
- package/app/.generated/routes/navbar.js +0 -7
- package/app/.generated/routes/progress.js +0 -9
- package/app/.generated/routes/radios.js +0 -9
- package/app/.generated/routes/switches.js +0 -9
- package/app/.generated/routes/tabs.js +0 -8
- package/app/.generated/routes/toasts.js +0 -9
- package/app/index.html +0 -67
- package/app/pages/alerts.html +0 -23
- package/app/pages/avatars.html +0 -22
- package/app/pages/badges.html +0 -22
- package/app/pages/buttons.html +0 -71
- package/app/pages/cards.html +0 -54
- package/app/pages/checkboxes.html +0 -39
- package/app/pages/chips.html +0 -23
- package/app/pages/dropdowns.html +0 -41
- package/app/pages/home.html +0 -59
- package/app/pages/icons.html +0 -29
- package/app/pages/inputs.html +0 -66
- package/app/pages/installation.html +0 -34
- package/app/pages/layout.html +0 -30
- package/app/pages/modals.html +0 -21
- package/app/pages/navbar.html +0 -22
- package/app/pages/progress.html +0 -35
- package/app/pages/radios.html +0 -40
- package/app/pages/switches.html +0 -39
- package/app/pages/tabs.html +0 -30
- package/app/pages/toasts.html +0 -22
- package/app-dist/index.html +0 -67
- package/app-dist/pages/alerts.html +0 -23
- package/app-dist/pages/avatars.html +0 -22
- package/app-dist/pages/badges.html +0 -22
- package/app-dist/pages/buttons.html +0 -71
- package/app-dist/pages/cards.html +0 -54
- package/app-dist/pages/checkboxes.html +0 -39
- package/app-dist/pages/chips.html +0 -23
- package/app-dist/pages/dropdowns.html +0 -41
- package/app-dist/pages/home.html +0 -59
- package/app-dist/pages/icons.html +0 -29
- package/app-dist/pages/inputs.html +0 -66
- package/app-dist/pages/installation.html +0 -34
- package/app-dist/pages/layout.html +0 -30
- package/app-dist/pages/modals.html +0 -21
- package/app-dist/pages/navbar.html +0 -22
- package/app-dist/pages/progress.html +0 -35
- package/app-dist/pages/radios.html +0 -40
- package/app-dist/pages/switches.html +0 -39
- package/app-dist/pages/tabs.html +0 -30
- package/app-dist/pages/toasts.html +0 -22
- package/app-dist/pages.json +0 -217
- package/app-dist/routes/alerts.js +0 -5
- package/app-dist/routes/avatars.js +0 -1
- package/app-dist/routes/badges.js +0 -1
- package/app-dist/routes/buttons.js +0 -1
- package/app-dist/routes/cards.js +0 -1
- package/app-dist/routes/checkboxes.js +0 -9
- package/app-dist/routes/chips.js +0 -4
- package/app-dist/routes/chunk-019e5e2f.js +0 -5
- package/app-dist/routes/chunk-0m4j19yd.js +0 -2
- package/app-dist/routes/chunk-0tmmp5q0.js +0 -1
- package/app-dist/routes/chunk-10xn709r.js +0 -1
- package/app-dist/routes/chunk-15m2qcda.js +0 -2
- package/app-dist/routes/chunk-1bh8g23n.js +0 -1
- package/app-dist/routes/chunk-1vg0v937.js +0 -1
- package/app-dist/routes/chunk-1zvcgy3j.js +0 -1
- package/app-dist/routes/chunk-2afb0861.js +0 -1
- package/app-dist/routes/chunk-2c6ttpzt.js +0 -5
- package/app-dist/routes/chunk-3dy30fhs.js +0 -1
- package/app-dist/routes/chunk-426dnces.js +0 -13
- package/app-dist/routes/chunk-44kgxery.js +0 -1
- package/app-dist/routes/chunk-47fdnejd.js +0 -33
- package/app-dist/routes/chunk-49a6t2vq.js +0 -1
- package/app-dist/routes/chunk-4fe1rm5b.js +0 -1
- package/app-dist/routes/chunk-4ggmvkta.js +0 -33
- package/app-dist/routes/chunk-4vkz81q7.js +0 -33
- package/app-dist/routes/chunk-4w4tmj8f.js +0 -31
- package/app-dist/routes/chunk-532s62kr.js +0 -31
- package/app-dist/routes/chunk-5hm3bssy.js +0 -33
- package/app-dist/routes/chunk-5vrh24hc.js +0 -1
- package/app-dist/routes/chunk-61pcg25a.js +0 -1
- package/app-dist/routes/chunk-6nfhygvf.js +0 -1
- package/app-dist/routes/chunk-700e7je6.js +0 -33
- package/app-dist/routes/chunk-7fsn17kg.js +0 -1
- package/app-dist/routes/chunk-7k789b32.js +0 -1
- package/app-dist/routes/chunk-7r46q0ys.js +0 -36
- package/app-dist/routes/chunk-86fmc1fr.js +0 -5
- package/app-dist/routes/chunk-8qth37vw.js +0 -1
- package/app-dist/routes/chunk-924wv8n0.js +0 -1
- package/app-dist/routes/chunk-9mbhgxk9.js +0 -1
- package/app-dist/routes/chunk-a216hyd9.js +0 -1
- package/app-dist/routes/chunk-akzxykh9.js +0 -33
- package/app-dist/routes/chunk-b3dcvy8c.js +0 -1
- package/app-dist/routes/chunk-b74zahz5.js +0 -31
- package/app-dist/routes/chunk-bftj53p2.js +0 -5
- package/app-dist/routes/chunk-c01hnz3e.js +0 -1
- package/app-dist/routes/chunk-d8pvv5km.js +0 -1
- package/app-dist/routes/chunk-dev0aezr.js +0 -2
- package/app-dist/routes/chunk-dh6vnv0e.js +0 -1
- package/app-dist/routes/chunk-dn2cbpva.js +0 -36
- package/app-dist/routes/chunk-dvn0my90.js +0 -1
- package/app-dist/routes/chunk-dvq8mnve.js +0 -36
- package/app-dist/routes/chunk-e8c2gc4d.js +0 -5
- package/app-dist/routes/chunk-ejf9ak2x.js +0 -1
- package/app-dist/routes/chunk-f083m55s.js +0 -1
- package/app-dist/routes/chunk-fnrj28s1.js +0 -31
- package/app-dist/routes/chunk-fvg3yjdp.js +0 -31
- package/app-dist/routes/chunk-g7k381n1.js +0 -1
- package/app-dist/routes/chunk-h01kq2ae.js +0 -13
- package/app-dist/routes/chunk-h4dk761v.js +0 -5
- package/app-dist/routes/chunk-hmx91z2x.js +0 -5
- package/app-dist/routes/chunk-hxbg4m42.js +0 -36
- package/app-dist/routes/chunk-jbjnfp2b.js +0 -2
- package/app-dist/routes/chunk-jxtz5vv6.js +0 -36
- package/app-dist/routes/chunk-jxzcs0ey.js +0 -36
- package/app-dist/routes/chunk-kt7wwhcx.js +0 -1
- package/app-dist/routes/chunk-kzptszyc.js +0 -33
- package/app-dist/routes/chunk-mhgca4w4.js +0 -2
- package/app-dist/routes/chunk-mhswxa20.js +0 -1
- package/app-dist/routes/chunk-n8zfeex6.js +0 -1
- package/app-dist/routes/chunk-pee47b2r.js +0 -1
- package/app-dist/routes/chunk-pesmw829.js +0 -1
- package/app-dist/routes/chunk-pgc4c6f3.js +0 -36
- package/app-dist/routes/chunk-q8egegm1.js +0 -1
- package/app-dist/routes/chunk-q9mn2qyq.js +0 -36
- package/app-dist/routes/chunk-qh0rtaf3.js +0 -5
- package/app-dist/routes/chunk-qqhmk6ye.js +0 -2
- package/app-dist/routes/chunk-qrxygmf7.js +0 -33
- package/app-dist/routes/chunk-r46yzksx.js +0 -36
- package/app-dist/routes/chunk-rgpbw2w0.js +0 -5
- package/app-dist/routes/chunk-rnpzv3d8.js +0 -2
- package/app-dist/routes/chunk-s5v8cv05.js +0 -2
- package/app-dist/routes/chunk-sbwn5bpc.js +0 -1
- package/app-dist/routes/chunk-sqbg8jbt.js +0 -33
- package/app-dist/routes/chunk-sv8dqnf7.js +0 -1
- package/app-dist/routes/chunk-t67sw3za.js +0 -1
- package/app-dist/routes/chunk-tjdpqwdf.js +0 -31
- package/app-dist/routes/chunk-tq2mfghg.js +0 -1
- package/app-dist/routes/chunk-ttn10vt6.js +0 -1
- package/app-dist/routes/chunk-v2hzpjxr.js +0 -1
- package/app-dist/routes/chunk-wfjjkw9y.js +0 -1
- package/app-dist/routes/chunk-wt8cxzmf.js +0 -31
- package/app-dist/routes/chunk-x45d372k.js +0 -5
- package/app-dist/routes/chunk-y3wsazkt.js +0 -1
- package/app-dist/routes/chunk-y7pmgc7t.js +0 -33
- package/app-dist/routes/chunk-zefdt2q3.js +0 -31
- package/app-dist/routes/dropdowns.js +0 -6
- package/app-dist/routes/home.js +0 -1
- package/app-dist/routes/icons.js +0 -1
- package/app-dist/routes/inputs.js +0 -12
- package/app-dist/routes/installation.js +0 -1
- package/app-dist/routes/layout.js +0 -1
- package/app-dist/routes/modals.js +0 -7
- package/app-dist/routes/navbar.js +0 -1
- package/app-dist/routes/progress.js +0 -1
- package/app-dist/routes/radios.js +0 -6
- package/app-dist/routes/switches.js +0 -6
- package/app-dist/routes/tabs.js +0 -1
- package/app-dist/routes/toasts.js +0 -16
- package/assets/fonts/material-symbols-mini.woff2 +0 -0
- package/assets/fonts/material-symbols.woff2 +0 -0
- package/assets/fonts/roboto-400.woff2 +0 -0
- package/assets/fonts/roboto-500.woff2 +0 -0
- package/assets/fonts/roboto-700.woff2 +0 -0
- package/assets/logo-banner-400.jpg +0 -0
- package/assets/logo-banner-400.webp +0 -0
- package/assets/logo-banner-800.webp +0 -0
- package/assets/logo-banner.jpg +0 -0
- package/assets/logo-icon-64.jpg +0 -0
- package/assets/logo-icon-64.webp +0 -0
- package/assets/logo-icon.jpg +0 -0
- package/assets/logo-square.jpg +0 -0
- package/bun.lock +0 -312
- package/bunfig.toml +0 -4
- package/custom-elements.json +0 -1916
- package/demo/api/sample-data.json +0 -38
- package/demo/content/alerts.html +0 -115
- package/demo/content/avatars.html +0 -70
- package/demo/content/badges.html +0 -65
- package/demo/content/buttons.html +0 -188
- package/demo/content/callouts.html +0 -91
- package/demo/content/cards.html +0 -121
- package/demo/content/checkboxes.html +0 -178
- package/demo/content/chips.html +0 -67
- package/demo/content/codeblocks.html +0 -101
- package/demo/content/confirms.html +0 -115
- package/demo/content/datatables.html +0 -149
- package/demo/content/dividers.html +0 -119
- package/demo/content/dropdowns.html +0 -89
- package/demo/content/enterprise.html +0 -252
- package/demo/content/home.html +0 -149
- package/demo/content/icons.html +0 -89
- package/demo/content/inputs.html +0 -135
- package/demo/content/installation.html +0 -16
- package/demo/content/layout.html +0 -136
- package/demo/content/modals.html +0 -141
- package/demo/content/navbar.html +0 -70
- package/demo/content/progress.html +0 -119
- package/demo/content/radios.html +0 -88
- package/demo/content/skeletons.html +0 -109
- package/demo/content/spinners.html +0 -96
- package/demo/content/switches.html +0 -84
- package/demo/content/tables.html +0 -124
- package/demo/content/tabs.html +0 -85
- package/demo/content/toasts.html +0 -116
- package/demo/content/tooltips.html +0 -107
- package/demo/content/virtual-lists.html +0 -233
- package/demo/favicon.ico +0 -0
- package/demo/favicon.png +0 -0
- package/demo/full.html +0 -52
- package/demo/iife.html +0 -46
- package/demo/manifest.json +0 -34
- package/demo/pages/datatable-demo.html +0 -237
- package/demo/pages/prompt-ui-demo.html +0 -218
- package/demo/pages/responsive-demo.html +0 -122
- package/demo/pages/schema-form-demo.html +0 -270
- package/demo/robots.txt +0 -6
- package/demo/shell.html +0 -712
- package/demo/sw.js +0 -387
- package/lighthouse-audit.mjs +0 -113
- package/scripts/analyze-components.js +0 -105
- package/scripts/build-app.js +0 -193
- package/scripts/build-framework.js +0 -444
- package/scripts/build-utils.js +0 -101
- package/scripts/test-isolated.js +0 -151
- package/server.js +0 -256
- package/tests/agents/agent-integration.test.js +0 -76
- package/tests/benchmark.html +0 -296
- package/tests/build/scan-components.test.js +0 -173
- package/tests/components/all-components.test.js +0 -245
- package/tests/components/all-missing-components.test.js +0 -574
- package/tests/components/mu-alert.test.js +0 -113
- package/tests/components/mu-avatar.test.js +0 -148
- package/tests/components/mu-badge.test.js +0 -92
- package/tests/components/mu-button.test.js +0 -112
- package/tests/components/mu-card.test.js +0 -89
- package/tests/components/mu-checkbox.test.js +0 -158
- package/tests/components/mu-chip.test.js +0 -118
- package/tests/components/mu-container.test.js +0 -120
- package/tests/components/mu-divider.test.js +0 -98
- package/tests/components/mu-drawer-item.test.js +0 -199
- package/tests/components/mu-drawer.test.js +0 -96
- package/tests/components/mu-dropdown.test.js +0 -125
- package/tests/components/mu-form.test.js +0 -138
- package/tests/components/mu-grid.test.js +0 -135
- package/tests/components/mu-icon.test.js +0 -110
- package/tests/components/mu-input.test.js +0 -131
- package/tests/components/mu-lazy.test.js +0 -103
- package/tests/components/mu-modal.test.js +0 -275
- package/tests/components/mu-navbar.test.js +0 -101
- package/tests/components/mu-progress.test.js +0 -115
- package/tests/components/mu-radio.test.js +0 -114
- package/tests/components/mu-repeat.test.js +0 -106
- package/tests/components/mu-sidebar.test.js +0 -126
- package/tests/components/mu-skeleton.test.js +0 -162
- package/tests/components/mu-stack.test.js +0 -143
- package/tests/components/mu-switch.test.js +0 -292
- package/tests/components/mu-table.test.js +0 -124
- package/tests/components/mu-tabs.test.js +0 -104
- package/tests/components/mu-textarea.test.js +0 -115
- package/tests/components/mu-toast.test.js +0 -321
- package/tests/components/mu-tooltip.test.js +0 -133
- package/tests/components/mu-virtual-list.test.js +0 -109
- package/tests/core/MuElement.test.js +0 -120
- package/tests/core/agent-api.test.js +0 -125
- package/tests/core/all-core-modules.test.js +0 -442
- package/tests/core/bus.test.js +0 -364
- package/tests/core/component-schema.test.js +0 -160
- package/tests/core/feature-registry.test.js +0 -198
- package/tests/core/form-state.test.js +0 -167
- package/tests/core/http.test.js +0 -119
- package/tests/core/keyboard.test.js +0 -319
- package/tests/core/layers.test.js +0 -129
- package/tests/core/namespaced-stores.test.js +0 -114
- package/tests/core/render.test.js +0 -121
- package/tests/core/ripple.test.js +0 -131
- package/tests/core/router.test.js +0 -89
- package/tests/core/scheduler.test.js +0 -121
- package/tests/core/signals.test.js +0 -128
- package/tests/core/store.test.js +0 -171
- package/tests/core/transitions.test.js +0 -82
- package/tests/e2e/accessibility-harness.html +0 -58
- package/tests/e2e/accessibility.test.js +0 -401
- package/tests/e2e/agent-features.test.js +0 -372
- package/tests/e2e/card-spacing.test.js +0 -287
- package/tests/e2e/components.test.js +0 -439
- package/tests/e2e/demo-routes.test.js +0 -478
- package/tests/e2e/layout-css-fallback.test.js +0 -334
- package/tests/e2e/mu-alert.e2e.test.js +0 -111
- package/tests/e2e/mu-checkbox.test.js +0 -489
- package/tests/e2e/mu-chip.test.js +0 -347
- package/tests/e2e/mu-form.test.js +0 -499
- package/tests/e2e/mu-icon.test.js +0 -114
- package/tests/e2e/mu-radio.test.js +0 -113
- package/tests/e2e/mu-skeleton.test.js +0 -140
- package/tests/e2e/mu-switch.test.js +0 -415
- package/tests/e2e/mu-tabs.test.js +0 -494
- package/tests/e2e/mu-textarea.test.js +0 -242
- package/tests/e2e/mu-virtual-list.test.js +0 -427
- package/tests/e2e/perf-memory.test.js +0 -161
- package/tests/e2e/puppeteer-helper.js +0 -137
- package/tests/e2e/puppeteer.test.js +0 -226
- package/tests/e2e/pwa.test.js +0 -261
- package/tests/e2e/test-harness.html +0 -319
- package/tests/manual/test-components.html +0 -120
- package/tests/memory-test.html +0 -309
- package/tests/setup-dom.js +0 -93
- package/tests/visual-test.html +0 -301
|
@@ -1,319 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @fileoverview Unit tests for core/keyboard.js
|
|
3
|
-
* Tests the centralized ESC Key Handler Stack
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import { describe, expect, it, beforeEach, afterEach, mock } from 'bun:test';
|
|
7
|
-
import { keyboard, KeyboardManager } from '../../src/core/keyboard.js';
|
|
8
|
-
|
|
9
|
-
// Polyfill KeyboardEvent for happy-dom
|
|
10
|
-
if (typeof globalThis.KeyboardEvent === 'undefined') {
|
|
11
|
-
globalThis.KeyboardEvent = class KeyboardEvent extends Event {
|
|
12
|
-
constructor(type, init = {}) {
|
|
13
|
-
super(type, init);
|
|
14
|
-
this.key = init.key || '';
|
|
15
|
-
this.code = init.code || '';
|
|
16
|
-
this.ctrlKey = init.ctrlKey || false;
|
|
17
|
-
this.shiftKey = init.shiftKey || false;
|
|
18
|
-
this.altKey = init.altKey || false;
|
|
19
|
-
this.metaKey = init.metaKey || false;
|
|
20
|
-
}
|
|
21
|
-
};
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
describe('KeyboardManager Singleton', () => {
|
|
25
|
-
it('should export keyboard singleton', () => {
|
|
26
|
-
expect(keyboard).toBeDefined();
|
|
27
|
-
expect(keyboard).toBeInstanceOf(KeyboardManager);
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
it('should export KeyboardManager class', () => {
|
|
31
|
-
expect(KeyboardManager).toBeDefined();
|
|
32
|
-
expect(typeof KeyboardManager).toBe('function');
|
|
33
|
-
});
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
describe('KeyboardManager.pushEscapeHandler', () => {
|
|
37
|
-
let unsubscribers = [];
|
|
38
|
-
|
|
39
|
-
afterEach(() => {
|
|
40
|
-
// Clean up all handlers after each test
|
|
41
|
-
unsubscribers.forEach(unsub => unsub?.());
|
|
42
|
-
unsubscribers = [];
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
it('should be a function', () => {
|
|
46
|
-
expect(typeof keyboard.pushEscapeHandler).toBe('function');
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
it('should return an unsubscribe function', () => {
|
|
50
|
-
const element = document.createElement('div');
|
|
51
|
-
const callback = mock(() => { });
|
|
52
|
-
|
|
53
|
-
const unsub = keyboard.pushEscapeHandler(element, callback);
|
|
54
|
-
unsubscribers.push(unsub);
|
|
55
|
-
|
|
56
|
-
expect(typeof unsub).toBe('function');
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
it('should increase stack depth when handler is added', () => {
|
|
60
|
-
const initialDepth = keyboard.stackDepth;
|
|
61
|
-
|
|
62
|
-
const element = document.createElement('div');
|
|
63
|
-
const callback = mock(() => { });
|
|
64
|
-
const unsub = keyboard.pushEscapeHandler(element, callback);
|
|
65
|
-
unsubscribers.push(unsub);
|
|
66
|
-
|
|
67
|
-
expect(keyboard.stackDepth).toBe(initialDepth + 1);
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
it('should decrease stack depth when unsubscribed', () => {
|
|
71
|
-
const element = document.createElement('div');
|
|
72
|
-
const callback = mock(() => { });
|
|
73
|
-
|
|
74
|
-
const unsub = keyboard.pushEscapeHandler(element, callback);
|
|
75
|
-
const depthAfterAdd = keyboard.stackDepth;
|
|
76
|
-
|
|
77
|
-
unsub();
|
|
78
|
-
|
|
79
|
-
expect(keyboard.stackDepth).toBe(depthAfterAdd - 1);
|
|
80
|
-
});
|
|
81
|
-
|
|
82
|
-
// NOTE: Event dispatching tests are skipped in unit tests due to linkedom limitations
|
|
83
|
-
// ESC key behavior is verified in E2E tests (tests/e2e/)
|
|
84
|
-
it.skip('should call topmost handler on ESC key (E2E only)', () => {
|
|
85
|
-
const element = document.createElement('div');
|
|
86
|
-
const callback = mock(() => { });
|
|
87
|
-
|
|
88
|
-
const unsub = keyboard.pushEscapeHandler(element, callback);
|
|
89
|
-
unsubscribers.push(unsub);
|
|
90
|
-
|
|
91
|
-
// Simulate ESC key press
|
|
92
|
-
const event = new KeyboardEvent('keydown', { key: 'Escape' });
|
|
93
|
-
document.dispatchEvent(event);
|
|
94
|
-
|
|
95
|
-
expect(callback).toHaveBeenCalled();
|
|
96
|
-
});
|
|
97
|
-
|
|
98
|
-
it.skip('should only call topmost handler in stack (LIFO) (E2E only)', () => {
|
|
99
|
-
const element1 = document.createElement('div');
|
|
100
|
-
const element2 = document.createElement('div');
|
|
101
|
-
const callback1 = mock(() => { });
|
|
102
|
-
const callback2 = mock(() => { });
|
|
103
|
-
|
|
104
|
-
const unsub1 = keyboard.pushEscapeHandler(element1, callback1);
|
|
105
|
-
const unsub2 = keyboard.pushEscapeHandler(element2, callback2);
|
|
106
|
-
unsubscribers.push(unsub1, unsub2);
|
|
107
|
-
|
|
108
|
-
// Simulate ESC key press
|
|
109
|
-
const event = new KeyboardEvent('keydown', { key: 'Escape' });
|
|
110
|
-
document.dispatchEvent(event);
|
|
111
|
-
|
|
112
|
-
// Only the second (topmost) handler should be called
|
|
113
|
-
expect(callback2).toHaveBeenCalled();
|
|
114
|
-
expect(callback1).not.toHaveBeenCalled();
|
|
115
|
-
});
|
|
116
|
-
|
|
117
|
-
it.skip('should call next handler after topmost is removed (E2E only)', () => {
|
|
118
|
-
const element1 = document.createElement('div');
|
|
119
|
-
const element2 = document.createElement('div');
|
|
120
|
-
const callback1 = mock(() => { });
|
|
121
|
-
const callback2 = mock(() => { });
|
|
122
|
-
|
|
123
|
-
const unsub1 = keyboard.pushEscapeHandler(element1, callback1);
|
|
124
|
-
const unsub2 = keyboard.pushEscapeHandler(element2, callback2);
|
|
125
|
-
unsubscribers.push(unsub1);
|
|
126
|
-
|
|
127
|
-
// Remove the topmost handler
|
|
128
|
-
unsub2();
|
|
129
|
-
|
|
130
|
-
// Simulate ESC key press
|
|
131
|
-
const event = new KeyboardEvent('keydown', { key: 'Escape' });
|
|
132
|
-
document.dispatchEvent(event);
|
|
133
|
-
|
|
134
|
-
// Now the first handler should be called
|
|
135
|
-
expect(callback1).toHaveBeenCalled();
|
|
136
|
-
});
|
|
137
|
-
|
|
138
|
-
it.skip('should not call handler for other keys (E2E only)', () => {
|
|
139
|
-
const element = document.createElement('div');
|
|
140
|
-
const callback = mock(() => { });
|
|
141
|
-
|
|
142
|
-
const unsub = keyboard.pushEscapeHandler(element, callback);
|
|
143
|
-
unsubscribers.push(unsub);
|
|
144
|
-
|
|
145
|
-
// Simulate Enter key press
|
|
146
|
-
const event = new KeyboardEvent('keydown', { key: 'Enter' });
|
|
147
|
-
document.dispatchEvent(event);
|
|
148
|
-
|
|
149
|
-
expect(callback).not.toHaveBeenCalled();
|
|
150
|
-
});
|
|
151
|
-
|
|
152
|
-
it('should handle unsubscribe being called multiple times', () => {
|
|
153
|
-
const element = document.createElement('div');
|
|
154
|
-
const callback = mock(() => { });
|
|
155
|
-
|
|
156
|
-
const unsub = keyboard.pushEscapeHandler(element, callback);
|
|
157
|
-
const depthBefore = keyboard.stackDepth;
|
|
158
|
-
|
|
159
|
-
// Call unsubscribe multiple times
|
|
160
|
-
unsub();
|
|
161
|
-
unsub();
|
|
162
|
-
unsub();
|
|
163
|
-
|
|
164
|
-
// Stack depth should only decrease once
|
|
165
|
-
expect(keyboard.stackDepth).toBe(depthBefore - 1);
|
|
166
|
-
});
|
|
167
|
-
});
|
|
168
|
-
|
|
169
|
-
describe('KeyboardManager.isTopmost', () => {
|
|
170
|
-
let unsubscribers = [];
|
|
171
|
-
|
|
172
|
-
afterEach(() => {
|
|
173
|
-
unsubscribers.forEach(unsub => unsub?.());
|
|
174
|
-
unsubscribers = [];
|
|
175
|
-
});
|
|
176
|
-
|
|
177
|
-
it('should be a function', () => {
|
|
178
|
-
expect(typeof keyboard.isTopmost).toBe('function');
|
|
179
|
-
});
|
|
180
|
-
|
|
181
|
-
it('should return false when stack is empty', () => {
|
|
182
|
-
// Clear stack first
|
|
183
|
-
const element = document.createElement('div');
|
|
184
|
-
expect(keyboard.isTopmost(element)).toBe(false);
|
|
185
|
-
});
|
|
186
|
-
|
|
187
|
-
it('should return true for topmost element', () => {
|
|
188
|
-
const element1 = document.createElement('div');
|
|
189
|
-
const element2 = document.createElement('div');
|
|
190
|
-
|
|
191
|
-
const unsub1 = keyboard.pushEscapeHandler(element1, () => { });
|
|
192
|
-
const unsub2 = keyboard.pushEscapeHandler(element2, () => { });
|
|
193
|
-
unsubscribers.push(unsub1, unsub2);
|
|
194
|
-
|
|
195
|
-
expect(keyboard.isTopmost(element2)).toBe(true);
|
|
196
|
-
expect(keyboard.isTopmost(element1)).toBe(false);
|
|
197
|
-
});
|
|
198
|
-
|
|
199
|
-
it('should update after handler is removed', () => {
|
|
200
|
-
const element1 = document.createElement('div');
|
|
201
|
-
const element2 = document.createElement('div');
|
|
202
|
-
|
|
203
|
-
const unsub1 = keyboard.pushEscapeHandler(element1, () => { });
|
|
204
|
-
const unsub2 = keyboard.pushEscapeHandler(element2, () => { });
|
|
205
|
-
unsubscribers.push(unsub1);
|
|
206
|
-
|
|
207
|
-
// Remove topmost
|
|
208
|
-
unsub2();
|
|
209
|
-
|
|
210
|
-
expect(keyboard.isTopmost(element1)).toBe(true);
|
|
211
|
-
});
|
|
212
|
-
});
|
|
213
|
-
|
|
214
|
-
describe('KeyboardManager.stackDepth', () => {
|
|
215
|
-
let unsubscribers = [];
|
|
216
|
-
|
|
217
|
-
afterEach(() => {
|
|
218
|
-
unsubscribers.forEach(unsub => unsub?.());
|
|
219
|
-
unsubscribers = [];
|
|
220
|
-
});
|
|
221
|
-
|
|
222
|
-
it('should be a number', () => {
|
|
223
|
-
expect(typeof keyboard.stackDepth).toBe('number');
|
|
224
|
-
});
|
|
225
|
-
|
|
226
|
-
it('should start at 0 or greater', () => {
|
|
227
|
-
expect(keyboard.stackDepth).toBeGreaterThanOrEqual(0);
|
|
228
|
-
});
|
|
229
|
-
|
|
230
|
-
it('should track multiple handlers correctly', () => {
|
|
231
|
-
const initialDepth = keyboard.stackDepth;
|
|
232
|
-
|
|
233
|
-
const unsub1 = keyboard.pushEscapeHandler(document.createElement('div'), () => { });
|
|
234
|
-
const unsub2 = keyboard.pushEscapeHandler(document.createElement('div'), () => { });
|
|
235
|
-
const unsub3 = keyboard.pushEscapeHandler(document.createElement('div'), () => { });
|
|
236
|
-
unsubscribers.push(unsub1, unsub2, unsub3);
|
|
237
|
-
|
|
238
|
-
expect(keyboard.stackDepth).toBe(initialDepth + 3);
|
|
239
|
-
});
|
|
240
|
-
});
|
|
241
|
-
|
|
242
|
-
describe('KeyboardManager Event Handling', () => {
|
|
243
|
-
let unsubscribers = [];
|
|
244
|
-
|
|
245
|
-
afterEach(() => {
|
|
246
|
-
unsubscribers.forEach(unsub => unsub?.());
|
|
247
|
-
unsubscribers = [];
|
|
248
|
-
});
|
|
249
|
-
|
|
250
|
-
it.skip('should prevent default on ESC when handlers exist (E2E only)', () => {
|
|
251
|
-
const element = document.createElement('div');
|
|
252
|
-
const callback = mock(() => { });
|
|
253
|
-
|
|
254
|
-
const unsub = keyboard.pushEscapeHandler(element, callback);
|
|
255
|
-
unsubscribers.push(unsub);
|
|
256
|
-
|
|
257
|
-
const event = new KeyboardEvent('keydown', { key: 'Escape', cancelable: true });
|
|
258
|
-
const preventDefaultSpy = mock(() => { });
|
|
259
|
-
event.preventDefault = preventDefaultSpy;
|
|
260
|
-
|
|
261
|
-
document.dispatchEvent(event);
|
|
262
|
-
|
|
263
|
-
expect(preventDefaultSpy).toHaveBeenCalled();
|
|
264
|
-
});
|
|
265
|
-
|
|
266
|
-
it.skip('should stop propagation on ESC when handlers exist (E2E only)', () => {
|
|
267
|
-
const element = document.createElement('div');
|
|
268
|
-
const callback = mock(() => { });
|
|
269
|
-
|
|
270
|
-
const unsub = keyboard.pushEscapeHandler(element, callback);
|
|
271
|
-
unsubscribers.push(unsub);
|
|
272
|
-
|
|
273
|
-
const event = new KeyboardEvent('keydown', { key: 'Escape', bubbles: true });
|
|
274
|
-
const stopPropagationSpy = mock(() => { });
|
|
275
|
-
event.stopPropagation = stopPropagationSpy;
|
|
276
|
-
|
|
277
|
-
document.dispatchEvent(event);
|
|
278
|
-
|
|
279
|
-
expect(stopPropagationSpy).toHaveBeenCalled();
|
|
280
|
-
});
|
|
281
|
-
});
|
|
282
|
-
|
|
283
|
-
describe('KeyboardManager Edge Cases', () => {
|
|
284
|
-
it.skip('should handle empty callback gracefully (E2E only)', () => {
|
|
285
|
-
const element = document.createElement('div');
|
|
286
|
-
const unsub = keyboard.pushEscapeHandler(element, () => { });
|
|
287
|
-
|
|
288
|
-
// Should not throw
|
|
289
|
-
const event = new KeyboardEvent('keydown', { key: 'Escape' });
|
|
290
|
-
expect(() => document.dispatchEvent(event)).not.toThrow();
|
|
291
|
-
|
|
292
|
-
unsub();
|
|
293
|
-
});
|
|
294
|
-
|
|
295
|
-
it.skip('should preserve order after middle element is removed (E2E only)', () => {
|
|
296
|
-
const element1 = document.createElement('div');
|
|
297
|
-
const element2 = document.createElement('div');
|
|
298
|
-
const element3 = document.createElement('div');
|
|
299
|
-
const callback1 = mock(() => { });
|
|
300
|
-
const callback3 = mock(() => { });
|
|
301
|
-
|
|
302
|
-
const unsub1 = keyboard.pushEscapeHandler(element1, callback1);
|
|
303
|
-
const unsub2 = keyboard.pushEscapeHandler(element2, () => { });
|
|
304
|
-
const unsub3 = keyboard.pushEscapeHandler(element3, callback3);
|
|
305
|
-
|
|
306
|
-
// Remove middle element
|
|
307
|
-
unsub2();
|
|
308
|
-
|
|
309
|
-
// Send ESC - should call callback3 (still topmost)
|
|
310
|
-
const event = new KeyboardEvent('keydown', { key: 'Escape' });
|
|
311
|
-
document.dispatchEvent(event);
|
|
312
|
-
|
|
313
|
-
expect(callback3).toHaveBeenCalled();
|
|
314
|
-
expect(callback1).not.toHaveBeenCalled();
|
|
315
|
-
|
|
316
|
-
unsub1();
|
|
317
|
-
unsub3();
|
|
318
|
-
});
|
|
319
|
-
});
|
|
@@ -1,129 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @fileoverview Unit tests for core/layers.js
|
|
3
|
-
* Tests the centralized Z-Index Layer System
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import { describe, expect, it, beforeEach } from 'bun:test';
|
|
7
|
-
import { Z_INDEX, injectLayerTokens } from '../../src/core/layers.js';
|
|
8
|
-
|
|
9
|
-
describe('Z_INDEX Constants', () => {
|
|
10
|
-
it('should export Z_INDEX object', () => {
|
|
11
|
-
expect(Z_INDEX).toBeDefined();
|
|
12
|
-
expect(typeof Z_INDEX).toBe('object');
|
|
13
|
-
});
|
|
14
|
-
|
|
15
|
-
it('should have all required layer levels', () => {
|
|
16
|
-
expect(Z_INDEX.base).toBeDefined();
|
|
17
|
-
expect(Z_INDEX.sticky).toBeDefined();
|
|
18
|
-
expect(Z_INDEX.dropdown).toBeDefined();
|
|
19
|
-
expect(Z_INDEX.drawer).toBeDefined();
|
|
20
|
-
expect(Z_INDEX.modal).toBeDefined();
|
|
21
|
-
expect(Z_INDEX.toast).toBeDefined();
|
|
22
|
-
expect(Z_INDEX.tooltip).toBeDefined();
|
|
23
|
-
expect(Z_INDEX.overlay).toBeDefined();
|
|
24
|
-
expect(Z_INDEX.devtools).toBeDefined();
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
it('should have correct numerical values', () => {
|
|
28
|
-
expect(Z_INDEX.base).toBe(1);
|
|
29
|
-
expect(Z_INDEX.sticky).toBe(100);
|
|
30
|
-
expect(Z_INDEX.dropdown).toBe(1000);
|
|
31
|
-
expect(Z_INDEX.drawer).toBe(1100);
|
|
32
|
-
expect(Z_INDEX.modal).toBe(1200);
|
|
33
|
-
expect(Z_INDEX.toast).toBe(1300);
|
|
34
|
-
expect(Z_INDEX.tooltip).toBe(1400);
|
|
35
|
-
expect(Z_INDEX.overlay).toBe(9999);
|
|
36
|
-
expect(Z_INDEX.devtools).toBe(999999);
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
it('should have layers in ascending order', () => {
|
|
40
|
-
expect(Z_INDEX.base).toBeLessThan(Z_INDEX.sticky);
|
|
41
|
-
expect(Z_INDEX.sticky).toBeLessThan(Z_INDEX.dropdown);
|
|
42
|
-
expect(Z_INDEX.dropdown).toBeLessThan(Z_INDEX.drawer);
|
|
43
|
-
expect(Z_INDEX.drawer).toBeLessThan(Z_INDEX.modal);
|
|
44
|
-
expect(Z_INDEX.modal).toBeLessThan(Z_INDEX.toast);
|
|
45
|
-
expect(Z_INDEX.toast).toBeLessThan(Z_INDEX.tooltip);
|
|
46
|
-
expect(Z_INDEX.tooltip).toBeLessThan(Z_INDEX.overlay);
|
|
47
|
-
expect(Z_INDEX.overlay).toBeLessThan(Z_INDEX.devtools);
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
it('should be frozen (immutable)', () => {
|
|
51
|
-
expect(Object.isFrozen(Z_INDEX)).toBe(true);
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
it('should not allow modifications', () => {
|
|
55
|
-
// Attempt to modify should fail silently in strict mode
|
|
56
|
-
const originalValue = Z_INDEX.modal;
|
|
57
|
-
try {
|
|
58
|
-
// @ts-ignore - intentionally testing immutability
|
|
59
|
-
Z_INDEX.modal = 999;
|
|
60
|
-
} catch (e) {
|
|
61
|
-
// Expected in strict mode
|
|
62
|
-
}
|
|
63
|
-
expect(Z_INDEX.modal).toBe(originalValue);
|
|
64
|
-
});
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
describe('injectLayerTokens', () => {
|
|
68
|
-
beforeEach(() => {
|
|
69
|
-
// Remove any existing layer tokens style element
|
|
70
|
-
const existing = document.getElementById('mu-layer-tokens');
|
|
71
|
-
if (existing) {
|
|
72
|
-
existing.remove();
|
|
73
|
-
}
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
it('should be a function', () => {
|
|
77
|
-
expect(typeof injectLayerTokens).toBe('function');
|
|
78
|
-
});
|
|
79
|
-
|
|
80
|
-
it('should inject style element into document head', () => {
|
|
81
|
-
injectLayerTokens();
|
|
82
|
-
const styleEl = document.getElementById('mu-layer-tokens');
|
|
83
|
-
expect(styleEl).toBeDefined();
|
|
84
|
-
expect(styleEl?.tagName.toLowerCase()).toBe('style');
|
|
85
|
-
});
|
|
86
|
-
|
|
87
|
-
it('should be idempotent (only inject once)', () => {
|
|
88
|
-
injectLayerTokens();
|
|
89
|
-
injectLayerTokens();
|
|
90
|
-
injectLayerTokens();
|
|
91
|
-
|
|
92
|
-
const elements = document.querySelectorAll('#mu-layer-tokens');
|
|
93
|
-
expect(elements.length).toBe(1);
|
|
94
|
-
});
|
|
95
|
-
|
|
96
|
-
it('should inject CSS custom properties for all layers', () => {
|
|
97
|
-
injectLayerTokens();
|
|
98
|
-
const styleEl = document.getElementById('mu-layer-tokens');
|
|
99
|
-
const content = styleEl?.textContent || '';
|
|
100
|
-
|
|
101
|
-
expect(content).toContain('--z-base');
|
|
102
|
-
expect(content).toContain('--z-sticky');
|
|
103
|
-
expect(content).toContain('--z-dropdown');
|
|
104
|
-
expect(content).toContain('--z-drawer');
|
|
105
|
-
expect(content).toContain('--z-modal');
|
|
106
|
-
expect(content).toContain('--z-toast');
|
|
107
|
-
expect(content).toContain('--z-tooltip');
|
|
108
|
-
expect(content).toContain('--z-overlay');
|
|
109
|
-
expect(content).toContain('--z-devtools');
|
|
110
|
-
});
|
|
111
|
-
|
|
112
|
-
it('should inject correct values in CSS', () => {
|
|
113
|
-
injectLayerTokens();
|
|
114
|
-
const styleEl = document.getElementById('mu-layer-tokens');
|
|
115
|
-
const content = styleEl?.textContent || '';
|
|
116
|
-
|
|
117
|
-
expect(content).toContain('--z-base: 1');
|
|
118
|
-
expect(content).toContain('--z-modal: 1200');
|
|
119
|
-
expect(content).toContain('--z-overlay: 9999');
|
|
120
|
-
});
|
|
121
|
-
|
|
122
|
-
it('should inject into :root selector', () => {
|
|
123
|
-
injectLayerTokens();
|
|
124
|
-
const styleEl = document.getElementById('mu-layer-tokens');
|
|
125
|
-
const content = styleEl?.textContent || '';
|
|
126
|
-
|
|
127
|
-
expect(content).toContain(':root');
|
|
128
|
-
});
|
|
129
|
-
});
|
|
@@ -1,114 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @fileoverview Tests for Namespaced Stores (Enterprise Features)
|
|
3
|
-
* Tests createNamespacedStore, getStore, getAllStores, captureAppState, restoreAppState
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import { describe, test, expect } from 'bun:test';
|
|
7
|
-
|
|
8
|
-
describe('Namespaced Stores', () => {
|
|
9
|
-
describe('Module Exports', () => {
|
|
10
|
-
test('should export createNamespacedStore function', async () => {
|
|
11
|
-
const module = await import('../../src/core/store.js');
|
|
12
|
-
expect(typeof module.createNamespacedStore).toBe('function');
|
|
13
|
-
});
|
|
14
|
-
|
|
15
|
-
test('should export getStore function', async () => {
|
|
16
|
-
const module = await import('../../src/core/store.js');
|
|
17
|
-
expect(typeof module.getStore).toBe('function');
|
|
18
|
-
});
|
|
19
|
-
|
|
20
|
-
test('should export getAllStores function', async () => {
|
|
21
|
-
const module = await import('../../src/core/store.js');
|
|
22
|
-
expect(typeof module.getAllStores).toBe('function');
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
test('should export captureAppState function', async () => {
|
|
26
|
-
const module = await import('../../src/core/store.js');
|
|
27
|
-
expect(typeof module.captureAppState).toBe('function');
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
test('should export restoreAppState function', async () => {
|
|
31
|
-
const module = await import('../../src/core/store.js');
|
|
32
|
-
expect(typeof module.restoreAppState).toBe('function');
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
test('should export enableObservability function', async () => {
|
|
36
|
-
const module = await import('../../src/core/store.js');
|
|
37
|
-
expect(typeof module.enableObservability).toBe('function');
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
test('should export disableObservability function', async () => {
|
|
41
|
-
const module = await import('../../src/core/store.js');
|
|
42
|
-
expect(typeof module.disableObservability).toBe('function');
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
test('should export getStateHistory function', async () => {
|
|
46
|
-
const module = await import('../../src/core/store.js');
|
|
47
|
-
expect(typeof module.getStateHistory).toBe('function');
|
|
48
|
-
});
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
describe('createNamespacedStore Integration', () => {
|
|
52
|
-
test('should create a store and retrieve it', async () => {
|
|
53
|
-
const { createNamespacedStore, getStore } = await import('../../src/core/store.js');
|
|
54
|
-
|
|
55
|
-
// Use unique namespace to avoid conflicts
|
|
56
|
-
const ns = 'test-' + Date.now();
|
|
57
|
-
const store = createNamespacedStore(ns, { count: 0 });
|
|
58
|
-
|
|
59
|
-
expect(store).toBeDefined();
|
|
60
|
-
expect(store.namespace).toBe(ns);
|
|
61
|
-
|
|
62
|
-
const retrieved = getStore(ns);
|
|
63
|
-
expect(retrieved).toBe(store);
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
test('should initialize with provided state', async () => {
|
|
67
|
-
const { createNamespacedStore } = await import('../../src/core/store.js');
|
|
68
|
-
|
|
69
|
-
const ns = 'init-test-' + Date.now();
|
|
70
|
-
const store = createNamespacedStore(ns, { items: ['a', 'b'], total: 100 });
|
|
71
|
-
|
|
72
|
-
const state = store.get();
|
|
73
|
-
expect(state.items).toEqual(['a', 'b']);
|
|
74
|
-
expect(state.total).toBe(100);
|
|
75
|
-
});
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
describe('getAllStores', () => {
|
|
79
|
-
test('should return object with stores', async () => {
|
|
80
|
-
const { getAllStores } = await import('../../src/core/store.js');
|
|
81
|
-
|
|
82
|
-
const stores = getAllStores();
|
|
83
|
-
expect(typeof stores).toBe('object');
|
|
84
|
-
});
|
|
85
|
-
});
|
|
86
|
-
|
|
87
|
-
describe('getStore', () => {
|
|
88
|
-
test('should return undefined for non-existent store', async () => {
|
|
89
|
-
const { getStore } = await import('../../src/core/store.js');
|
|
90
|
-
const store = getStore('definitely-not-existing-' + Date.now());
|
|
91
|
-
expect(store).toBeUndefined();
|
|
92
|
-
});
|
|
93
|
-
});
|
|
94
|
-
|
|
95
|
-
describe('Observability', () => {
|
|
96
|
-
test('should enable and disable observability', async () => {
|
|
97
|
-
const { enableObservability, disableObservability } = await import('../../src/core/store.js');
|
|
98
|
-
|
|
99
|
-
// Should not throw
|
|
100
|
-
enableObservability();
|
|
101
|
-
expect(true).toBe(true);
|
|
102
|
-
|
|
103
|
-
disableObservability();
|
|
104
|
-
expect(true).toBe(true);
|
|
105
|
-
});
|
|
106
|
-
|
|
107
|
-
test('should return history array', async () => {
|
|
108
|
-
const { getStateHistory } = await import('../../src/core/store.js');
|
|
109
|
-
|
|
110
|
-
const history = getStateHistory('any');
|
|
111
|
-
expect(Array.isArray(history)).toBe(true);
|
|
112
|
-
});
|
|
113
|
-
});
|
|
114
|
-
});
|
|
@@ -1,121 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @fileoverview Unit Tests for render.js Module
|
|
3
|
-
* Target: 82% → 95% coverage
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import { describe, test, expect, beforeAll, beforeEach } from 'bun:test';
|
|
7
|
-
import { scheduler, memo, debounce, throttle, domBatch, processInChunks } from '../../src/core/render.js';
|
|
8
|
-
|
|
9
|
-
describe('render Module Unit Tests', () => {
|
|
10
|
-
|
|
11
|
-
// Mock requestAnimationFrame
|
|
12
|
-
beforeAll(() => {
|
|
13
|
-
globalThis.requestAnimationFrame = (cb) => { cb(Date.now()); return 0; };
|
|
14
|
-
});
|
|
15
|
-
|
|
16
|
-
// SCHEDULER
|
|
17
|
-
test('scheduler should exist', () => {
|
|
18
|
-
expect(scheduler).toBeDefined();
|
|
19
|
-
});
|
|
20
|
-
|
|
21
|
-
test('scheduler.schedule should be a function', () => {
|
|
22
|
-
expect(typeof scheduler.schedule).toBe('function');
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
test('scheduler should execute callbacks', () => {
|
|
26
|
-
let called = false;
|
|
27
|
-
scheduler.schedule(() => { called = true; });
|
|
28
|
-
expect(called).toBe(true);
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
test('scheduler should batch multiple callbacks', () => {
|
|
32
|
-
let count = 0;
|
|
33
|
-
scheduler.schedule(() => { count++; });
|
|
34
|
-
scheduler.schedule(() => { count++; });
|
|
35
|
-
scheduler.schedule(() => { count++; });
|
|
36
|
-
expect(count).toBe(3);
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
// MEMO
|
|
40
|
-
test('memo should be a function', () => {
|
|
41
|
-
expect(typeof memo).toBe('function');
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
test('memo should cache results', () => {
|
|
45
|
-
let callCount = 0;
|
|
46
|
-
const expensive = memo((x) => { callCount++; return x * 2; });
|
|
47
|
-
expect(expensive(5)).toBe(10);
|
|
48
|
-
expect(expensive(5)).toBe(10);
|
|
49
|
-
expect(callCount).toBe(1); // Only called once
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
test('memo should compute for different args', () => {
|
|
53
|
-
const fn = memo((x) => x * 2);
|
|
54
|
-
expect(fn(2)).toBe(4);
|
|
55
|
-
expect(fn(3)).toBe(6);
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
// DEBOUNCE
|
|
59
|
-
test('debounce should be a function', () => {
|
|
60
|
-
expect(typeof debounce).toBe('function');
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
test('debounce should return a function', () => {
|
|
64
|
-
const debounced = debounce(() => { }, 100);
|
|
65
|
-
expect(typeof debounced).toBe('function');
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
// THROTTLE
|
|
69
|
-
test('throttle should be a function', () => {
|
|
70
|
-
expect(typeof throttle).toBe('function');
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
test('throttle should return a function', () => {
|
|
74
|
-
const throttled = throttle(() => { }, 100);
|
|
75
|
-
expect(typeof throttled).toBe('function');
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
test('throttle should call function immediately', () => {
|
|
79
|
-
let called = false;
|
|
80
|
-
const throttled = throttle(() => { called = true; }, 100);
|
|
81
|
-
throttled();
|
|
82
|
-
expect(called).toBe(true);
|
|
83
|
-
});
|
|
84
|
-
|
|
85
|
-
// DOM BATCH
|
|
86
|
-
test('domBatch should exist', () => {
|
|
87
|
-
expect(domBatch).toBeDefined();
|
|
88
|
-
});
|
|
89
|
-
|
|
90
|
-
test('domBatch.read should be a function', () => {
|
|
91
|
-
expect(typeof domBatch.read).toBe('function');
|
|
92
|
-
});
|
|
93
|
-
|
|
94
|
-
test('domBatch.write should be a function', () => {
|
|
95
|
-
expect(typeof domBatch.write).toBe('function');
|
|
96
|
-
});
|
|
97
|
-
|
|
98
|
-
test('domBatch should execute reads', () => {
|
|
99
|
-
let readCalled = false;
|
|
100
|
-
domBatch.read(() => { readCalled = true; });
|
|
101
|
-
expect(readCalled).toBe(true);
|
|
102
|
-
});
|
|
103
|
-
|
|
104
|
-
test('domBatch should execute writes', () => {
|
|
105
|
-
let writeCalled = false;
|
|
106
|
-
domBatch.write(() => { writeCalled = true; });
|
|
107
|
-
expect(writeCalled).toBe(true);
|
|
108
|
-
});
|
|
109
|
-
|
|
110
|
-
// PROCESS IN CHUNKS
|
|
111
|
-
test('processInChunks should be a function', () => {
|
|
112
|
-
expect(typeof processInChunks).toBe('function');
|
|
113
|
-
});
|
|
114
|
-
|
|
115
|
-
test('processInChunks should process all items', async () => {
|
|
116
|
-
const items = [1, 2, 3, 4, 5];
|
|
117
|
-
const processed = [];
|
|
118
|
-
await processInChunks(items, (item) => processed.push(item), 2);
|
|
119
|
-
expect(processed).toEqual([1, 2, 3, 4, 5]);
|
|
120
|
-
});
|
|
121
|
-
});
|