microui-wc 0.1.1 → 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.
Files changed (339) hide show
  1. package/AGENTS.md +71 -71
  2. package/CHANGELOG.md +1 -1
  3. package/README.md +14 -9
  4. package/dist/AGENTS.md +71 -71
  5. package/dist/README.md +14 -9
  6. package/dist/microui.css +1 -1
  7. package/dist/microui.esm.js.map +1 -1
  8. package/dist/microui.min.js.map +1 -1
  9. package/docs/getting-started.md +3 -3
  10. package/package.json +39 -11
  11. package/src/components/mu-schema-form.js +1 -1
  12. package/.github/ISSUE_TEMPLATE/bug_report.md +0 -40
  13. package/.github/ISSUE_TEMPLATE/feature_request.md +0 -33
  14. package/.github/PULL_REQUEST_TEMPLATE.md +0 -28
  15. package/.github/workflows/ci.yml +0 -42
  16. package/.github/workflows/deploy-pages.yml +0 -112
  17. package/CODE_OF_CONDUCT.md +0 -59
  18. package/CONTRIBUTING.md +0 -156
  19. package/SECURITY.md +0 -58
  20. package/app/.generated/routes/alerts.js +0 -8
  21. package/app/.generated/routes/avatars.js +0 -8
  22. package/app/.generated/routes/badges.js +0 -8
  23. package/app/.generated/routes/buttons.js +0 -10
  24. package/app/.generated/routes/cards.js +0 -10
  25. package/app/.generated/routes/checkboxes.js +0 -9
  26. package/app/.generated/routes/chips.js +0 -8
  27. package/app/.generated/routes/dropdowns.js +0 -9
  28. package/app/.generated/routes/home.js +0 -7
  29. package/app/.generated/routes/icons.js +0 -9
  30. package/app/.generated/routes/inputs.js +0 -10
  31. package/app/.generated/routes/installation.js +0 -7
  32. package/app/.generated/routes/layout.js +0 -9
  33. package/app/.generated/routes/modals.js +0 -9
  34. package/app/.generated/routes/navbar.js +0 -7
  35. package/app/.generated/routes/progress.js +0 -9
  36. package/app/.generated/routes/radios.js +0 -9
  37. package/app/.generated/routes/switches.js +0 -9
  38. package/app/.generated/routes/tabs.js +0 -8
  39. package/app/.generated/routes/toasts.js +0 -9
  40. package/app/index.html +0 -67
  41. package/app/pages/alerts.html +0 -23
  42. package/app/pages/avatars.html +0 -22
  43. package/app/pages/badges.html +0 -22
  44. package/app/pages/buttons.html +0 -71
  45. package/app/pages/cards.html +0 -54
  46. package/app/pages/checkboxes.html +0 -39
  47. package/app/pages/chips.html +0 -23
  48. package/app/pages/dropdowns.html +0 -41
  49. package/app/pages/home.html +0 -59
  50. package/app/pages/icons.html +0 -29
  51. package/app/pages/inputs.html +0 -66
  52. package/app/pages/installation.html +0 -34
  53. package/app/pages/layout.html +0 -30
  54. package/app/pages/modals.html +0 -21
  55. package/app/pages/navbar.html +0 -22
  56. package/app/pages/progress.html +0 -35
  57. package/app/pages/radios.html +0 -40
  58. package/app/pages/switches.html +0 -39
  59. package/app/pages/tabs.html +0 -30
  60. package/app/pages/toasts.html +0 -22
  61. package/app-dist/index.html +0 -67
  62. package/app-dist/pages/alerts.html +0 -23
  63. package/app-dist/pages/avatars.html +0 -22
  64. package/app-dist/pages/badges.html +0 -22
  65. package/app-dist/pages/buttons.html +0 -71
  66. package/app-dist/pages/cards.html +0 -54
  67. package/app-dist/pages/checkboxes.html +0 -39
  68. package/app-dist/pages/chips.html +0 -23
  69. package/app-dist/pages/dropdowns.html +0 -41
  70. package/app-dist/pages/home.html +0 -59
  71. package/app-dist/pages/icons.html +0 -29
  72. package/app-dist/pages/inputs.html +0 -66
  73. package/app-dist/pages/installation.html +0 -34
  74. package/app-dist/pages/layout.html +0 -30
  75. package/app-dist/pages/modals.html +0 -21
  76. package/app-dist/pages/navbar.html +0 -22
  77. package/app-dist/pages/progress.html +0 -35
  78. package/app-dist/pages/radios.html +0 -40
  79. package/app-dist/pages/switches.html +0 -39
  80. package/app-dist/pages/tabs.html +0 -30
  81. package/app-dist/pages/toasts.html +0 -22
  82. package/app-dist/pages.json +0 -217
  83. package/app-dist/routes/alerts.js +0 -5
  84. package/app-dist/routes/avatars.js +0 -1
  85. package/app-dist/routes/badges.js +0 -1
  86. package/app-dist/routes/buttons.js +0 -1
  87. package/app-dist/routes/cards.js +0 -1
  88. package/app-dist/routes/checkboxes.js +0 -9
  89. package/app-dist/routes/chips.js +0 -4
  90. package/app-dist/routes/chunk-019e5e2f.js +0 -5
  91. package/app-dist/routes/chunk-0m4j19yd.js +0 -2
  92. package/app-dist/routes/chunk-0tmmp5q0.js +0 -1
  93. package/app-dist/routes/chunk-10xn709r.js +0 -1
  94. package/app-dist/routes/chunk-15m2qcda.js +0 -2
  95. package/app-dist/routes/chunk-1bh8g23n.js +0 -1
  96. package/app-dist/routes/chunk-1vg0v937.js +0 -1
  97. package/app-dist/routes/chunk-1zvcgy3j.js +0 -1
  98. package/app-dist/routes/chunk-2afb0861.js +0 -1
  99. package/app-dist/routes/chunk-2c6ttpzt.js +0 -5
  100. package/app-dist/routes/chunk-3dy30fhs.js +0 -1
  101. package/app-dist/routes/chunk-426dnces.js +0 -13
  102. package/app-dist/routes/chunk-44kgxery.js +0 -1
  103. package/app-dist/routes/chunk-47fdnejd.js +0 -33
  104. package/app-dist/routes/chunk-49a6t2vq.js +0 -1
  105. package/app-dist/routes/chunk-4fe1rm5b.js +0 -1
  106. package/app-dist/routes/chunk-4ggmvkta.js +0 -33
  107. package/app-dist/routes/chunk-4vkz81q7.js +0 -33
  108. package/app-dist/routes/chunk-4w4tmj8f.js +0 -31
  109. package/app-dist/routes/chunk-532s62kr.js +0 -31
  110. package/app-dist/routes/chunk-5hm3bssy.js +0 -33
  111. package/app-dist/routes/chunk-5vrh24hc.js +0 -1
  112. package/app-dist/routes/chunk-61pcg25a.js +0 -1
  113. package/app-dist/routes/chunk-6nfhygvf.js +0 -1
  114. package/app-dist/routes/chunk-700e7je6.js +0 -33
  115. package/app-dist/routes/chunk-7fsn17kg.js +0 -1
  116. package/app-dist/routes/chunk-7k789b32.js +0 -1
  117. package/app-dist/routes/chunk-7r46q0ys.js +0 -36
  118. package/app-dist/routes/chunk-86fmc1fr.js +0 -5
  119. package/app-dist/routes/chunk-8qth37vw.js +0 -1
  120. package/app-dist/routes/chunk-924wv8n0.js +0 -1
  121. package/app-dist/routes/chunk-9mbhgxk9.js +0 -1
  122. package/app-dist/routes/chunk-a216hyd9.js +0 -1
  123. package/app-dist/routes/chunk-akzxykh9.js +0 -33
  124. package/app-dist/routes/chunk-b3dcvy8c.js +0 -1
  125. package/app-dist/routes/chunk-b74zahz5.js +0 -31
  126. package/app-dist/routes/chunk-bftj53p2.js +0 -5
  127. package/app-dist/routes/chunk-c01hnz3e.js +0 -1
  128. package/app-dist/routes/chunk-d8pvv5km.js +0 -1
  129. package/app-dist/routes/chunk-dev0aezr.js +0 -2
  130. package/app-dist/routes/chunk-dh6vnv0e.js +0 -1
  131. package/app-dist/routes/chunk-dn2cbpva.js +0 -36
  132. package/app-dist/routes/chunk-dvn0my90.js +0 -1
  133. package/app-dist/routes/chunk-dvq8mnve.js +0 -36
  134. package/app-dist/routes/chunk-e8c2gc4d.js +0 -5
  135. package/app-dist/routes/chunk-ejf9ak2x.js +0 -1
  136. package/app-dist/routes/chunk-f083m55s.js +0 -1
  137. package/app-dist/routes/chunk-fnrj28s1.js +0 -31
  138. package/app-dist/routes/chunk-fvg3yjdp.js +0 -31
  139. package/app-dist/routes/chunk-g7k381n1.js +0 -1
  140. package/app-dist/routes/chunk-h01kq2ae.js +0 -13
  141. package/app-dist/routes/chunk-h4dk761v.js +0 -5
  142. package/app-dist/routes/chunk-hmx91z2x.js +0 -5
  143. package/app-dist/routes/chunk-hxbg4m42.js +0 -36
  144. package/app-dist/routes/chunk-jbjnfp2b.js +0 -2
  145. package/app-dist/routes/chunk-jxtz5vv6.js +0 -36
  146. package/app-dist/routes/chunk-jxzcs0ey.js +0 -36
  147. package/app-dist/routes/chunk-kt7wwhcx.js +0 -1
  148. package/app-dist/routes/chunk-kzptszyc.js +0 -33
  149. package/app-dist/routes/chunk-mhgca4w4.js +0 -2
  150. package/app-dist/routes/chunk-mhswxa20.js +0 -1
  151. package/app-dist/routes/chunk-n8zfeex6.js +0 -1
  152. package/app-dist/routes/chunk-pee47b2r.js +0 -1
  153. package/app-dist/routes/chunk-pesmw829.js +0 -1
  154. package/app-dist/routes/chunk-pgc4c6f3.js +0 -36
  155. package/app-dist/routes/chunk-q8egegm1.js +0 -1
  156. package/app-dist/routes/chunk-q9mn2qyq.js +0 -36
  157. package/app-dist/routes/chunk-qh0rtaf3.js +0 -5
  158. package/app-dist/routes/chunk-qqhmk6ye.js +0 -2
  159. package/app-dist/routes/chunk-qrxygmf7.js +0 -33
  160. package/app-dist/routes/chunk-r46yzksx.js +0 -36
  161. package/app-dist/routes/chunk-rgpbw2w0.js +0 -5
  162. package/app-dist/routes/chunk-rnpzv3d8.js +0 -2
  163. package/app-dist/routes/chunk-s5v8cv05.js +0 -2
  164. package/app-dist/routes/chunk-sbwn5bpc.js +0 -1
  165. package/app-dist/routes/chunk-sqbg8jbt.js +0 -33
  166. package/app-dist/routes/chunk-sv8dqnf7.js +0 -1
  167. package/app-dist/routes/chunk-t67sw3za.js +0 -1
  168. package/app-dist/routes/chunk-tjdpqwdf.js +0 -31
  169. package/app-dist/routes/chunk-tq2mfghg.js +0 -1
  170. package/app-dist/routes/chunk-ttn10vt6.js +0 -1
  171. package/app-dist/routes/chunk-v2hzpjxr.js +0 -1
  172. package/app-dist/routes/chunk-wfjjkw9y.js +0 -1
  173. package/app-dist/routes/chunk-wt8cxzmf.js +0 -31
  174. package/app-dist/routes/chunk-x45d372k.js +0 -5
  175. package/app-dist/routes/chunk-y3wsazkt.js +0 -1
  176. package/app-dist/routes/chunk-y7pmgc7t.js +0 -33
  177. package/app-dist/routes/chunk-zefdt2q3.js +0 -31
  178. package/app-dist/routes/dropdowns.js +0 -6
  179. package/app-dist/routes/home.js +0 -1
  180. package/app-dist/routes/icons.js +0 -1
  181. package/app-dist/routes/inputs.js +0 -12
  182. package/app-dist/routes/installation.js +0 -1
  183. package/app-dist/routes/layout.js +0 -1
  184. package/app-dist/routes/modals.js +0 -7
  185. package/app-dist/routes/navbar.js +0 -1
  186. package/app-dist/routes/progress.js +0 -1
  187. package/app-dist/routes/radios.js +0 -6
  188. package/app-dist/routes/switches.js +0 -6
  189. package/app-dist/routes/tabs.js +0 -1
  190. package/app-dist/routes/toasts.js +0 -16
  191. package/assets/fonts/material-symbols-mini.woff2 +0 -0
  192. package/assets/fonts/material-symbols.woff2 +0 -0
  193. package/assets/fonts/roboto-400.woff2 +0 -0
  194. package/assets/fonts/roboto-500.woff2 +0 -0
  195. package/assets/fonts/roboto-700.woff2 +0 -0
  196. package/assets/logo-banner-400.jpg +0 -0
  197. package/assets/logo-banner-400.webp +0 -0
  198. package/assets/logo-banner-800.webp +0 -0
  199. package/assets/logo-banner.jpg +0 -0
  200. package/assets/logo-icon-64.jpg +0 -0
  201. package/assets/logo-icon-64.webp +0 -0
  202. package/assets/logo-icon.jpg +0 -0
  203. package/assets/logo-square.jpg +0 -0
  204. package/bun.lock +0 -312
  205. package/bunfig.toml +0 -4
  206. package/custom-elements.json +0 -1916
  207. package/demo/api/sample-data.json +0 -38
  208. package/demo/content/alerts.html +0 -115
  209. package/demo/content/avatars.html +0 -70
  210. package/demo/content/badges.html +0 -65
  211. package/demo/content/buttons.html +0 -188
  212. package/demo/content/callouts.html +0 -91
  213. package/demo/content/cards.html +0 -121
  214. package/demo/content/checkboxes.html +0 -178
  215. package/demo/content/chips.html +0 -67
  216. package/demo/content/codeblocks.html +0 -101
  217. package/demo/content/confirms.html +0 -115
  218. package/demo/content/datatables.html +0 -149
  219. package/demo/content/dividers.html +0 -119
  220. package/demo/content/dropdowns.html +0 -89
  221. package/demo/content/enterprise.html +0 -252
  222. package/demo/content/home.html +0 -149
  223. package/demo/content/icons.html +0 -89
  224. package/demo/content/inputs.html +0 -135
  225. package/demo/content/installation.html +0 -16
  226. package/demo/content/layout.html +0 -136
  227. package/demo/content/modals.html +0 -141
  228. package/demo/content/navbar.html +0 -70
  229. package/demo/content/progress.html +0 -119
  230. package/demo/content/radios.html +0 -88
  231. package/demo/content/skeletons.html +0 -109
  232. package/demo/content/spinners.html +0 -96
  233. package/demo/content/switches.html +0 -84
  234. package/demo/content/tables.html +0 -124
  235. package/demo/content/tabs.html +0 -85
  236. package/demo/content/toasts.html +0 -116
  237. package/demo/content/tooltips.html +0 -107
  238. package/demo/content/virtual-lists.html +0 -233
  239. package/demo/favicon.ico +0 -0
  240. package/demo/favicon.png +0 -0
  241. package/demo/full.html +0 -52
  242. package/demo/iife.html +0 -46
  243. package/demo/manifest.json +0 -34
  244. package/demo/pages/datatable-demo.html +0 -237
  245. package/demo/pages/prompt-ui-demo.html +0 -218
  246. package/demo/pages/responsive-demo.html +0 -122
  247. package/demo/pages/schema-form-demo.html +0 -270
  248. package/demo/robots.txt +0 -6
  249. package/demo/shell.html +0 -712
  250. package/demo/sw.js +0 -387
  251. package/lighthouse-audit.mjs +0 -113
  252. package/scripts/analyze-components.js +0 -105
  253. package/scripts/build-app.js +0 -193
  254. package/scripts/build-framework.js +0 -444
  255. package/scripts/build-utils.js +0 -101
  256. package/scripts/test-isolated.js +0 -151
  257. package/server.js +0 -256
  258. package/tests/agents/agent-integration.test.js +0 -76
  259. package/tests/benchmark.html +0 -296
  260. package/tests/build/scan-components.test.js +0 -173
  261. package/tests/components/all-components.test.js +0 -245
  262. package/tests/components/all-missing-components.test.js +0 -574
  263. package/tests/components/mu-alert.test.js +0 -113
  264. package/tests/components/mu-avatar.test.js +0 -148
  265. package/tests/components/mu-badge.test.js +0 -92
  266. package/tests/components/mu-button.test.js +0 -112
  267. package/tests/components/mu-card.test.js +0 -89
  268. package/tests/components/mu-checkbox.test.js +0 -158
  269. package/tests/components/mu-chip.test.js +0 -118
  270. package/tests/components/mu-container.test.js +0 -120
  271. package/tests/components/mu-divider.test.js +0 -98
  272. package/tests/components/mu-drawer-item.test.js +0 -199
  273. package/tests/components/mu-drawer.test.js +0 -96
  274. package/tests/components/mu-dropdown.test.js +0 -125
  275. package/tests/components/mu-form.test.js +0 -138
  276. package/tests/components/mu-grid.test.js +0 -135
  277. package/tests/components/mu-icon.test.js +0 -110
  278. package/tests/components/mu-input.test.js +0 -131
  279. package/tests/components/mu-lazy.test.js +0 -103
  280. package/tests/components/mu-modal.test.js +0 -275
  281. package/tests/components/mu-navbar.test.js +0 -101
  282. package/tests/components/mu-progress.test.js +0 -115
  283. package/tests/components/mu-radio.test.js +0 -114
  284. package/tests/components/mu-repeat.test.js +0 -106
  285. package/tests/components/mu-sidebar.test.js +0 -126
  286. package/tests/components/mu-skeleton.test.js +0 -162
  287. package/tests/components/mu-stack.test.js +0 -143
  288. package/tests/components/mu-switch.test.js +0 -292
  289. package/tests/components/mu-table.test.js +0 -124
  290. package/tests/components/mu-tabs.test.js +0 -104
  291. package/tests/components/mu-textarea.test.js +0 -115
  292. package/tests/components/mu-toast.test.js +0 -321
  293. package/tests/components/mu-tooltip.test.js +0 -133
  294. package/tests/components/mu-virtual-list.test.js +0 -109
  295. package/tests/core/MuElement.test.js +0 -120
  296. package/tests/core/agent-api.test.js +0 -125
  297. package/tests/core/all-core-modules.test.js +0 -442
  298. package/tests/core/bus.test.js +0 -364
  299. package/tests/core/component-schema.test.js +0 -160
  300. package/tests/core/feature-registry.test.js +0 -198
  301. package/tests/core/form-state.test.js +0 -167
  302. package/tests/core/http.test.js +0 -119
  303. package/tests/core/keyboard.test.js +0 -319
  304. package/tests/core/layers.test.js +0 -129
  305. package/tests/core/namespaced-stores.test.js +0 -114
  306. package/tests/core/render.test.js +0 -121
  307. package/tests/core/ripple.test.js +0 -131
  308. package/tests/core/router.test.js +0 -89
  309. package/tests/core/scheduler.test.js +0 -121
  310. package/tests/core/signals.test.js +0 -128
  311. package/tests/core/store.test.js +0 -171
  312. package/tests/core/transitions.test.js +0 -82
  313. package/tests/e2e/accessibility-harness.html +0 -58
  314. package/tests/e2e/accessibility.test.js +0 -401
  315. package/tests/e2e/agent-features.test.js +0 -372
  316. package/tests/e2e/card-spacing.test.js +0 -287
  317. package/tests/e2e/components.test.js +0 -439
  318. package/tests/e2e/demo-routes.test.js +0 -478
  319. package/tests/e2e/layout-css-fallback.test.js +0 -334
  320. package/tests/e2e/mu-alert.e2e.test.js +0 -111
  321. package/tests/e2e/mu-checkbox.test.js +0 -489
  322. package/tests/e2e/mu-chip.test.js +0 -347
  323. package/tests/e2e/mu-form.test.js +0 -499
  324. package/tests/e2e/mu-icon.test.js +0 -114
  325. package/tests/e2e/mu-radio.test.js +0 -113
  326. package/tests/e2e/mu-skeleton.test.js +0 -140
  327. package/tests/e2e/mu-switch.test.js +0 -415
  328. package/tests/e2e/mu-tabs.test.js +0 -494
  329. package/tests/e2e/mu-textarea.test.js +0 -242
  330. package/tests/e2e/mu-virtual-list.test.js +0 -427
  331. package/tests/e2e/perf-memory.test.js +0 -161
  332. package/tests/e2e/puppeteer-helper.js +0 -137
  333. package/tests/e2e/puppeteer.test.js +0 -226
  334. package/tests/e2e/pwa.test.js +0 -261
  335. package/tests/e2e/test-harness.html +0 -319
  336. package/tests/manual/test-components.html +0 -120
  337. package/tests/memory-test.html +0 -309
  338. package/tests/setup-dom.js +0 -93
  339. package/tests/visual-test.html +0 -301
