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
package/scripts/build-utils.js
DELETED
|
@@ -1,101 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @fileoverview Build utilities for microUI
|
|
3
|
-
*
|
|
4
|
-
* Contains reusable functions for the build process,
|
|
5
|
-
* particularly for automatic component detection.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* Scan HTML content for mu-* components within page divs.
|
|
10
|
-
* Extracts component names from each page section.
|
|
11
|
-
*
|
|
12
|
-
* @param {string} htmlContent - Full HTML content to scan
|
|
13
|
-
* @returns {Object<string, string[]>} Map of pageId -> component names
|
|
14
|
-
*
|
|
15
|
-
* @example
|
|
16
|
-
* const html = '<div id="page-home"><mu-button>Click</mu-button></div>';
|
|
17
|
-
* const result = scanPageComponents(html);
|
|
18
|
-
* // { home: ['mu-button'] }
|
|
19
|
-
*/
|
|
20
|
-
export function scanPageComponents(htmlContent) {
|
|
21
|
-
const pages = {};
|
|
22
|
-
|
|
23
|
-
// Match page divs: <div id="page-xxx">...</div>
|
|
24
|
-
// Use non-greedy matching and look ahead for next page or end
|
|
25
|
-
const pageRegex = /<div\s+id="page-(\w+)"[^>]*>([\s\S]*?)(?=<div\s+id="page-|\s*<\/mu-layout|\s*<footer|\s*$)/gi;
|
|
26
|
-
|
|
27
|
-
let match;
|
|
28
|
-
while ((match = pageRegex.exec(htmlContent)) !== null) {
|
|
29
|
-
const pageId = match[1];
|
|
30
|
-
const pageContent = match[2];
|
|
31
|
-
|
|
32
|
-
// Extract all mu-* tags (opening tags only)
|
|
33
|
-
const components = new Set();
|
|
34
|
-
const tagRegex = /<(mu-[\w-]+)/gi;
|
|
35
|
-
let tagMatch;
|
|
36
|
-
while ((tagMatch = tagRegex.exec(pageContent)) !== null) {
|
|
37
|
-
// Normalize to lowercase
|
|
38
|
-
components.add(tagMatch[1].toLowerCase());
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
if (components.size > 0) {
|
|
42
|
-
pages[pageId] = [...components].sort();
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
return pages;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
/**
|
|
50
|
-
* Map component names to their route file names.
|
|
51
|
-
* Some components share routes (e.g., mu-tab is in tabs route).
|
|
52
|
-
*
|
|
53
|
-
* @param {string[]} components - Array of component names
|
|
54
|
-
* @returns {string[]} Array of route names to load
|
|
55
|
-
*/
|
|
56
|
-
export function componentsToRoutes(components) {
|
|
57
|
-
// Component -> route mapping (most are 1:1 with pluralization)
|
|
58
|
-
const componentRouteMap = {
|
|
59
|
-
'mu-tab': 'tabs',
|
|
60
|
-
'mu-tabs': 'tabs',
|
|
61
|
-
'mu-button': 'buttons',
|
|
62
|
-
'mu-input': 'inputs',
|
|
63
|
-
'mu-textarea': 'inputs',
|
|
64
|
-
'mu-checkbox': 'checkboxes',
|
|
65
|
-
'mu-switch': 'switches',
|
|
66
|
-
'mu-radio': 'radios',
|
|
67
|
-
'mu-dropdown': 'dropdowns',
|
|
68
|
-
'mu-card': 'cards',
|
|
69
|
-
'mu-stack': 'layout',
|
|
70
|
-
'mu-grid': 'layout',
|
|
71
|
-
'mu-alert': 'alerts',
|
|
72
|
-
'mu-toast': 'toasts',
|
|
73
|
-
'mu-modal': 'modals',
|
|
74
|
-
'mu-progress': 'progress',
|
|
75
|
-
'mu-spinner': 'progress',
|
|
76
|
-
'mu-avatar': 'avatars',
|
|
77
|
-
'mu-badge': 'badges',
|
|
78
|
-
'mu-chip': 'chips',
|
|
79
|
-
'mu-icon': 'icons',
|
|
80
|
-
'mu-navbar': 'navbar',
|
|
81
|
-
'mu-example': 'tabs', // part of tabs route
|
|
82
|
-
'mu-code': 'tabs', // part of tabs route
|
|
83
|
-
'mu-api-table': 'tabs', // part of tabs route
|
|
84
|
-
// Shell components (always loaded)
|
|
85
|
-
'mu-layout': 'shell-critical',
|
|
86
|
-
'mu-drawer': 'shell-critical',
|
|
87
|
-
'mu-drawer-item': 'shell-critical',
|
|
88
|
-
'mu-theme-toggle': 'shell-deferred',
|
|
89
|
-
'mu-bottom-nav': 'shell-deferred',
|
|
90
|
-
};
|
|
91
|
-
|
|
92
|
-
const routes = new Set();
|
|
93
|
-
for (const comp of components) {
|
|
94
|
-
const route = componentRouteMap[comp];
|
|
95
|
-
if (route) {
|
|
96
|
-
routes.add(route);
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
return [...routes].sort();
|
|
101
|
-
}
|
package/scripts/test-isolated.js
DELETED
|
@@ -1,151 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bun
|
|
2
|
-
/**
|
|
3
|
-
* @fileoverview Isolated Test Runner
|
|
4
|
-
*
|
|
5
|
-
* Runs each test file in a separate Bun process to ensure complete isolation
|
|
6
|
-
* of globalThis, module cache, and custom element registries.
|
|
7
|
-
*
|
|
8
|
-
* This solves the linkedom globalThis pollution issue where tests pass
|
|
9
|
-
* individually but fail when run together.
|
|
10
|
-
*
|
|
11
|
-
* Usage: bun run scripts/test-isolated.js [pattern]
|
|
12
|
-
*/
|
|
13
|
-
|
|
14
|
-
import { spawn } from 'bun';
|
|
15
|
-
import { readdirSync, statSync } from 'fs';
|
|
16
|
-
import { join, relative } from 'path';
|
|
17
|
-
|
|
18
|
-
const args = process.argv.slice(2);
|
|
19
|
-
const filterPattern = args[0] || '';
|
|
20
|
-
|
|
21
|
-
// Colors for output
|
|
22
|
-
const colors = {
|
|
23
|
-
reset: '\x1b[0m',
|
|
24
|
-
green: '\x1b[32m',
|
|
25
|
-
red: '\x1b[31m',
|
|
26
|
-
yellow: '\x1b[33m',
|
|
27
|
-
cyan: '\x1b[36m',
|
|
28
|
-
dim: '\x1b[2m'
|
|
29
|
-
};
|
|
30
|
-
|
|
31
|
-
// Find all test files
|
|
32
|
-
function findTestFiles(dir, files = []) {
|
|
33
|
-
const entries = readdirSync(dir);
|
|
34
|
-
for (const entry of entries) {
|
|
35
|
-
const fullPath = join(dir, entry);
|
|
36
|
-
const stat = statSync(fullPath);
|
|
37
|
-
if (stat.isDirectory() && !entry.startsWith('.') && entry !== 'node_modules') {
|
|
38
|
-
findTestFiles(fullPath, files);
|
|
39
|
-
} else if (entry.endsWith('.test.js')) {
|
|
40
|
-
files.push(fullPath);
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
return files;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
// Run a single test file in isolation
|
|
47
|
-
async function runTestFile(testFile) {
|
|
48
|
-
const relativePath = relative(process.cwd(), testFile);
|
|
49
|
-
|
|
50
|
-
const proc = spawn({
|
|
51
|
-
cmd: ['bun', 'test', testFile],
|
|
52
|
-
cwd: process.cwd(),
|
|
53
|
-
stdout: 'pipe',
|
|
54
|
-
stderr: 'pipe',
|
|
55
|
-
env: {
|
|
56
|
-
...process.env,
|
|
57
|
-
FORCE_COLOR: '1'
|
|
58
|
-
}
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
const stdout = await new Response(proc.stdout).text();
|
|
62
|
-
const stderr = await new Response(proc.stderr).text();
|
|
63
|
-
const exitCode = await proc.exited;
|
|
64
|
-
|
|
65
|
-
// Combine output (bun test writes summary to stderr)
|
|
66
|
-
const output = stdout + stderr;
|
|
67
|
-
|
|
68
|
-
// Parse results from output
|
|
69
|
-
const passMatch = output.match(/\s*(\d+)\s+pass/);
|
|
70
|
-
const failMatch = output.match(/\s*(\d+)\s+fail/);
|
|
71
|
-
const skipMatch = output.match(/\s*(\d+)\s+skip/);
|
|
72
|
-
|
|
73
|
-
const pass = passMatch ? parseInt(passMatch[1]) : 0;
|
|
74
|
-
const fail = failMatch ? parseInt(failMatch[1]) : 0;
|
|
75
|
-
const skip = skipMatch ? parseInt(skipMatch[1]) : 0;
|
|
76
|
-
|
|
77
|
-
return {
|
|
78
|
-
file: relativePath,
|
|
79
|
-
pass,
|
|
80
|
-
fail,
|
|
81
|
-
skip,
|
|
82
|
-
exitCode,
|
|
83
|
-
stdout,
|
|
84
|
-
stderr
|
|
85
|
-
};
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
// Main execution
|
|
89
|
-
async function main() {
|
|
90
|
-
const testsDir = join(process.cwd(), 'tests');
|
|
91
|
-
let testFiles = findTestFiles(testsDir);
|
|
92
|
-
|
|
93
|
-
// Filter by pattern if provided
|
|
94
|
-
if (filterPattern) {
|
|
95
|
-
testFiles = testFiles.filter(f => f.includes(filterPattern));
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
// Filter out e2e tests (they have their own runner)
|
|
99
|
-
testFiles = testFiles.filter(f => !f.includes('/e2e/'));
|
|
100
|
-
|
|
101
|
-
console.log(`${colors.cyan}๐งช Isolated Test Runner${colors.reset}`);
|
|
102
|
-
console.log(`${colors.dim}Running ${testFiles.length} test files in separate processes...${colors.reset}\n`);
|
|
103
|
-
|
|
104
|
-
const startTime = Date.now();
|
|
105
|
-
const results = [];
|
|
106
|
-
|
|
107
|
-
for (const testFile of testFiles) {
|
|
108
|
-
const result = await runTestFile(testFile);
|
|
109
|
-
results.push(result);
|
|
110
|
-
|
|
111
|
-
// Print inline status
|
|
112
|
-
const status = result.fail === 0
|
|
113
|
-
? `${colors.green}โ${colors.reset}`
|
|
114
|
-
: `${colors.red}โ${colors.reset}`;
|
|
115
|
-
const counts = `${colors.green}${result.pass}${colors.reset}/${colors.red}${result.fail}${colors.reset}/${colors.yellow}${result.skip}${colors.reset}`;
|
|
116
|
-
console.log(`${status} ${result.file} [${counts}]`);
|
|
117
|
-
|
|
118
|
-
// Show errors for failed files
|
|
119
|
-
if (result.fail > 0 && process.env.VERBOSE) {
|
|
120
|
-
console.log(result.stdout);
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
const duration = Date.now() - startTime;
|
|
125
|
-
|
|
126
|
-
// Summary
|
|
127
|
-
const totalPass = results.reduce((sum, r) => sum + r.pass, 0);
|
|
128
|
-
const totalFail = results.reduce((sum, r) => sum + r.fail, 0);
|
|
129
|
-
const totalSkip = results.reduce((sum, r) => sum + r.skip, 0);
|
|
130
|
-
const failedFiles = results.filter(r => r.fail > 0);
|
|
131
|
-
|
|
132
|
-
console.log(`\n${colors.cyan}โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ${colors.reset}`);
|
|
133
|
-
console.log(`${colors.green}${totalPass} pass${colors.reset} | ${colors.red}${totalFail} fail${colors.reset} | ${colors.yellow}${totalSkip} skip${colors.reset}`);
|
|
134
|
-
console.log(`${colors.dim}Ran ${results.length} test files in ${(duration / 1000).toFixed(2)}s${colors.reset}`);
|
|
135
|
-
|
|
136
|
-
if (failedFiles.length > 0) {
|
|
137
|
-
console.log(`\n${colors.red}Failed files:${colors.reset}`);
|
|
138
|
-
for (const f of failedFiles) {
|
|
139
|
-
console.log(` ${colors.red}โ${colors.reset} ${f.file} (${f.fail} failures)`);
|
|
140
|
-
}
|
|
141
|
-
process.exit(1);
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
console.log(`\n${colors.green}โ All tests passed!${colors.reset}`);
|
|
145
|
-
process.exit(0);
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
main().catch(err => {
|
|
149
|
-
console.error('Error:', err);
|
|
150
|
-
process.exit(1);
|
|
151
|
-
});
|
package/server.js
DELETED
|
@@ -1,256 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* microUI Dev Server with caching + Brotli/gzip compression
|
|
3
|
-
* Optimized for Lighthouse 100 - SOTA 2026
|
|
4
|
-
* Run: bun run server.js
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import { brotliCompressSync, constants as zlibConstants } from 'zlib';
|
|
8
|
-
|
|
9
|
-
const PORT = 5001;
|
|
10
|
-
|
|
11
|
-
// Cache durations
|
|
12
|
-
const CACHE_LONG = 'public, max-age=31536000, immutable'; // 1 year for versioned assets
|
|
13
|
-
const CACHE_SHORT = 'public, max-age=3600'; // 1 hour for HTML
|
|
14
|
-
const CACHE_REVALIDATE = 'public, max-age=0, must-revalidate'; // Always revalidate but allow bf-cache
|
|
15
|
-
|
|
16
|
-
// MIME types
|
|
17
|
-
const MIME_TYPES = {
|
|
18
|
-
'.html': 'text/html; charset=utf-8',
|
|
19
|
-
'.css': 'text/css; charset=utf-8',
|
|
20
|
-
'.js': 'text/javascript; charset=utf-8',
|
|
21
|
-
'.mjs': 'text/javascript; charset=utf-8',
|
|
22
|
-
'.json': 'application/json',
|
|
23
|
-
'.txt': 'text/plain; charset=utf-8',
|
|
24
|
-
'.png': 'image/png',
|
|
25
|
-
'.jpg': 'image/jpeg',
|
|
26
|
-
'.jpeg': 'image/jpeg',
|
|
27
|
-
'.webp': 'image/webp',
|
|
28
|
-
'.svg': 'image/svg+xml',
|
|
29
|
-
'.ico': 'image/x-icon',
|
|
30
|
-
'.woff': 'font/woff',
|
|
31
|
-
'.woff2': 'font/woff2',
|
|
32
|
-
'.webmanifest': 'application/manifest+json',
|
|
33
|
-
};
|
|
34
|
-
|
|
35
|
-
// Types that benefit from gzip
|
|
36
|
-
const COMPRESSIBLE_TYPES = ['.html', '.css', '.js', '.mjs', '.json', '.svg'];
|
|
37
|
-
|
|
38
|
-
// Preload hints for critical resources (Link headers)
|
|
39
|
-
const PRELOAD_HINTS = {
|
|
40
|
-
'/demo/shell.html': [
|
|
41
|
-
'</dist/microui.css>; rel=preload; as=style',
|
|
42
|
-
'</assets/fonts/roboto-400.woff2>; rel=preload; as=font; type="font/woff2"; crossorigin',
|
|
43
|
-
'</assets/logo-banner-400.webp>; rel=preload; as=image; type="image/webp"',
|
|
44
|
-
],
|
|
45
|
-
};
|
|
46
|
-
|
|
47
|
-
function getContentType(path) {
|
|
48
|
-
const ext = path.match(/\.[^.]+$/)?.[0] || '';
|
|
49
|
-
return MIME_TYPES[ext] || 'application/octet-stream';
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
function getCacheControl(path) {
|
|
53
|
-
const cleanPath = path.split('?')[0];
|
|
54
|
-
|
|
55
|
-
// Immutable assets: versioned bundles, fonts, images
|
|
56
|
-
if (cleanPath.startsWith('/dist/') ||
|
|
57
|
-
cleanPath.startsWith('/assets/') ||
|
|
58
|
-
cleanPath.endsWith('.woff2') ||
|
|
59
|
-
cleanPath.endsWith('.woff') ||
|
|
60
|
-
cleanPath.endsWith('.png') ||
|
|
61
|
-
cleanPath.endsWith('.webp') ||
|
|
62
|
-
cleanPath.endsWith('.jpg') ||
|
|
63
|
-
cleanPath.endsWith('.ico')) {
|
|
64
|
-
return CACHE_LONG;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
// HTML: allow bf-cache but revalidate
|
|
68
|
-
if (cleanPath.endsWith('.html') || cleanPath === '/' || cleanPath === '/index.html') {
|
|
69
|
-
return CACHE_REVALIDATE;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
// SW and manifest need revalidate
|
|
73
|
-
if (cleanPath.endsWith('sw.js') || cleanPath.endsWith('manifest.json')) {
|
|
74
|
-
return CACHE_REVALIDATE;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
return CACHE_SHORT;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
function shouldCompress(path) {
|
|
81
|
-
const cleanPath = path.split('?')[0];
|
|
82
|
-
const ext = cleanPath.match(/\.[^.]+$/)?.[0] || '';
|
|
83
|
-
return COMPRESSIBLE_TYPES.includes(ext);
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
function getPreloadLinks(path) {
|
|
87
|
-
return PRELOAD_HINTS[path] || [];
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
const MAX_PORT_RETRIES = 10;
|
|
91
|
-
|
|
92
|
-
function startServer(initialPort) {
|
|
93
|
-
let port = initialPort;
|
|
94
|
-
|
|
95
|
-
for (let i = 0; i < MAX_PORT_RETRIES; i++) {
|
|
96
|
-
try {
|
|
97
|
-
const server = Bun.serve({
|
|
98
|
-
port: port,
|
|
99
|
-
async fetch(req) {
|
|
100
|
-
const url = new URL(req.url);
|
|
101
|
-
let path = url.pathname;
|
|
102
|
-
|
|
103
|
-
// ===== CACHE MANAGEMENT ENDPOINTS =====
|
|
104
|
-
|
|
105
|
-
// Clear cache endpoint - sends headers to invalidate browser cache
|
|
106
|
-
if (path === '/-/clear-cache') {
|
|
107
|
-
return new Response(JSON.stringify({
|
|
108
|
-
status: 'ok',
|
|
109
|
-
message: 'Cache cleared. Reload the page with Ctrl+Shift+R',
|
|
110
|
-
timestamp: new Date().toISOString()
|
|
111
|
-
}), {
|
|
112
|
-
headers: {
|
|
113
|
-
'Content-Type': 'application/json',
|
|
114
|
-
'Cache-Control': 'no-store, no-cache, must-revalidate',
|
|
115
|
-
'Clear-Site-Data': '"cache", "storage"',
|
|
116
|
-
},
|
|
117
|
-
});
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
// Health check
|
|
121
|
-
if (path === '/-/health') {
|
|
122
|
-
return new Response(JSON.stringify({ status: 'ok', port: server.port }), {
|
|
123
|
-
headers: { 'Content-Type': 'application/json' },
|
|
124
|
-
});
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
// Serve robots.txt inline (Lighthouse has issues with redirects)
|
|
128
|
-
if (path === '/robots.txt') {
|
|
129
|
-
const robotsTxt = `# robots.txt for microUI
|
|
130
|
-
User-agent: *
|
|
131
|
-
Allow: /
|
|
132
|
-
|
|
133
|
-
# Sitemap
|
|
134
|
-
Sitemap: https://microui.dev/sitemap.xml
|
|
135
|
-
`;
|
|
136
|
-
return new Response(robotsTxt, {
|
|
137
|
-
headers: {
|
|
138
|
-
'Content-Type': 'text/plain; charset=utf-8',
|
|
139
|
-
'Cache-Control': 'public, max-age=86400',
|
|
140
|
-
'X-Robots-Tag': 'all',
|
|
141
|
-
},
|
|
142
|
-
});
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
// Default to demo/shell.html for root
|
|
146
|
-
if (path === '/' || path === '/index.html') {
|
|
147
|
-
path = '/demo/shell.html';
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
// Serve shell.html for App Shell pattern
|
|
151
|
-
if (path === '/shell.html') {
|
|
152
|
-
path = '/demo/shell.html';
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
// Serve content fragments for dynamic loading
|
|
156
|
-
if (path.startsWith('/content/')) {
|
|
157
|
-
path = '/demo' + path;
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
// Serve PWA assets from demo/
|
|
161
|
-
if (path === '/sw.js' || path === '/manifest.json' ||
|
|
162
|
-
path === '/favicon.ico' || path === '/favicon.png') {
|
|
163
|
-
path = '/demo' + path;
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
// Security: prevent directory traversal
|
|
167
|
-
if (path.includes('..')) {
|
|
168
|
-
return new Response('Forbidden', { status: 403 });
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
const filePath = '.' + path;
|
|
172
|
-
const file = Bun.file(filePath);
|
|
173
|
-
|
|
174
|
-
if (await file.exists()) {
|
|
175
|
-
const contentType = getContentType(path);
|
|
176
|
-
const cacheControl = getCacheControl(path);
|
|
177
|
-
const acceptEncoding = req.headers.get('accept-encoding') || '';
|
|
178
|
-
const preloadLinks = getPreloadLinks(path);
|
|
179
|
-
|
|
180
|
-
// Build headers
|
|
181
|
-
const headers = {
|
|
182
|
-
'Content-Type': contentType,
|
|
183
|
-
'Cache-Control': cacheControl,
|
|
184
|
-
'X-Content-Type-Options': 'nosniff',
|
|
185
|
-
// Enable bf-cache
|
|
186
|
-
'Vary': 'Accept-Encoding',
|
|
187
|
-
};
|
|
188
|
-
|
|
189
|
-
// Add Link preload headers for HTML
|
|
190
|
-
if (preloadLinks.length > 0) {
|
|
191
|
-
headers['Link'] = preloadLinks.join(', ');
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
// Brotli (priority) or Gzip compression for text assets
|
|
195
|
-
if (shouldCompress(path)) {
|
|
196
|
-
const content = await file.arrayBuffer();
|
|
197
|
-
const rawData = Buffer.from(content);
|
|
198
|
-
|
|
199
|
-
// Prefer Brotli (20-30% better compression than gzip)
|
|
200
|
-
if (acceptEncoding.includes('br')) {
|
|
201
|
-
try {
|
|
202
|
-
const brotliCompressed = brotliCompressSync(rawData, {
|
|
203
|
-
params: {
|
|
204
|
-
[zlibConstants.BROTLI_PARAM_QUALITY]: 6, // Balance speed/compression
|
|
205
|
-
}
|
|
206
|
-
});
|
|
207
|
-
|
|
208
|
-
return new Response(brotliCompressed, {
|
|
209
|
-
headers: {
|
|
210
|
-
...headers,
|
|
211
|
-
'Content-Encoding': 'br',
|
|
212
|
-
},
|
|
213
|
-
});
|
|
214
|
-
} catch {
|
|
215
|
-
// Fallback to gzip if Brotli fails
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
// Fallback to gzip
|
|
220
|
-
if (acceptEncoding.includes('gzip')) {
|
|
221
|
-
const compressed = Bun.gzipSync(new Uint8Array(content));
|
|
222
|
-
return new Response(compressed, {
|
|
223
|
-
headers: {
|
|
224
|
-
...headers,
|
|
225
|
-
'Content-Encoding': 'gzip',
|
|
226
|
-
},
|
|
227
|
-
});
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
return new Response(file, { headers });
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
return new Response('Not Found', { status: 404 });
|
|
235
|
-
},
|
|
236
|
-
});
|
|
237
|
-
|
|
238
|
-
console.log(`๐ microUI Dev Server running at http://localhost:${server.port}`);
|
|
239
|
-
console.log(`๐ฆ Lighthouse-optimized: Cache-Control, Gzip, Link preload`);
|
|
240
|
-
console.log(`๐งน Clear cache: http://localhost:${server.port}/-/clear-cache`);
|
|
241
|
-
return server;
|
|
242
|
-
} catch (e) {
|
|
243
|
-
if (e.code === 'EADDRINUSE') {
|
|
244
|
-
console.warn(`โ ๏ธ Port ${port} is busy, trying ${port + 1}...`);
|
|
245
|
-
port++;
|
|
246
|
-
} else {
|
|
247
|
-
throw e;
|
|
248
|
-
}
|
|
249
|
-
}
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
console.error(`โ Failed to start server after ${MAX_PORT_RETRIES} attempts. Last port tried: ${port}`);
|
|
253
|
-
process.exit(1);
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
startServer(PORT);
|
|
@@ -1,76 +0,0 @@
|
|
|
1
|
-
import { describe, test, expect, beforeAll } from 'bun:test';
|
|
2
|
-
import { parseHTML } from 'linkedom';
|
|
3
|
-
|
|
4
|
-
// Mock Browser Environment
|
|
5
|
-
const { document, customElements, HTMLElement, window } = parseHTML('<!DOCTYPE html><html><body></body></html>');
|
|
6
|
-
globalThis.document = document;
|
|
7
|
-
globalThis.customElements = customElements;
|
|
8
|
-
globalThis.HTMLElement = HTMLElement;
|
|
9
|
-
globalThis.window = window;
|
|
10
|
-
globalThis.requestAnimationFrame = (cb) => cb();
|
|
11
|
-
|
|
12
|
-
describe('Agent Integration Features', () => {
|
|
13
|
-
let MuElement, MuInput, MuButton;
|
|
14
|
-
|
|
15
|
-
beforeAll(async () => {
|
|
16
|
-
// Load Core
|
|
17
|
-
const core = await import('../../src/core/MuElement.js');
|
|
18
|
-
MuElement = core.MuElement;
|
|
19
|
-
|
|
20
|
-
// Load Components
|
|
21
|
-
const inputModule = await import('../../src/components/mu-input.js');
|
|
22
|
-
MuInput = inputModule.MuInput;
|
|
23
|
-
|
|
24
|
-
const buttonModule = await import('../../src/components/mu-button.js');
|
|
25
|
-
MuButton = buttonModule.MuButton;
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
test('should expose window.__MICROUI_ERRORS__', () => {
|
|
29
|
-
// Create an element to trigger init
|
|
30
|
-
const el = new MuInput();
|
|
31
|
-
expect(Array.isArray(window.__MICROUI_ERRORS__)).toBe(true);
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
test('should expose window.microUIAgent helper', () => {
|
|
35
|
-
expect(window.microUIAgent).toBeDefined();
|
|
36
|
-
expect(typeof window.microUIAgent.reset).toBe('function');
|
|
37
|
-
expect(typeof window.microUIAgent.getErrors).toBe('function');
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
test('should log error for invalid button variant', () => {
|
|
41
|
-
window.microUIAgent.reset();
|
|
42
|
-
|
|
43
|
-
const btn = document.createElement('mu-button');
|
|
44
|
-
btn.setAttribute('variant', 'invalid-variant');
|
|
45
|
-
document.body.appendChild(btn); // Render triggers updateClasses -> validation
|
|
46
|
-
|
|
47
|
-
const errors = window.microUIAgent.getErrors();
|
|
48
|
-
expect(errors.length).toBe(1);
|
|
49
|
-
expect(errors[0].code).toBe('INVALID_VARIANT');
|
|
50
|
-
expect(errors[0].component).toBe('mu-button');
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
test('should log error for missing input label', () => {
|
|
54
|
-
window.microUIAgent.reset();
|
|
55
|
-
|
|
56
|
-
const input = document.createElement('mu-input');
|
|
57
|
-
// No label, no aria-label, no placeholder validation logic check?
|
|
58
|
-
// Logic: if (!labelText && !ariaLabel) -> Error
|
|
59
|
-
document.body.appendChild(input);
|
|
60
|
-
|
|
61
|
-
const errors = window.microUIAgent.getErrors();
|
|
62
|
-
expect(errors.length).toBe(1);
|
|
63
|
-
expect(errors[0].code).toBe('A11Y_MISSING_LABEL');
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
test('should NOT log error if label is present', () => {
|
|
67
|
-
window.microUIAgent.reset();
|
|
68
|
-
|
|
69
|
-
const input = document.createElement('mu-input');
|
|
70
|
-
input.setAttribute('label', 'Valid Label');
|
|
71
|
-
document.body.appendChild(input);
|
|
72
|
-
|
|
73
|
-
const errors = window.microUIAgent.getErrors();
|
|
74
|
-
expect(errors.length).toBe(0);
|
|
75
|
-
});
|
|
76
|
-
});
|