@@ -1,242 +0,0 @@
1
- /**
2
- * @fileoverview Comprehensive E2E Tests for mu-textarea Component
3
- */
4
-
5
- import { describe, test, expect, beforeAll, afterEach, afterAll } from 'bun:test';
6
- import { launchBrowser, puppeteer } from './puppeteer-helper.js';
7
- import { dirname, join } from 'path';
8
- import { fileURLToPath } from 'url';
9
-
10
- const __filename = fileURLToPath(import.meta.url);
11
- const __dirname = dirname(__filename);
12
- const delay = (ms) => new Promise(resolve => setTimeout(resolve, ms));
13
-
14
- describe('mu-textarea E2E Tests', () => {
15
- let browser, page, server;
16
-
17
- beforeAll(async () => {
18
- const projectRoot = join(__dirname, '../..');
19
- server = Bun.serve({
20
- port: 0,
21
- async fetch(req) {
22
- const url = new URL(req.url);
23
- let filePath = url.pathname === '/' ? join(projectRoot, 'demo/shell.html') : join(projectRoot, url.pathname);
24
- try {
25
- const file = Bun.file(filePath);
26
- if (!await file.exists()) return new Response('Not Found', { status: 404 });
27
- const ext = filePath.split('.').pop();
28
- const types = { 'html': 'text/html', 'js': 'text/javascript', 'css': 'text/css', 'json': 'application/json', 'svg': 'image/svg+xml' };
29
- return new Response(await file.arrayBuffer(), { headers: { 'Content-Type': types[ext] || 'application/octet-stream' } });
30
- } catch (e) { return new Response('Error', { status: 500 }); }
31
- }
32
- });
33
- console.log(`[mu-textarea test] Server at http://localhost:${server.port}`);
34
- browser = await puppeteer.launch({
35
- headless: true,
36
- executablePath: '/tmp/puppeteer/chrome-headless-shell/mac_arm-144.0.7559.96/chrome-headless-shell-mac-arm64/chrome-headless-shell',
37
- args: ['--no-sandbox', '--disable-setuid-sandbox'],
38
- userDataDir: `./.tmp/puppeteer-mu_textarea-${Date.now()}`
39
- });
40
- page = await browser.newPage();
41
- }, 60000);
42
-
43
- afterEach(async () => { await page.goto('about:blank'); });
44
- afterAll(async () => { if (browser) await browser.close(); if (server) server.stop(); });
45
-
46
- test('should be registered as custom element', async () => {
47
- await page.goto(`http://localhost:${server.port}/demo/shell.html#inputs`, { waitUntil: 'networkidle0' });
48
- await delay(500);
49
- const isRegistered = await page.evaluate(() => customElements.get('mu-textarea') !== undefined);
50
- expect(isRegistered).toBe(true);
51
- });
52
-
53
- test('should render with base class', async () => {
54
- await page.goto(`http://localhost:${server.port}/demo/shell.html#inputs`, { waitUntil: 'networkidle0' });
55
- await delay(500);
56
- const hasClass = await page.evaluate(() => document.querySelector('mu-textarea')?.classList.contains('mu-textarea'));
57
- expect(hasClass).toBe(true);
58
- });
59
-
60
- test('should render native textarea element', async () => {
61
- await page.goto(`http://localhost:${server.port}/demo/shell.html#inputs`, { waitUntil: 'networkidle0' });
62
- await delay(500);
63
- const hasTextarea = await page.evaluate(() => document.querySelector('mu-textarea textarea') !== null);
64
- expect(hasTextarea).toBe(true);
65
- });
66
-
67
- test('should have placeholder attribute forwarded', async () => {
68
- await page.goto(`http://localhost:${server.port}/demo/shell.html#inputs`, { waitUntil: 'networkidle0' });
69
- await delay(500);
70
- const placeholder = await page.evaluate(() => {
71
- const ta = document.querySelector('mu-textarea[placeholder] textarea');
72
- return ta?.placeholder;
73
- });
74
- expect(placeholder).toBeTruthy();
75
- });
76
-
77
- test('should support value getter/setter', async () => {
78
- await page.goto(`http://localhost:${server.port}/demo/shell.html#inputs`, { waitUntil: 'networkidle0' });
79
- await delay(500);
80
- const result = await page.evaluate(() => {
81
- const ta = document.querySelector('mu-textarea');
82
- const initial = ta.value;
83
- ta.value = 'Test content';
84
- const afterSet = ta.value;
85
- return { initial, afterSet };
86
- });
87
- expect(result.afterSet).toBe('Test content');
88
- });
89
-
90
- test('should emit mu-input event on input', async () => {
91
- await page.goto(`http://localhost:${server.port}/demo/shell.html#inputs`, { waitUntil: 'networkidle0' });
92
- await delay(500);
93
- const result = await page.evaluate(() => {
94
- return new Promise(resolve => {
95
- const ta = document.querySelector('mu-textarea');
96
- let fired = false;
97
- ta.addEventListener('mu-input', () => { fired = true; });
98
- const native = ta.querySelector('textarea');
99
- native.value = 'test';
100
- native.dispatchEvent(new Event('input', { bubbles: true }));
101
- setTimeout(() => resolve({ fired }), 100);
102
- });
103
- });
104
- expect(result.fired).toBe(true);
105
- });
106
-
107
- test('should emit mu-change event on change', async () => {
108
- await page.goto(`http://localhost:${server.port}/demo/shell.html#inputs`, { waitUntil: 'networkidle0' });
109
- await delay(500);
110
- const result = await page.evaluate(() => {
111
- return new Promise(resolve => {
112
- const ta = document.querySelector('mu-textarea');
113
- let fired = false;
114
- ta.addEventListener('mu-change', () => { fired = true; });
115
- const native = ta.querySelector('textarea');
116
- native.value = 'test';
117
- native.dispatchEvent(new Event('change', { bubbles: true }));
118
- setTimeout(() => resolve({ fired }), 100);
119
- });
120
- });
121
- expect(result.fired).toBe(true);
122
- });
123
-
124
- test('should have display block', async () => {
125
- await page.goto(`http://localhost:${server.port}/demo/shell.html#inputs`, { waitUntil: 'networkidle0' });
126
- await delay(500);
127
- const display = await page.evaluate(() => getComputedStyle(document.querySelector('mu-textarea')).display);
128
- expect(display).toBe('block');
129
- });
130
-
131
- test('should have focus method', async () => {
132
- await page.goto(`http://localhost:${server.port}/demo/shell.html#inputs`, { waitUntil: 'networkidle0' });
133
- await delay(500);
134
- const hasFocus = await page.evaluate(() => typeof document.querySelector('mu-textarea').focus === 'function');
135
- expect(hasFocus).toBe(true);
136
- });
137
-
138
- // ========================================
139
- // ROWS ATTRIBUTE (was skipped in unit tests)
140
- // Note: mu-textarea applies rows at render time only
141
- // ========================================
142
-
143
- test('should support rows attribute at render time', async () => {
144
- await page.goto(`http://localhost:${server.port}/demo/shell.html#inputs`, { waitUntil: 'networkidle0' });
145
- await delay(500);
146
- const result = await page.evaluate(() => {
147
- // Create a new mu-textarea with rows attribute
148
- const container = document.createElement('div');
149
- container.innerHTML = '<mu-textarea rows="8" placeholder="Test rows"></mu-textarea>';
150
- document.body.appendChild(container);
151
-
152
- // Wait for render
153
- return new Promise(resolve => {
154
- setTimeout(() => {
155
- const ta = container.querySelector('mu-textarea');
156
- const native = ta?.querySelector('textarea');
157
- resolve({
158
- hasAttr: ta?.getAttribute('rows') === '8',
159
- nativeRows: native?.rows || native?.getAttribute('rows'),
160
- rowsApplied: native?.rows === 8 || native?.getAttribute('rows') === '8'
161
- });
162
- }, 200);
163
- });
164
- });
165
- expect(result.hasAttr).toBe(true);
166
- expect(result.rowsApplied).toBe(true);
167
- });
168
-
169
- test('should reflect rows attribute to native textarea', async () => {
170
- await page.goto(`http://localhost:${server.port}/demo/shell.html#inputs`, { waitUntil: 'networkidle0' });
171
- await delay(500);
172
- const result = await page.evaluate(() => {
173
- const ta = document.querySelector('mu-textarea');
174
- const native = ta.querySelector('textarea');
175
- return {
176
- hasTextarea: native !== null,
177
- defaultRows: native?.rows || null
178
- };
179
- });
180
- expect(result.hasTextarea).toBe(true);
181
- // Default rows should be a number > 0
182
- expect(result.defaultRows).toBeGreaterThan(0);
183
- });
184
-
185
- // ========================================
186
- // READONLY ATTRIBUTE (was skipped in unit tests)
187
- // Note: mu-textarea applies readonly at render time only
188
- // ========================================
189
-
190
- test('should support readonly attribute at render time', async () => {
191
- await page.goto(`http://localhost:${server.port}/demo/shell.html#inputs`, { waitUntil: 'networkidle0' });
192
- await delay(500);
193
- const result = await page.evaluate(() => {
194
- // Create a new mu-textarea with readonly attribute
195
- const container = document.createElement('div');
196
- container.innerHTML = '<mu-textarea readonly placeholder="Readonly test"></mu-textarea>';
197
- document.body.appendChild(container);
198
-
199
- // Wait for render
200
- return new Promise(resolve => {
201
- setTimeout(() => {
202
- const ta = container.querySelector('mu-textarea');
203
- const native = ta?.querySelector('textarea');
204
- resolve({
205
- hasAttr: ta?.hasAttribute('readonly'),
206
- isReadOnly: native?.readOnly || native?.hasAttribute('readonly')
207
- });
208
- }, 200);
209
- });
210
- });
211
- expect(result.hasAttr).toBe(true);
212
- expect(result.isReadOnly).toBe(true);
213
- });
214
-
215
- test('readonly textarea should have readonly native element', async () => {
216
- await page.goto(`http://localhost:${server.port}/demo/shell.html#inputs`, { waitUntil: 'networkidle0' });
217
- await delay(500);
218
- const result = await page.evaluate(() => {
219
- // Find existing textarea with readonly or create one
220
- let ta = document.querySelector('mu-textarea[readonly]');
221
- if (!ta) {
222
- const container = document.createElement('div');
223
- container.innerHTML = '<mu-textarea readonly placeholder="Test readonly"></mu-textarea>';
224
- document.body.appendChild(container);
225
- ta = container.querySelector('mu-textarea');
226
- }
227
-
228
- return new Promise(resolve => {
229
- setTimeout(() => {
230
- const native = ta?.querySelector('textarea');
231
- resolve({
232
- hasNative: native !== null,
233
- isReadOnly: native?.readOnly || native?.hasAttribute('readonly'),
234
- hasAttr: ta?.hasAttribute('readonly')
235
- });
236
- }, 200);
237
- });
238
- });
239
- expect(result.hasNative).toBe(true);
240
- expect(result.hasAttr).toBe(true);
241
- });
242
- });
@@ -1,427 +0,0 @@
1
- /**
2
- * @fileoverview Comprehensive E2E Tests for mu-virtual-list Component
3
- *
4
- * Tests:
5
- * - Component registration and basic rendering
6
- * - Virtual scrolling with large datasets
7
- * - Item rendering and visibility
8
- * - scrollToIndex method
9
- * - Performance with large lists
10
- */
11
-
12
- import { describe, test, expect, beforeAll, afterEach, afterAll } from 'bun:test';
13
- import { launchBrowser, puppeteer } from './puppeteer-helper.js';
14
- import { fileURLToPath } from 'url';
15
- import { dirname, join } from 'path';
16
-
17
- const __filename = fileURLToPath(import.meta.url);
18
- const __dirname = dirname(__filename);
19
-
20
- const delay = (ms) => new Promise(resolve => setTimeout(resolve, ms));
21
-
22
- describe('mu-virtual-list E2E Tests', () => {
23
- let browser;
24
- let page;
25
- let server;
26
-
27
- beforeAll(async () => {
28
- const projectRoot = join(__dirname, '../..');
29
-
30
- server = Bun.serve({
31
- port: 0,
32
- async fetch(req) {
33
- const url = new URL(req.url);
34
- let filePath = join(projectRoot, url.pathname);
35
-
36
- if (url.pathname === '/') {
37
- filePath = join(projectRoot, 'demo/shell.html');
38
- }
39
-
40
- // Serve the virtual-list test harness
41
- if (url.pathname === '/test-virtual-list.html') {
42
- return new Response(`
43
- <!DOCTYPE html>
44
- <html>
45
- <head>
46
- <title>Virtual List Test</title>
47
- <script type="module" src="/dist/microui.esm.js"></script>
48
- <style>
49
- #test-list {
50
- height: 400px;
51
- border: 1px solid #ccc;
52
- }
53
- </style>
54
- </head>
55
- <body>
56
- <mu-virtual-list id="test-list" item-height="50" buffer="5"></mu-virtual-list>
57
- <script type="module">
58
- const list = document.getElementById('test-list');
59
- // Create 1000 items
60
- const items = Array.from({ length: 1000 }, (_, i) => ({
61
- id: i,
62
- name: 'Item ' + i
63
- }));
64
- list.items = items;
65
- list.renderItem = (item) => '<div class="item">Item ' + item.id + ': ' + item.name + '</div>';
66
- </script>
67
- </body>
68
- </html>
69
- `, { headers: { 'Content-Type': 'text/html' } });
70
- }
71
-
72
- try {
73
- const file = Bun.file(filePath);
74
- if (!await file.exists()) {
75
- return new Response('Not Found', { status: 404 });
76
- }
77
-
78
- const content = await file.arrayBuffer();
79
- const ext = filePath.split('.').pop();
80
- const mimeTypes = {
81
- 'html': 'text/html',
82
- 'js': 'text/javascript',
83
- 'css': 'text/css',
84
- 'json': 'application/json',
85
- 'svg': 'image/svg+xml'
86
- };
87
-
88
- return new Response(content, {
89
- headers: { 'Content-Type': mimeTypes[ext] || 'application/octet-stream' }
90
- });
91
- } catch (e) {
92
- return new Response('Error: ' + e.message, { status: 500 });
93
- }
94
- }
95
- });
96
-
97
- console.log(`[mu-virtual-list test] Server started at http://localhost:${server.port}`);
98
-
99
- browser = await puppeteer.launch({
100
- headless: true,
101
- executablePath: '/tmp/puppeteer/chrome-headless-shell/mac_arm-144.0.7559.96/chrome-headless-shell-mac-arm64/chrome-headless-shell',
102
- args: ['--no-sandbox', '--disable-setuid-sandbox'],
103
- userDataDir: `./.tmp/puppeteer-mu_virtual_list-${Date.now()}-${Math.random().toString(36).slice(2)}`
104
- });
105
- page = await browser.newPage();
106
- }, 60000);
107
-
108
- afterEach(async () => {
109
- await page.goto('about:blank');
110
- });
111
-
112
- afterAll(async () => {
113
- if (browser) await browser.close();
114
- if (server) server.stop();
115
- });
116
-
117
- // ========================================
118
- // COMPONENT REGISTRATION
119
- // ========================================
120
-
121
- test('should be registered as custom element', async () => {
122
- await page.goto(`http://localhost:${server.port}/test-virtual-list.html`, {
123
- waitUntil: 'networkidle0'
124
- });
125
- await delay(500);
126
-
127
- const isRegistered = await page.evaluate(() =>
128
- customElements.get('mu-virtual-list') !== undefined
129
- );
130
-
131
- expect(isRegistered).toBe(true);
132
- });
133
-
134
- test('should render with base class', async () => {
135
- await page.goto(`http://localhost:${server.port}/test-virtual-list.html`, {
136
- waitUntil: 'networkidle0'
137
- });
138
- await delay(500);
139
-
140
- const hasBaseClass = await page.evaluate(() => {
141
- const el = document.querySelector('mu-virtual-list');
142
- return el?.classList.contains('mu-virtual-list');
143
- });
144
-
145
- expect(hasBaseClass).toBe(true);
146
- });
147
-
148
- // ========================================
149
- // VIEWPORT AND CONTAINER STRUCTURE
150
- // ========================================
151
-
152
- test('should render viewport container', async () => {
153
- await page.goto(`http://localhost:${server.port}/test-virtual-list.html`, {
154
- waitUntil: 'networkidle0'
155
- });
156
- await delay(500);
157
-
158
- const result = await page.evaluate(() => {
159
- const el = document.querySelector('mu-virtual-list');
160
- const viewport = el?.querySelector('.mu-virtual-list__viewport');
161
- return {
162
- hasViewport: viewport !== null,
163
- overflowY: viewport ? getComputedStyle(viewport).overflowY : null
164
- };
165
- });
166
-
167
- expect(result.hasViewport).toBe(true);
168
- expect(result.overflowY).toBe('auto');
169
- });
170
-
171
- test('should render content container with correct total height', async () => {
172
- await page.goto(`http://localhost:${server.port}/test-virtual-list.html`, {
173
- waitUntil: 'networkidle0'
174
- });
175
- await delay(500);
176
-
177
- const result = await page.evaluate(() => {
178
- const el = document.querySelector('mu-virtual-list');
179
- const content = el?.querySelector('.mu-virtual-list__content');
180
- const height = content ? parseInt(content.style.height) : 0;
181
- // 1000 items * 50px = 50000px
182
- return { height, expected: 1000 * 50 };
183
- });
184
-
185
- expect(result.height).toBe(result.expected);
186
- });
187
-
188
- // ========================================
189
- // VIRTUAL RENDERING
190
- // ========================================
191
-
192
- test('should only render visible items (virtual scrolling)', async () => {
193
- await page.goto(`http://localhost:${server.port}/test-virtual-list.html`, {
194
- waitUntil: 'networkidle0'
195
- });
196
- await delay(500);
197
-
198
- const result = await page.evaluate(() => {
199
- const el = document.querySelector('mu-virtual-list');
200
- const items = el?.querySelectorAll('.mu-virtual-list__item');
201
- // With 400px height, 50px items, and buffer of 5, should have ~18-20 items
202
- return {
203
- renderedCount: items?.length || 0,
204
- totalItems: 1000
205
- };
206
- });
207
-
208
- // Should NOT render all 1000 items
209
- expect(result.renderedCount).toBeLessThan(50);
210
- expect(result.renderedCount).toBeGreaterThan(0);
211
- });
212
-
213
- test('should render items with correct positioning', async () => {
214
- await page.goto(`http://localhost:${server.port}/test-virtual-list.html`, {
215
- waitUntil: 'networkidle0'
216
- });
217
- await delay(500);
218
-
219
- const result = await page.evaluate(() => {
220
- const el = document.querySelector('mu-virtual-list');
221
- const items = el?.querySelectorAll('.mu-virtual-list__item');
222
- if (!items || items.length === 0) return { error: 'No items' };
223
-
224
- const firstItem = items[0];
225
- const top = parseInt(firstItem.style.top);
226
- const height = parseInt(firstItem.style.height);
227
- const position = getComputedStyle(firstItem).position;
228
-
229
- return { top, height, position };
230
- });
231
-
232
- expect(result.position).toBe('absolute');
233
- expect(result.height).toBe(50);
234
- expect(result.top).toBe(0); // First visible item starts at 0
235
- });
236
-
237
- // ========================================
238
- // SCROLL BEHAVIOR
239
- // ========================================
240
-
241
- test('should update visible items on scroll', async () => {
242
- await page.goto(`http://localhost:${server.port}/test-virtual-list.html`, {
243
- waitUntil: 'networkidle0'
244
- });
245
- await delay(500);
246
-
247
- const result = await page.evaluate(async () => {
248
- const el = document.querySelector('mu-virtual-list');
249
- const viewport = el.querySelector('.mu-virtual-list__viewport');
250
-
251
- // Get first visible item before scroll
252
- const beforeItems = el.querySelectorAll('.mu-virtual-list__item');
253
- const beforeIndex = parseInt(beforeItems[0]?.getAttribute('data-index') || '0');
254
-
255
- // Scroll down 500px (10 items)
256
- viewport.scrollTop = 500;
257
-
258
- // Wait for throttled scroll handler (16ms) + render time
259
- await new Promise(r => setTimeout(r, 150));
260
-
261
- const afterItems = el.querySelectorAll('.mu-virtual-list__item');
262
- const afterIndex = parseInt(afterItems[0]?.getAttribute('data-index') || '0');
263
-
264
- return { beforeIndex, afterIndex, scrolled: afterIndex > beforeIndex };
265
- });
266
-
267
- expect(result.scrolled).toBe(true);
268
- expect(result.afterIndex).toBeGreaterThan(result.beforeIndex);
269
- });
270
-
271
- // ========================================
272
- // scrollToIndex METHOD
273
- // ========================================
274
-
275
- test('should have scrollToIndex method', async () => {
276
- await page.goto(`http://localhost:${server.port}/test-virtual-list.html`, {
277
- waitUntil: 'networkidle0'
278
- });
279
- await delay(500);
280
-
281
- const hasMethod = await page.evaluate(() => {
282
- const el = document.querySelector('mu-virtual-list');
283
- return typeof el?.scrollToIndex === 'function';
284
- });
285
-
286
- expect(hasMethod).toBe(true);
287
- });
288
-
289
- test('scrollToIndex should scroll to correct position', async () => {
290
- await page.goto(`http://localhost:${server.port}/test-virtual-list.html`, {
291
- waitUntil: 'networkidle0'
292
- });
293
- await delay(500);
294
-
295
- const result = await page.evaluate(async () => {
296
- const el = document.querySelector('mu-virtual-list');
297
- const viewport = el.querySelector('.mu-virtual-list__viewport');
298
-
299
- el.scrollToIndex(100);
300
-
301
- // Wait for scroll
302
- await new Promise(r => setTimeout(r, 100));
303
-
304
- const scrollTop = viewport.scrollTop;
305
- const expectedScrollTop = 100 * 50; // index * itemHeight
306
-
307
- return { scrollTop, expectedScrollTop };
308
- });
309
-
310
- expect(result.scrollTop).toBe(result.expectedScrollTop);
311
- });
312
-
313
- // ========================================
314
- // ITEMS PROPERTY
315
- // ========================================
316
-
317
- test('should have items getter and setter', async () => {
318
- await page.goto(`http://localhost:${server.port}/test-virtual-list.html`, {
319
- waitUntil: 'networkidle0'
320
- });
321
- await delay(500);
322
-
323
- const result = await page.evaluate(() => {
324
- const el = document.querySelector('mu-virtual-list');
325
- const items = el.items;
326
- return {
327
- isArray: Array.isArray(items),
328
- length: items?.length
329
- };
330
- });
331
-
332
- expect(result.isArray).toBe(true);
333
- expect(result.length).toBe(1000);
334
- });
335
-
336
- test('should update when items are changed', async () => {
337
- await page.goto(`http://localhost:${server.port}/test-virtual-list.html`, {
338
- waitUntil: 'networkidle0'
339
- });
340
- await delay(500);
341
-
342
- const result = await page.evaluate(async () => {
343
- const el = document.querySelector('mu-virtual-list');
344
- const content = el.querySelector('.mu-virtual-list__content');
345
-
346
- const heightBefore = parseInt(content.style.height);
347
-
348
- // Set new items array
349
- el.items = Array.from({ length: 500 }, (_, i) => ({ id: i, name: 'New Item ' + i }));
350
-
351
- // Wait for render
352
- await new Promise(r => setTimeout(r, 100));
353
-
354
- const heightAfter = parseInt(content.style.height);
355
-
356
- return { heightBefore, heightAfter, changed: heightBefore !== heightAfter };
357
- });
358
-
359
- expect(result.changed).toBe(true);
360
- expect(result.heightAfter).toBe(500 * 50); // 500 items * 50px
361
- });
362
-
363
- // ========================================
364
- // RENDER ITEM CALLBACK
365
- // ========================================
366
-
367
- test('should use custom renderItem callback', async () => {
368
- await page.goto(`http://localhost:${server.port}/test-virtual-list.html`, {
369
- waitUntil: 'networkidle0'
370
- });
371
- await delay(500);
372
-
373
- const result = await page.evaluate(() => {
374
- const el = document.querySelector('mu-virtual-list');
375
- const item = el.querySelector('.mu-virtual-list__item');
376
- return {
377
- hasCustomContent: item !== null,
378
- text: item?.textContent?.trim()
379
- };
380
- });
381
-
382
- expect(result.hasCustomContent).toBe(true);
383
- // The item renders the content from renderItem callback
384
- expect(result.text).toBeTruthy();
385
- });
386
-
387
- // ========================================
388
- // DATA ATTRIBUTES
389
- // ========================================
390
-
391
- test('should have data-index attribute on items', async () => {
392
- await page.goto(`http://localhost:${server.port}/test-virtual-list.html`, {
393
- waitUntil: 'networkidle0'
394
- });
395
- await delay(500);
396
-
397
- const result = await page.evaluate(() => {
398
- const el = document.querySelector('mu-virtual-list');
399
- const items = el.querySelectorAll('.mu-virtual-list__item');
400
- const indices = Array.from(items).map(i => parseInt(i.getAttribute('data-index')));
401
- return {
402
- hasIndices: indices.length > 0,
403
- firstIndex: indices[0],
404
- allHaveIndex: indices.every(i => !isNaN(i))
405
- };
406
- });
407
-
408
- expect(result.hasIndices).toBe(true);
409
- expect(result.firstIndex).toBe(0);
410
- expect(result.allHaveIndex).toBe(true);
411
- });
412
-
413
- test('should have data-mu-state=visible on rendered items', async () => {
414
- await page.goto(`http://localhost:${server.port}/test-virtual-list.html`, {
415
- waitUntil: 'networkidle0'
416
- });
417
- await delay(500);
418
-
419
- const result = await page.evaluate(() => {
420
- const el = document.querySelector('mu-virtual-list');
421
- const items = el.querySelectorAll('.mu-virtual-list__item[data-mu-state="visible"]');
422
- return { count: items.length };
423
- });
424
-
425
- expect(result.count).toBeGreaterThan(0);
426
- });
427
- });