@gjsify/adwaita-web 0.64.1 → 0.64.2-next.254

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 (335) hide show
  1. package/README.md +22 -88
  2. package/dist/adwaita-web.css +91 -12
  3. package/dist/adwaita-web.css.map +1 -1
  4. package/lib/types/accessible.d.ts +8 -0
  5. package/lib/types/application.d.ts +1 -1
  6. package/lib/types/child-slot.spec.d.ts +1 -0
  7. package/lib/types/compute-expand.d.ts +5 -0
  8. package/lib/types/compute-expand.spec.d.ts +1 -0
  9. package/lib/types/container-fill.spec.d.ts +1 -0
  10. package/lib/types/elements/adw-about-dialog.d.ts +28 -1
  11. package/lib/types/elements/adw-action-row.d.ts +2 -1
  12. package/lib/types/elements/adw-alert-dialog.d.ts +80 -19
  13. package/lib/types/elements/adw-avatar.d.ts +2 -1
  14. package/lib/types/elements/adw-banner.d.ts +2 -1
  15. package/lib/types/elements/adw-bin.d.ts +2 -1
  16. package/lib/types/elements/adw-bottom-sheet.d.ts +5 -4
  17. package/lib/types/elements/adw-breakpoint-bin.d.ts +2 -1
  18. package/lib/types/elements/adw-button-content.d.ts +2 -1
  19. package/lib/types/elements/adw-button-row.d.ts +2 -1
  20. package/lib/types/elements/adw-card.d.ts +2 -1
  21. package/lib/types/elements/adw-carousel.d.ts +20 -3
  22. package/lib/types/elements/adw-clamp-scrollable.d.ts +2 -1
  23. package/lib/types/elements/adw-clamp.d.ts +2 -1
  24. package/lib/types/elements/adw-combo-row.d.ts +6 -1
  25. package/lib/types/elements/adw-data-grid.d.ts +2 -1
  26. package/lib/types/elements/adw-dialog.d.ts +2 -1
  27. package/lib/types/elements/adw-entry-row.d.ts +2 -1
  28. package/lib/types/elements/adw-expander-row.d.ts +2 -1
  29. package/lib/types/elements/adw-header-bar.d.ts +2 -1
  30. package/lib/types/elements/adw-inline-view-switcher.d.ts +3 -2
  31. package/lib/types/elements/adw-layout-slot.d.ts +2 -1
  32. package/lib/types/elements/adw-multi-layout-view.d.ts +2 -1
  33. package/lib/types/elements/adw-navigation-split-view.d.ts +2 -1
  34. package/lib/types/elements/adw-navigation-view.d.ts +3 -2
  35. package/lib/types/elements/adw-overlay-split-view.d.ts +2 -1
  36. package/lib/types/elements/adw-preferences-dialog.d.ts +5 -4
  37. package/lib/types/elements/adw-preferences-group.d.ts +2 -1
  38. package/lib/types/elements/adw-preferences-row.d.ts +2 -1
  39. package/lib/types/elements/adw-shortcut-label.d.ts +2 -1
  40. package/lib/types/elements/adw-shortcuts-item.d.ts +2 -1
  41. package/lib/types/elements/adw-shortcuts-section.d.ts +2 -1
  42. package/lib/types/elements/adw-sidebar.d.ts +4 -3
  43. package/lib/types/elements/adw-spin-row.d.ts +2 -1
  44. package/lib/types/elements/adw-spinner.d.ts +2 -1
  45. package/lib/types/elements/adw-split-button.d.ts +2 -1
  46. package/lib/types/elements/adw-status-page.d.ts +2 -1
  47. package/lib/types/elements/adw-switch-row.d.ts +4 -1
  48. package/lib/types/elements/adw-tab-bar.d.ts +2 -1
  49. package/lib/types/elements/adw-tab-button.d.ts +2 -1
  50. package/lib/types/elements/adw-tab-overview.d.ts +2 -1
  51. package/lib/types/elements/adw-tab-view.d.ts +3 -2
  52. package/lib/types/elements/adw-toast-overlay.d.ts +10 -30
  53. package/lib/types/elements/adw-toggle-group.d.ts +3 -2
  54. package/lib/types/elements/adw-toolbar-view.d.ts +2 -1
  55. package/lib/types/elements/adw-view-stack.d.ts +3 -1
  56. package/lib/types/elements/adw-view-switcher-bar.d.ts +2 -1
  57. package/lib/types/elements/adw-view-switcher-sidebar.d.ts +2 -1
  58. package/lib/types/elements/adw-view-switcher.d.ts +3 -2
  59. package/lib/types/elements/adw-window-features.d.ts +8 -0
  60. package/lib/types/elements/adw-window-title.d.ts +2 -1
  61. package/lib/types/elements/adw-wrap-box.d.ts +2 -1
  62. package/lib/types/elements/checks.d.ts +16 -1
  63. package/lib/types/elements/gtk-action-bar.d.ts +2 -1
  64. package/lib/types/elements/gtk-application-window.d.ts +1 -4
  65. package/lib/types/elements/gtk-aspect-frame.d.ts +2 -1
  66. package/lib/types/elements/gtk-box.d.ts +5 -1
  67. package/lib/types/elements/gtk-button.d.ts +5 -7
  68. package/lib/types/elements/gtk-calendar.d.ts +2 -1
  69. package/lib/types/elements/gtk-center-box.d.ts +2 -1
  70. package/lib/types/elements/gtk-color-dialog-button.d.ts +2 -1
  71. package/lib/types/elements/gtk-drag-icon.d.ts +2 -1
  72. package/lib/types/elements/gtk-drawing-area.d.ts +2 -1
  73. package/lib/types/elements/gtk-drop-down.d.ts +2 -1
  74. package/lib/types/elements/gtk-editable-label.d.ts +2 -1
  75. package/lib/types/elements/gtk-emoji-chooser.d.ts +2 -0
  76. package/lib/types/elements/gtk-entry.d.ts +2 -1
  77. package/lib/types/elements/gtk-expander.d.ts +2 -1
  78. package/lib/types/elements/gtk-fixed.d.ts +2 -1
  79. package/lib/types/elements/gtk-flow-box-child.d.ts +2 -1
  80. package/lib/types/elements/gtk-flow-box.d.ts +2 -1
  81. package/lib/types/elements/gtk-font-dialog-button.d.ts +2 -1
  82. package/lib/types/elements/gtk-frame.d.ts +2 -1
  83. package/lib/types/elements/gtk-gl-area.d.ts +2 -1
  84. package/lib/types/elements/gtk-graphics-offload.d.ts +2 -1
  85. package/lib/types/elements/gtk-grid.d.ts +2 -1
  86. package/lib/types/elements/gtk-header-bar.d.ts +2 -1
  87. package/lib/types/elements/gtk-image.d.ts +5 -6
  88. package/lib/types/elements/gtk-inscription.d.ts +2 -1
  89. package/lib/types/elements/gtk-label.d.ts +10 -3
  90. package/lib/types/elements/gtk-level-bar.d.ts +2 -1
  91. package/lib/types/elements/gtk-list-box-row.d.ts +2 -1
  92. package/lib/types/elements/gtk-list-box.d.ts +2 -1
  93. package/lib/types/elements/gtk-list-view.d.ts +2 -1
  94. package/lib/types/elements/gtk-media-controls.d.ts +2 -1
  95. package/lib/types/elements/gtk-menu-button.d.ts +2 -1
  96. package/lib/types/elements/gtk-notebook.d.ts +2 -1
  97. package/lib/types/elements/gtk-overlay.d.ts +2 -1
  98. package/lib/types/elements/gtk-paned.d.ts +2 -1
  99. package/lib/types/elements/gtk-picture.d.ts +2 -1
  100. package/lib/types/elements/gtk-popover-bin.d.ts +2 -1
  101. package/lib/types/elements/gtk-popover-menu-bar.d.ts +2 -1
  102. package/lib/types/elements/gtk-popover-menu.d.ts +2 -0
  103. package/lib/types/elements/gtk-popover.d.ts +7 -1
  104. package/lib/types/elements/gtk-progress-bar.d.ts +2 -1
  105. package/lib/types/elements/gtk-revealer.d.ts +2 -1
  106. package/lib/types/elements/gtk-scale-button.d.ts +2 -1
  107. package/lib/types/elements/gtk-scale.d.ts +2 -1
  108. package/lib/types/elements/gtk-scrollbar.d.ts +2 -1
  109. package/lib/types/elements/gtk-scrolled-window.d.ts +2 -1
  110. package/lib/types/elements/gtk-search-bar.d.ts +2 -1
  111. package/lib/types/elements/gtk-separator.d.ts +2 -1
  112. package/lib/types/elements/gtk-spin-button.d.ts +2 -1
  113. package/lib/types/elements/gtk-spinner.d.ts +2 -1
  114. package/lib/types/elements/gtk-stack-page.d.ts +29 -0
  115. package/lib/types/elements/gtk-stack-sidebar.d.ts +2 -1
  116. package/lib/types/elements/gtk-stack-switcher.d.ts +2 -1
  117. package/lib/types/elements/gtk-stack.d.ts +13 -1
  118. package/lib/types/elements/gtk-switch.d.ts +2 -1
  119. package/lib/types/elements/gtk-text-view.d.ts +2 -1
  120. package/lib/types/elements/gtk-text.d.ts +2 -1
  121. package/lib/types/elements/gtk-tree-expander.d.ts +2 -1
  122. package/lib/types/elements/gtk-video.d.ts +2 -1
  123. package/lib/types/elements/gtk-viewport.d.ts +2 -1
  124. package/lib/types/elements/gtk-window-controls.d.ts +2 -1
  125. package/lib/types/elements/gtk-window-handle.d.ts +2 -1
  126. package/lib/types/elements/gtk-window.d.ts +18 -2
  127. package/lib/types/gobject-elements.d.ts +15 -0
  128. package/lib/types/gtksource/gtk-source-view.d.ts +2 -1
  129. package/lib/types/{icon-registry.spec.d.ts → icon-masks.spec.d.ts} +1 -1
  130. package/lib/types/icon-theme.d.ts +15 -35
  131. package/lib/types/index.d.ts +3 -4
  132. package/lib/types/learn6502-window.spec.d.ts +1 -0
  133. package/lib/types/namespace/adw.d.ts +1 -1
  134. package/lib/types/namespace/gdk.d.ts +1 -0
  135. package/lib/types/namespace/gio.d.ts +1 -0
  136. package/lib/types/namespace/glib.d.ts +2 -1
  137. package/lib/types/namespace/gtk.d.ts +2 -0
  138. package/lib/types/shared-tree-builder.d.ts +31 -0
  139. package/lib/types/signals.d.ts +5 -1
  140. package/lib/types/source-view/adw-source-view.d.ts +2 -1
  141. package/lib/types/styles.generated.d.ts +1 -1
  142. package/lib/types/widget-element.d.ts +25 -0
  143. package/lib/types/widget-map.spec.d.ts +1 -0
  144. package/lib/types/widget-mapping.d.ts +7 -0
  145. package/lib/types/widget-visible.spec.d.ts +1 -0
  146. package/lib/types/window-maximize.spec.d.ts +1 -0
  147. package/package.json +13 -12
  148. package/scss/_about_dialog.scss +46 -1
  149. package/scss/_bin.scss +6 -3
  150. package/scss/_checks.scss +5 -2
  151. package/scss/_icon.scss +3 -4
  152. package/scss/_labels.scss +2 -2
  153. package/scss/_overlay.scss +11 -1
  154. package/scss/_overlay_split_view.scss +10 -0
  155. package/scss/_reset.scss +1 -1
  156. package/scss/_scrolled_window.scss +15 -0
  157. package/scss/_stack.scss +8 -2
  158. package/scss/_toast.scss +13 -3
  159. package/scss/_widget.scss +5 -1
  160. package/scss/_window.scss +16 -0
  161. package/src/accessible.ts +29 -0
  162. package/src/adw-about-dialog.spec.ts +81 -12
  163. package/src/adw-action-rows.spec.ts +2 -2
  164. package/src/adw-alert-dialog.spec.ts +71 -5
  165. package/src/adw-bin.spec.ts +4 -3
  166. package/src/adw-button-content.spec.ts +1 -1
  167. package/src/adw-carousel.spec.ts +49 -1
  168. package/src/adw-header-bar.spec.ts +18 -0
  169. package/src/adw-toast-overlay.spec.ts +68 -24
  170. package/src/application.spec.ts +44 -0
  171. package/src/application.ts +13 -18
  172. package/src/capabilities.mjs +2 -3
  173. package/src/checks.spec.ts +51 -1
  174. package/src/child-slot.spec.ts +71 -0
  175. package/src/compute-expand.spec.ts +186 -0
  176. package/src/compute-expand.ts +134 -0
  177. package/src/construct-vectors.spec.ts +97 -7
  178. package/src/container-fill.spec.ts +70 -0
  179. package/src/elements/adw-about-dialog.ts +152 -9
  180. package/src/elements/adw-action-row.ts +2 -1
  181. package/src/elements/adw-alert-dialog.ts +177 -46
  182. package/src/elements/adw-application-window.ts +0 -3
  183. package/src/elements/adw-avatar.ts +2 -1
  184. package/src/elements/adw-banner.ts +2 -1
  185. package/src/elements/adw-bin.ts +2 -1
  186. package/src/elements/adw-bottom-sheet.ts +5 -4
  187. package/src/elements/adw-breakpoint-bin.ts +5 -1
  188. package/src/elements/adw-button-content.ts +2 -1
  189. package/src/elements/adw-button-row.ts +2 -1
  190. package/src/elements/adw-card.ts +2 -1
  191. package/src/elements/adw-carousel.ts +62 -3
  192. package/src/elements/adw-clamp-scrollable.ts +2 -1
  193. package/src/elements/adw-clamp.ts +2 -1
  194. package/src/elements/adw-combo-row.ts +15 -1
  195. package/src/elements/adw-data-grid.ts +2 -1
  196. package/src/elements/adw-dialog.ts +5 -2
  197. package/src/elements/adw-entry-row.ts +2 -1
  198. package/src/elements/adw-expander-row.ts +2 -1
  199. package/src/elements/adw-header-bar.ts +6 -1
  200. package/src/elements/adw-inline-view-switcher.ts +3 -2
  201. package/src/elements/adw-layout-slot.ts +2 -1
  202. package/src/elements/adw-multi-layout-view.ts +2 -1
  203. package/src/elements/adw-navigation-split-view.ts +2 -1
  204. package/src/elements/adw-navigation-view.ts +3 -2
  205. package/src/elements/adw-overlay-split-view.ts +2 -1
  206. package/src/elements/adw-preferences-dialog.ts +6 -4
  207. package/src/elements/adw-preferences-group.ts +2 -1
  208. package/src/elements/adw-preferences-row.ts +2 -1
  209. package/src/elements/adw-shortcut-label.ts +2 -1
  210. package/src/elements/adw-shortcuts-item.ts +2 -1
  211. package/src/elements/adw-shortcuts-section.ts +2 -1
  212. package/src/elements/adw-sidebar.ts +4 -3
  213. package/src/elements/adw-spin-row.ts +2 -1
  214. package/src/elements/adw-spinner.ts +2 -1
  215. package/src/elements/adw-split-button.ts +2 -1
  216. package/src/elements/adw-status-page.ts +2 -1
  217. package/src/elements/adw-switch-row.ts +10 -1
  218. package/src/elements/adw-tab-bar.ts +2 -1
  219. package/src/elements/adw-tab-button.ts +2 -1
  220. package/src/elements/adw-tab-overview.ts +2 -1
  221. package/src/elements/adw-tab-view.ts +3 -2
  222. package/src/elements/adw-toast-overlay.ts +37 -70
  223. package/src/elements/adw-toggle-group.ts +3 -2
  224. package/src/elements/adw-toolbar-view.ts +2 -1
  225. package/src/elements/adw-view-stack.ts +39 -1
  226. package/src/elements/adw-view-switcher-bar.ts +2 -1
  227. package/src/elements/adw-view-switcher-sidebar.ts +2 -1
  228. package/src/elements/adw-view-switcher.ts +3 -2
  229. package/src/elements/adw-window-features.ts +14 -2
  230. package/src/elements/adw-window-title.ts +2 -1
  231. package/src/elements/adw-wrap-box.ts +2 -1
  232. package/src/elements/checks.ts +85 -6
  233. package/src/elements/gtk-action-bar.ts +2 -1
  234. package/src/elements/gtk-application-window.ts +3 -8
  235. package/src/elements/gtk-aspect-frame.ts +2 -1
  236. package/src/elements/gtk-box.ts +56 -1
  237. package/src/elements/gtk-button.ts +10 -22
  238. package/src/elements/gtk-calendar.ts +2 -1
  239. package/src/elements/gtk-center-box.ts +2 -1
  240. package/src/elements/gtk-color-dialog-button.ts +2 -1
  241. package/src/elements/gtk-drag-icon.ts +6 -1
  242. package/src/elements/gtk-drawing-area.ts +2 -1
  243. package/src/elements/gtk-drop-down.ts +2 -1
  244. package/src/elements/gtk-editable-label.ts +2 -1
  245. package/src/elements/gtk-emoji-chooser.ts +5 -0
  246. package/src/elements/gtk-entry.ts +2 -1
  247. package/src/elements/gtk-expander.ts +2 -1
  248. package/src/elements/gtk-fixed.ts +2 -1
  249. package/src/elements/gtk-flow-box-child.ts +5 -2
  250. package/src/elements/gtk-flow-box.ts +2 -1
  251. package/src/elements/gtk-font-dialog-button.ts +2 -1
  252. package/src/elements/gtk-frame.ts +2 -1
  253. package/src/elements/gtk-gl-area.ts +2 -1
  254. package/src/elements/gtk-graphics-offload.ts +5 -2
  255. package/src/elements/gtk-grid.ts +2 -1
  256. package/src/elements/gtk-header-bar.ts +2 -1
  257. package/src/elements/gtk-image.ts +14 -15
  258. package/src/elements/gtk-inscription.ts +2 -1
  259. package/src/elements/gtk-label.ts +12 -5
  260. package/src/elements/gtk-level-bar.ts +2 -1
  261. package/src/elements/gtk-list-box-row.ts +5 -2
  262. package/src/elements/gtk-list-box.ts +2 -1
  263. package/src/elements/gtk-list-view.ts +2 -1
  264. package/src/elements/gtk-media-controls.ts +2 -1
  265. package/src/elements/gtk-menu-button.ts +2 -1
  266. package/src/elements/gtk-notebook.ts +2 -1
  267. package/src/elements/gtk-overlay.ts +7 -5
  268. package/src/elements/gtk-paned.ts +9 -2
  269. package/src/elements/gtk-picture.ts +2 -1
  270. package/src/elements/gtk-popover-bin.ts +5 -1
  271. package/src/elements/gtk-popover-menu-bar.ts +2 -1
  272. package/src/elements/gtk-popover-menu.ts +5 -0
  273. package/src/elements/gtk-popover.ts +14 -1
  274. package/src/elements/gtk-progress-bar.ts +2 -1
  275. package/src/elements/gtk-revealer.ts +2 -1
  276. package/src/elements/gtk-scale-button.ts +2 -1
  277. package/src/elements/gtk-scale.ts +2 -1
  278. package/src/elements/gtk-scrollbar.ts +2 -1
  279. package/src/elements/gtk-scrolled-window.ts +8 -5
  280. package/src/elements/gtk-search-bar.ts +7 -6
  281. package/src/elements/gtk-separator.ts +2 -1
  282. package/src/elements/gtk-spin-button.ts +2 -1
  283. package/src/elements/gtk-spinner.ts +2 -1
  284. package/src/elements/gtk-stack-page.ts +112 -0
  285. package/src/elements/gtk-stack-sidebar.ts +2 -1
  286. package/src/elements/gtk-stack-switcher.ts +2 -1
  287. package/src/elements/gtk-stack.ts +52 -6
  288. package/src/elements/gtk-switch.ts +2 -1
  289. package/src/elements/gtk-text-view.ts +2 -1
  290. package/src/elements/gtk-text.ts +2 -1
  291. package/src/elements/gtk-tree-expander.ts +5 -1
  292. package/src/elements/gtk-video.ts +2 -1
  293. package/src/elements/gtk-viewport.ts +6 -1
  294. package/src/elements/gtk-window-controls.ts +2 -1
  295. package/src/elements/gtk-window-handle.ts +6 -1
  296. package/src/elements/gtk-window.ts +57 -7
  297. package/src/gio.spec.ts +13 -3
  298. package/src/glib.spec.ts +9 -0
  299. package/src/gobject-elements.ts +44 -2
  300. package/src/gtk-label.spec.ts +39 -0
  301. package/src/gtk-scrolled-window.spec.ts +6 -6
  302. package/src/gtk-stack.spec.ts +170 -0
  303. package/src/gtk-value-doors.spec.ts +19 -0
  304. package/src/gtk-window.spec.ts +2 -2
  305. package/src/gtksource/gtk-source-view.ts +2 -1
  306. package/src/{icon-registry.spec.ts → icon-masks.spec.ts} +4 -100
  307. package/src/icon-theme.spec.ts +69 -124
  308. package/src/icon-theme.ts +29 -119
  309. package/src/index.ts +5 -9
  310. package/src/keyboard-operable.spec.ts +3 -3
  311. package/src/learn6502-window.spec.ts +113 -0
  312. package/src/namespace/adw.ts +1 -1
  313. package/src/namespace/gdk.ts +1 -0
  314. package/src/namespace/gio.ts +8 -0
  315. package/src/namespace/glib.ts +4 -0
  316. package/src/namespace/gtk.ts +2 -0
  317. package/src/shared-tree-builder.ts +131 -13
  318. package/src/shared-trees.spec.ts +15 -1
  319. package/src/signals.ts +11 -2
  320. package/src/slotted-children.spec.ts +24 -0
  321. package/src/slotted-children.ts +18 -2
  322. package/src/source-view/adw-source-view.ts +2 -1
  323. package/src/split-button.spec.ts +1 -1
  324. package/src/style-manager.spec.ts +13 -0
  325. package/src/style-manager.ts +6 -0
  326. package/src/styles.generated.ts +1 -1
  327. package/src/test.browser.mts +16 -2
  328. package/src/value-lists.spec.ts +3 -3
  329. package/src/widget-element.ts +75 -0
  330. package/src/widget-map.spec.ts +116 -0
  331. package/src/widget-mapping.ts +100 -0
  332. package/src/widget-visible.spec.ts +148 -0
  333. package/src/window-maximize.spec.ts +93 -0
  334. package/lib/types/icon-registry.d.ts +0 -37
  335. package/src/icon-registry.ts +0 -163
@@ -7,6 +7,8 @@
7
7
  import { describe, expect, it } from '@gjsify/unit';
8
8
 
9
9
  import type { GtkStack } from './elements/gtk-stack.js';
10
+ import { pageChildOf } from './elements/gtk-stack-page.js';
11
+ import { mountSharedTree } from './shared-tree-builder.js';
10
12
 
11
13
  interface Page {
12
14
  name: string;
@@ -144,6 +146,66 @@ export const GtkStackTest = async () => {
144
146
  host.remove();
145
147
  });
146
148
 
149
+ await it('a descendant toggling hidden keeps the selection', async () => {
150
+ // gtkstack.c:1617-1633 listens to the PAGE's own visibility, never to what is
151
+ // nested inside it (an `adw-view-stack` showing and hiding its own pages).
152
+ const { el, host } = mount([
153
+ { name: 'single', title: 'Single' },
154
+ { name: 'three', title: 'Three' },
155
+ ]);
156
+ const nested = document.createElement('span');
157
+ el.children[0]!.appendChild(nested);
158
+ el.setVisibleChildName('three');
159
+ nested.setAttribute('hidden', '');
160
+ await settle();
161
+ nested.removeAttribute('hidden');
162
+ await settle();
163
+ expect(el.visibleChildName).toBe('three');
164
+ expect(marks(el)).toStrictEqual(['single', 'three:visible']);
165
+ host.remove();
166
+ });
167
+
168
+ await it('hiding a page that is not visible keeps the selection', async () => {
169
+ const { el, host } = mount([
170
+ { name: 'a', title: 'Alpha' },
171
+ { name: 'b', title: 'Beta' },
172
+ { name: 'c', title: 'Gamma' },
173
+ ]);
174
+ el.setVisibleChildName('c');
175
+ (el.children[1] as HTMLElement).setAttribute('hidden', '');
176
+ await settle();
177
+ expect(el.visibleChildName).toBe('c');
178
+ host.remove();
179
+ });
180
+
181
+ await it('hiding the visible page after a selection falls back to the first visible', async () => {
182
+ const { el, host } = mount([
183
+ { name: 'a', title: 'Alpha' },
184
+ { name: 'b', title: 'Beta' },
185
+ { name: 'c', title: 'Gamma' },
186
+ ]);
187
+ el.setVisibleChildName('c');
188
+ (el.children[0] as HTMLElement).setAttribute('hidden', '');
189
+ (el.children[2] as HTMLElement).setAttribute('hidden', '');
190
+ await settle();
191
+ expect(el.visibleChildName).toBe('b');
192
+ host.remove();
193
+ });
194
+
195
+ await it('a descendant name change dispatches no page-updated', async () => {
196
+ const { el, host } = mount([{ name: 'a', title: 'Alpha' }]);
197
+ const nested = document.createElement('span');
198
+ el.children[0]!.appendChild(nested);
199
+ await settle();
200
+ const events: string[] = [];
201
+ el.addEventListener('page-updated', () => events.push('updated'));
202
+ nested.setAttribute('name', 'inner');
203
+ await settle();
204
+ expect(events).toStrictEqual([]);
205
+ expect(el.visibleChildName).toBe('a');
206
+ host.remove();
207
+ });
208
+
147
209
  await it('re-selecting the visible page is not a change', () => {
148
210
  // gtkstack.c:1452-1453 — no notify, no transition.
149
211
  const { el, host } = mount([
@@ -429,4 +491,112 @@ export const GtkStackTest = async () => {
429
491
  host.remove();
430
492
  });
431
493
  });
494
+
495
+ await describe('<gtk-stack> adopts a <gtk-stack-page> record', async () => {
496
+ const record = (attrs: Record<string, string>, body = 'page'): HTMLElement => {
497
+ const page = document.createElement('gtk-stack-page');
498
+ for (const [name, value] of Object.entries(attrs)) page.setAttribute(name, value);
499
+ const child = document.createElement('div');
500
+ child.setAttribute('slot', 'child');
501
+ child.textContent = body;
502
+ page.appendChild(child);
503
+ return page;
504
+ };
505
+
506
+ await it('puts the child where the record was, with its properties as attributes', () => {
507
+ const host = document.createElement('div');
508
+ document.body.appendChild(host);
509
+ const el = document.createElement('gtk-stack') as GtkStack;
510
+ el.appendChild(
511
+ record({
512
+ name: 'a',
513
+ title: 'Alpha',
514
+ 'icon-name': 'go-home',
515
+ 'needs-attention': '',
516
+ 'use-underline': '',
517
+ }),
518
+ );
519
+ el.appendChild(record({ name: 'b' }, 'beta'));
520
+ host.appendChild(el);
521
+ expect(el.querySelector('gtk-stack-page')).toBe(null);
522
+ expect(marks(el)).toStrictEqual(['a:visible', 'b']);
523
+ const [page] = el.pages;
524
+ expect(page!.title).toBe('Alpha');
525
+ expect(page!.iconName).toBe('go-home');
526
+ expect(page!.needsAttention).toBe(true);
527
+ expect(page!.useUnderline).toBe(true);
528
+ expect(page!.child.hasAttribute('slot')).toBe(false);
529
+ expect(el.getChildByName('b')!.textContent).toBe('beta');
530
+ host.remove();
531
+ });
532
+
533
+ await it('reads a hidden record as GtkStackPage:visible = FALSE', () => {
534
+ const host = document.createElement('div');
535
+ document.body.appendChild(host);
536
+ const el = document.createElement('gtk-stack') as GtkStack;
537
+ const hidden = record({ name: 'a' });
538
+ (hidden as unknown as { visible: boolean }).visible = false;
539
+ el.appendChild(hidden);
540
+ el.appendChild(record({ name: 'b' }));
541
+ host.appendChild(el);
542
+ expect(el.pages.map((page) => page.visible)).toStrictEqual([false, true]);
543
+ expect(el.visibleChildName).toBe('b');
544
+ host.remove();
545
+ });
546
+
547
+ await it('adopts a record appended after connect, counted once', async () => {
548
+ const { el, host } = mount([{ name: 'a' }]);
549
+ const events: unknown[] = [];
550
+ el.addEventListener('items-changed', (e) => events.push((e as CustomEvent).detail));
551
+ el.appendChild(record({ name: 'b' }));
552
+ await settle();
553
+ await settle();
554
+ expect(marks(el)).toStrictEqual(['a:visible', 'b']);
555
+ expect(events).toStrictEqual([{ position: 1, removed: 0, added: 1 }]);
556
+ host.remove();
557
+ });
558
+
559
+ await it('takes a bare child as the page, and refuses a record with two', () => {
560
+ const bare = document.createElement('gtk-stack-page');
561
+ const child = document.createElement('div');
562
+ bare.appendChild(child);
563
+ expect(pageChildOf(bare)).toBe(child);
564
+ bare.appendChild(document.createElement('div'));
565
+ expect(() => pageChildOf(bare)).toThrow('has one `child`');
566
+ expect(pageChildOf(document.createElement('gtk-stack-page'))).toBe(null);
567
+ });
568
+
569
+ await it('builds `Stack { StackPage { name; child: … } }` from a shared tree', () => {
570
+ const { root, unmount } = mountSharedTree({
571
+ tag: 'GtkStack',
572
+ id: 'layoutHost',
573
+ children: [
574
+ {
575
+ tag: 'GtkStackPage',
576
+ id: 'singlePage',
577
+ props: { name: 'single' },
578
+ children: [{ tag: 'AdwBin', slot: 'child', id: 'singleBin' }],
579
+ },
580
+ {
581
+ tag: 'GtkStackPage',
582
+ id: 'threePage',
583
+ props: { name: 'three', title: 'Three' },
584
+ children: [{ tag: 'AdwBin', slot: 'child', id: 'threeBin' }],
585
+ },
586
+ ],
587
+ });
588
+ try {
589
+ const stack = root as GtkStack;
590
+ expect(stack.pages.map((page) => page.child.id)).toStrictEqual(['singleBin', 'threeBin']);
591
+ // The first page added is the visible one (gtkstack.c:1773-1775); a breakpoint
592
+ // setter selects another by the name its record carried.
593
+ expect(stack.visibleChild!.id).toBe('singleBin');
594
+ expect(stack.setVisibleChildName('three')).toBe(true);
595
+ expect(stack.visibleChild!.id).toBe('threeBin');
596
+ expect(stack.getPage(stack.visibleChild!)!.title).toBe('Three');
597
+ } finally {
598
+ unmount();
599
+ }
600
+ });
601
+ });
432
602
  };
@@ -20,8 +20,15 @@
20
20
  import { describe, it, expect } from '@gjsify/unit';
21
21
 
22
22
  import { GtkAdjustment, GtkStringList } from '@gjsify/adwaita-core';
23
+ import {
24
+ ROW_ACCESSOR_VECTORS,
25
+ driveRowAccessorVectors,
26
+ type ComboRowLike,
27
+ type SwitchRowLike,
28
+ } from '@gjsify/adwaita-core/conformance';
23
29
 
24
30
  import type { AdwComboRow } from './elements/adw-combo-row.js';
31
+ import type { AdwSwitchRow } from './elements/adw-switch-row.js';
25
32
  import type { AdwSpinRow } from './elements/adw-spin-row.js';
26
33
  import type { GtkDropDown } from './elements/gtk-drop-down.js';
27
34
 
@@ -101,4 +108,16 @@ export const GtkValueDoorsTest = async () => {
101
108
  expect(row.value).toBe(16);
102
109
  });
103
110
  });
111
+
112
+ await driveRowAccessorVectors(
113
+ {
114
+ name: 'adwaita-web',
115
+ isOracle: false,
116
+ makeComboRow: () => mount<AdwComboRow>('adw-combo-row') as unknown as ComboRowLike,
117
+ makeSwitchRow: () => mount<AdwSwitchRow>('adw-switch-row') as unknown as SwitchRowLike,
118
+ StringList: GtkStringList,
119
+ },
120
+ { describe, it, expect },
121
+ ROW_ACCESSOR_VECTORS,
122
+ );
104
123
  };
@@ -310,11 +310,11 @@ export const GtkWindowTest = async () => {
310
310
  const { el, host } = mount();
311
311
  let message = '';
312
312
  try {
313
- el.connect('map', () => {});
313
+ el.connect('toggled', () => {});
314
314
  } catch (error) {
315
315
  message = (error as Error).message;
316
316
  }
317
- expect(message.includes("'map'")).toBe(true);
317
+ expect(message.includes("'toggled'")).toBe(true);
318
318
  host.remove();
319
319
  });
320
320
 
@@ -20,6 +20,7 @@ import type { Buffer, Gutter } from '@gjsify/gtksource-core';
20
20
  import { EditorSession, GutterSet, STOP_EMISSION, toBoolean, toNumber } from '@gjsify/gtksource-core';
21
21
 
22
22
  import { WebEditorDriver } from './web-editor-driver.js';
23
+ import { WidgetElement } from '../widget-element.js';
23
24
 
24
25
  const STYLE_ID = 'gjsify-gtk-source-view-style';
25
26
 
@@ -130,7 +131,7 @@ const OBSERVED_ATTRIBUTES = [
130
131
  'bottom-margin',
131
132
  ];
132
133
 
133
- export class GtkSourceView extends HTMLElement {
134
+ export class GtkSourceView extends WidgetElement {
134
135
  static get observedAttributes(): string[] {
135
136
  return OBSERVED_ATTRIBUTES;
136
137
  }
@@ -14,17 +14,12 @@
14
14
  // that was never given draws nothing at all.
15
15
  import { describe, expect, it } from '@gjsify/unit';
16
16
 
17
- import { isIconAvailable, registerIcon } from './icon-registry.js';
17
+ import { isIconAvailable } from './icon-theme.js';
18
18
  import type { GtkImage } from './elements/gtk-image.js';
19
19
 
20
20
  /** A name no compiled entry can collide with, for the "not shipped" arm. */
21
21
  const ABSENT = 'gjsify-registry-probe';
22
22
 
23
- /** An Adwaita symbolic SVG, small and distinctive — a filled 8px square in the 16px grid. */
24
- const PROBE_SVG = `<svg height="16px" viewBox="0 0 16 16" width="16px" xmlns="http://www.w3.org/2000/svg">
25
- <path d="m 4 4 h 8 v 8 h -8 z m 0 0" fill="currentColor"/>
26
- </svg>`;
27
-
28
23
  function mount(iconName: string | null): { el: GtkImage; host: HTMLElement } {
29
24
  const host = document.createElement('div');
30
25
  document.body.appendChild(host);
@@ -55,7 +50,7 @@ export function fallbackMask(): string {
55
50
  return getComputedStyle(document.documentElement).getPropertyValue('--icon-image-missing').trim();
56
51
  }
57
52
 
58
- export const AdwIconRegistryTest = async () => {
53
+ export const AdwIconMasksTest = async () => {
59
54
  await describe('<gtk-image> resolves a name to a real mask', async () => {
60
55
  await it('a compiled name draws its OWN glyph, not the fallback', () => {
61
56
  const { el, host } = mount('go-next');
@@ -136,8 +131,8 @@ export const AdwIconRegistryTest = async () => {
136
131
  });
137
132
  });
138
133
 
139
- await describe('registerIcon — the documented way out of the compiled set', async () => {
140
- await it('reports an unshipped name as unavailable before registration', () => {
134
+ await describe('isIconAvailable', async () => {
135
+ await it('answers for the compiled set and nothing else', () => {
141
136
  expect(isIconAvailable(ABSENT)).toBe(false);
142
137
  // …and a compiled one as available, so the check is not answering `false` to
143
138
  // everything. `image-missing` is the case a fallback-comparison would get wrong.
@@ -145,96 +140,5 @@ export const AdwIconRegistryTest = async () => {
145
140
  expect(isIconAvailable('image-missing')).toBe(true);
146
141
  expect(isIconAvailable('')).toBe(false);
147
142
  });
148
-
149
- await it('makes the name resolve, on an element that is ALREADY mounted', () => {
150
- const { el, host } = mount(ABSENT);
151
- const before = maskOf(el);
152
- expect(before).toBe(fallbackMask());
153
-
154
- registerIcon(ABSENT, PROBE_SVG);
155
-
156
- const after = maskOf(el);
157
- expect(after).not.toBe(before);
158
- expect(after).not.toBe(fallbackMask());
159
- // The glyph really is the SVG that was handed over: `toDataUri` percent-encodes
160
- // it, so the path data survives as `m%204%204`.
161
- expect(after.includes('m%204%204')).toBe(true);
162
- expect(isIconAvailable(ABSENT)).toBe(true);
163
- host.remove();
164
- });
165
-
166
- await it('serves an element mounted AFTER the registration too', () => {
167
- const { el, host } = mount(`${ABSENT}-symbolic`);
168
- // The `-symbolic` suffix is stripped on both sides, so a consumer may spell the
169
- // name either way at either end.
170
- expect(el.resolvedIconName).toBe(ABSENT);
171
- expect(maskOf(el).includes('m%204%204')).toBe(true);
172
- host.remove();
173
- });
174
-
175
- await it('re-registering REPLACES the glyph without a second rule', () => {
176
- const sheet = (document.getElementById('adwaita-web-icon-registry') as HTMLStyleElement).sheet;
177
- const rulesBefore = (sheet as CSSStyleSheet).cssRules.length;
178
-
179
- registerIcon(ABSENT, PROBE_SVG.replace('m 4 4 h 8 v 8 h -8 z', 'm 2 2 h 12 v 12 h -12 z'));
180
-
181
- const { el, host } = mount(ABSENT);
182
- expect(maskOf(el).includes('m%202%202')).toBe(true);
183
- expect((sheet as CSSStyleSheet).cssRules.length).toBe(rulesBefore);
184
- host.remove();
185
- });
186
-
187
- await it('throws on a name that could never be one CSS class', () => {
188
- // Silently registering nothing is the exact failure this area exists to end, and
189
- // unlike a render this is an explicit call with a wrong argument.
190
- expect(() => registerIcon('org.gnome.Builder', PROBE_SVG)).toThrow();
191
- expect(() => registerIcon('a b', PROBE_SVG)).toThrow();
192
- expect(() => registerIcon('', PROBE_SVG)).toThrow();
193
- });
194
-
195
- await it('throws on a payload that is not SVG source, and registers nothing', () => {
196
- // The NAME was checked from the start; the payload was not, and a wrong payload
197
- // is worse than no registration at all: `toDataUri` percent-encodes anything, so
198
- // the mask comes out well-formed and masks NOTHING, the registered class beats
199
- // `:where(.adw-icon)` and switches this PR's fallback off, and `isIconAvailable`
200
- // answers `true`. Measured in Firefox before the guard: `'not an svg at all'`
201
- // gave `url("data:image/svg+xml,not%20an%20svg%20at%20all")` on a box painted 0 %,
202
- // where the unregistered control painted the 30 % image-missing glyph.
203
- const REJECTED = 'gjsify-registry-payload-probe';
204
- expect(() => registerIcon(REJECTED, 'not an svg at all')).toThrow();
205
- expect(() => registerIcon(REJECTED, '')).toThrow();
206
- // The likeliest wrong argument of all: what the custom property HOLDS, handed
207
- // over instead of what it was made from.
208
- expect(() => registerIcon(REJECTED, 'url("data:image/svg+xml,%3Csvg%3E%3C/svg%3E")')).toThrow();
209
- expect(isIconAvailable(REJECTED)).toBe(false);
210
-
211
- const { el, host } = mount(REJECTED);
212
- expect(maskOf(el)).toBe(fallbackMask());
213
- host.remove();
214
- });
215
-
216
- await it('rebuilds its rules after the <style> element is taken away', () => {
217
- // What an SPA head swap or an Astro view transition does. The record of inserted
218
- // rules is module-level and outlives the element it describes, so the early
219
- // return above used to skip the insert forever: the custom property was re-set
220
- // and the rule that consumes it never came back.
221
- const LOST = 'gjsify-registry-sheet-probe';
222
- registerIcon(LOST, PROBE_SVG);
223
- document.getElementById('adwaita-web-icon-registry')?.remove();
224
-
225
- registerIcon(LOST, PROBE_SVG);
226
-
227
- expect(document.getElementById('adwaita-web-icon-registry')).not.toBe(null);
228
- const { el, host } = mount(LOST);
229
- expect(maskOf(el).includes('m%204%204')).toBe(true);
230
- host.remove();
231
-
232
- // …and the names registered BEFORE the swap are on the rebuilt sheet too. Their
233
- // custom properties survived on the document element, so re-issuing only the
234
- // one name being registered would leave every earlier icon on the fallback.
235
- const { el: earlier, host: earlierHost } = mount(ABSENT);
236
- expect(maskOf(earlier)).not.toBe(fallbackMask());
237
- earlierHost.remove();
238
- });
239
143
  });
240
144
  };
@@ -1,30 +1,38 @@
1
1
  // The colour half of the icon theme, measured through `getComputedStyle` like
2
- // `icon-registry.spec.ts` measures the symbolic half: a class on the node proves nothing
2
+ // `icon-masks.spec.ts` measures the symbolic half: a class on the node proves nothing
3
3
  // about what it paints.
4
4
  //
5
5
  // The order under test is GLib's (`g_themed_icon_update_names`, which `GtkImage`'s lookup
6
6
  // goes through): `X` tries [X, X-symbolic], `X-symbolic` tries [X-symbolic, X], and a name
7
7
  // neither answers draws `image-missing`. Over the names the order is gtkicontheme.c's: the
8
- // current theme (the stylesheet's glyphs) before hicolor, where a colour registration sits.
9
- // The registry is module-level and outlives a test,
10
- // so every case registers names of its own.
8
+ // current theme (the stylesheet's glyphs) before hicolor, where an app's resource files sit.
9
+ // Colour icons come from the shared GResource fixture, on the theme's resource path as
10
+ // `GtkApplication` adds `<resource-base-path>/icons/` (ADR 0116 § 4).
11
+ import { ICON_FIXTURE_PATH, gioResourceFixtures } from '@gjsify/adwaita-core/conformance';
11
12
  import { describe, expect, it } from '@gjsify/unit';
12
13
 
13
14
  import { COLOR_ICON_CLASS, type GtkImage } from './elements/gtk-image.js';
14
- import { fallbackMask, maskOf } from './icon-registry.spec.js';
15
- import { isIconAvailable, registerIcon } from './icon-registry.js';
16
- import { registerColorIcon } from './icon-theme.js';
17
-
18
- /** A full-colour app icon: a red square on a blue disc, nothing `currentColor` about it. */
19
- const COLOR_SVG = `<svg height="128" viewBox="0 0 128 128" width="128" xmlns="http://www.w3.org/2000/svg">
20
- <circle cx="64" cy="64" r="60" fill="#3584e4"/>
21
- <rect x="40" y="40" width="48" height="48" fill="#e01b24"/>
22
- </svg>`;
23
-
24
- /** A symbolic glyph, distinct from the colour one by its path data. */
25
- const SYMBOLIC_SVG = `<svg height="16px" viewBox="0 0 16 16" width="16px" xmlns="http://www.w3.org/2000/svg">
26
- <path d="m 3 3 h 10 v 10 h -10 z m 0 0" fill="currentColor"/>
27
- </svg>`;
15
+ import { fallbackMask, maskOf } from './icon-masks.spec.js';
16
+ import { isIconAvailable } from './icon-theme.js';
17
+ import { Gio, Gtk } from './index.js';
18
+
19
+ const theme = Gtk.IconTheme.get_for_display(null);
20
+
21
+ /** Runs `body` with the fixture's icons on the theme's resource path. */
22
+ async function withIcons(body: () => void): Promise<void> {
23
+ const resource = Gio.Resource.new_from_data(gioResourceFixtures().icons);
24
+ Gio.resources_register(resource);
25
+ const before = theme.get_resource_path();
26
+ theme.add_resource_path(ICON_FIXTURE_PATH);
27
+ // `changed` is deferred: let it drop the previous scan's URLs first.
28
+ await new Promise((resolve) => setTimeout(resolve, 0));
29
+ try {
30
+ body();
31
+ } finally {
32
+ theme.set_resource_path(before);
33
+ Gio.resources_unregister(resource);
34
+ }
35
+ }
28
36
 
29
37
  function mount(iconName: string): { el: GtkImage; host: HTMLElement } {
30
38
  const host = document.createElement('div');
@@ -40,7 +48,7 @@ function maskClasses(el: HTMLElement): string[] {
40
48
  return [...el.classList].filter((c) => c.startsWith('adw-icon--'));
41
49
  }
42
50
 
43
- /** True when `el` paints the registered colour image and nothing masked. */
51
+ /** True when `el` paints a resource image as-is and nothing masked. */
44
52
  function drawsInColor(el: HTMLElement): boolean {
45
53
  const style = getComputedStyle(el);
46
54
  return (
@@ -48,66 +56,41 @@ function drawsInColor(el: HTMLElement): boolean {
48
56
  maskClasses(el).length === 0 &&
49
57
  style.maskImage === 'none' &&
50
58
  style.backgroundColor === 'rgba(0, 0, 0, 0)' &&
51
- // `#e01b24` survives `toDataUri` as `%23e01b24`.
52
- style.backgroundImage.includes('%23e01b24')
59
+ style.backgroundImage.startsWith('url("blob:')
53
60
  );
54
61
  }
55
62
 
56
63
  export const AdwIconThemeTest = async () => {
57
- await describe('registerColorIcon — a full-colour icon draws as-is', async () => {
58
- await it('a reverse-DNS application id draws its image, unmasked and unfilled', () => {
59
- const APP_ID = 'org.gjsify.ColorProbe';
60
- registerColorIcon(APP_ID, COLOR_SVG);
61
-
62
- const { el, host } = mount(APP_ID);
63
- expect(drawsInColor(el)).toBe(true);
64
- expect(isIconAvailable(APP_ID)).toBe(true);
65
- host.remove();
66
- });
67
-
68
- await it('an already-mounted element redraws when the registration lands', () => {
69
- const APP_ID = 'org.gjsify.LateColorProbe';
70
- const { el, host } = mount(APP_ID);
71
- // Before: what GTK draws for a name its theme does not have.
72
- expect(maskOf(el)).toBe(fallbackMask());
73
-
74
- registerColorIcon(APP_ID, COLOR_SVG);
75
- expect(drawsInColor(el)).toBe(true);
76
- host.remove();
77
- });
78
-
79
- await it("AdwAboutDialog's application-icon draws in colour", () => {
80
- const APP_ID = 'org.gjsify.AboutColorProbe';
81
- registerColorIcon(APP_ID, COLOR_SVG);
82
-
83
- const host = document.createElement('div');
84
- document.body.appendChild(host);
85
- const dialog = document.createElement('adw-about-dialog');
86
- dialog.setAttribute('application-icon', APP_ID);
87
- host.appendChild(dialog);
88
-
89
- const icon = dialog.querySelector('.adw-about-dialog-icon') as HTMLElement;
90
- expect(drawsInColor(icon)).toBe(true);
91
- // The 96px box, and NOT a solid square under the image: the about dialog used to
92
- // force `background-color: currentColor` on it.
93
- expect(getComputedStyle(icon).width).toBe('96px');
94
- host.remove();
64
+ await describe('a full-colour resource icon draws as-is', async () => {
65
+ await it('draws its image, unmasked and unfilled', async () => {
66
+ await withIcons(() => {
67
+ const { el, host } = mount('flat');
68
+ expect(drawsInColor(el)).toBe(true);
69
+ expect(isIconAvailable('flat')).toBe(true);
70
+ host.remove();
71
+ });
95
72
  });
96
73
 
97
- await it('a URL source resolves against the document', () => {
98
- const APP_ID = 'org.gjsify.UrlColorProbe';
99
- registerColorIcon(APP_ID, 'icons/org.gjsify.UrlColorProbe.svg');
100
-
101
- const { el, host } = mount(APP_ID);
102
- const expected = new URL('icons/org.gjsify.UrlColorProbe.svg', document.baseURI).href;
103
- expect(el.classList.contains(COLOR_ICON_CLASS)).toBe(true);
104
- expect(getComputedStyle(el).backgroundImage).toBe(`url("${expected}")`);
105
- host.remove();
74
+ await it("AdwAboutDialog's application-icon draws in colour", async () => {
75
+ await withIcons(() => {
76
+ const host = document.createElement('div');
77
+ document.body.appendChild(host);
78
+ const dialog = document.createElement('adw-about-dialog');
79
+ dialog.setAttribute('application-icon', 'flat');
80
+ host.appendChild(dialog);
81
+
82
+ const icon = dialog.querySelector('.adw-about-dialog-icon') as HTMLElement;
83
+ expect(drawsInColor(icon)).toBe(true);
84
+ // The 96px box, and NOT a solid square under the image: the about dialog used to
85
+ // force `background-color: currentColor` on it.
86
+ expect(getComputedStyle(icon).width).toBe('96px');
87
+ host.remove();
88
+ });
106
89
  });
107
90
  });
108
91
 
109
92
  await describe('the lookup order is GThemedIcon’s', async () => {
110
- await it('a symbolic icon still masks — a compiled one with no colour entry', () => {
93
+ await it('a symbolic icon still masks — a compiled one with no resource', () => {
111
94
  const { el, host } = mount('go-next-symbolic');
112
95
  expect(el.classList.contains(COLOR_ICON_CLASS)).toBe(false);
113
96
  expect(maskClasses(el)).toStrictEqual(['adw-icon--go-next']);
@@ -116,41 +99,28 @@ export const AdwIconThemeTest = async () => {
116
99
  host.remove();
117
100
  });
118
101
 
119
- await it("the theme's X-symbolic answers X and X-symbolic before the colour X, as on GTK", () => {
120
- const NAME = 'gjsify-theme-both-probe';
121
- registerColorIcon(NAME, COLOR_SVG);
122
- registerIcon(`${NAME}-symbolic`, SYMBOLIC_SVG);
123
-
124
- // gtkicontheme.c `real_choose_icon`: theme outer, name inner, and the colour icon is
125
- // hicolor's, so the current theme's `X-symbolic` comes first (ADR 0115 Amendment 2).
126
- const plain = mount(NAME);
127
- expect(plain.el.classList.contains(COLOR_ICON_CLASS)).toBe(false);
128
- expect(maskOf(plain.el).includes('m%203%203')).toBe(true);
129
- plain.host.remove();
130
-
131
- const symbolic = mount(`${NAME}-symbolic`);
132
- expect(symbolic.el.classList.contains(COLOR_ICON_CLASS)).toBe(false);
133
- expect(maskOf(symbolic.el).includes('m%203%203')).toBe(true);
134
- expect(getComputedStyle(symbolic.el).backgroundImage).toBe('none');
135
- symbolic.host.remove();
102
+ await it("the theme's X-symbolic answers X before a resource's colour X, as on GTK", async () => {
103
+ // The fixture ships a colour `16x16/actions/go-next.png`; gtkicontheme.c
104
+ // `real_choose_icon` goes theme outer, name inner, and the file is hicolor's.
105
+ await withIcons(() => {
106
+ const { el, host } = mount('go-next');
107
+ expect(el.classList.contains(COLOR_ICON_CLASS)).toBe(false);
108
+ expect(maskClasses(el)).toStrictEqual(['adw-icon--go-next']);
109
+ host.remove();
110
+ });
136
111
  });
137
112
 
138
- await it('X-symbolic falls back to the colour X, and back again once X-symbolic lands', () => {
139
- const NAME = 'gjsify-theme-fallback-probe';
140
- registerColorIcon(NAME, COLOR_SVG);
141
-
142
- const { el, host } = mount(`${NAME}-symbolic`);
143
- expect(drawsInColor(el)).toBe(true);
144
- expect(isIconAvailable(`${NAME}-symbolic`)).toBe(true);
145
-
146
- registerIcon(NAME, SYMBOLIC_SVG);
147
- expect(el.classList.contains(COLOR_ICON_CLASS)).toBe(false);
148
- expect(maskOf(el).includes('m%203%203')).toBe(true);
149
- host.remove();
113
+ await it('X-symbolic falls back to the colour X', async () => {
114
+ await withIcons(() => {
115
+ const { el, host } = mount('flat-symbolic');
116
+ expect(drawsInColor(el)).toBe(true);
117
+ expect(isIconAvailable('flat-symbolic')).toBe(true);
118
+ host.remove();
119
+ });
150
120
  });
151
121
 
152
122
  await it('an unknown name draws image-missing', () => {
153
- for (const unknown of ['org.gjsify.NeverRegistered', 'gjsify-theme-never-registered']) {
123
+ for (const unknown of ['org.gjsify.NeverShipped', 'gjsify-theme-never-shipped', 'flat']) {
154
124
  const { el, host } = mount(unknown);
155
125
  expect(el.classList.contains(COLOR_ICON_CLASS)).toBe(false);
156
126
  expect(maskOf(el)).toBe(fallbackMask());
@@ -159,29 +129,4 @@ export const AdwIconThemeTest = async () => {
159
129
  }
160
130
  });
161
131
  });
162
-
163
- await describe('registerColorIcon rejects what it cannot draw faithfully', async () => {
164
- await it('throws on a name that is empty, spaced, or symbolic', () => {
165
- expect(() => registerColorIcon('', COLOR_SVG)).toThrow();
166
- expect(() => registerColorIcon('a b', COLOR_SVG)).toThrow();
167
- // The symbolic half has its own door; GTK's theme treats all three suffixes as
168
- // symbolic files.
169
- expect(() => registerColorIcon('gjsify-probe-symbolic', COLOR_SVG)).toThrow();
170
- expect(() => registerColorIcon('gjsify-probe-symbolic-ltr', COLOR_SVG)).toThrow();
171
- expect(() => registerColorIcon('gjsify-probe-symbolic-rtl', COLOR_SVG)).toThrow();
172
- });
173
-
174
- await it('throws on a source that is not SVG or an image URL, and registers nothing', () => {
175
- const REJECTED = 'org.gjsify.RejectedColorProbe';
176
- expect(() => registerColorIcon(REJECTED, '')).toThrow();
177
- expect(() => registerColorIcon(REJECTED, '<p>not svg</p>')).toThrow();
178
- expect(() => registerColorIcon(REJECTED, 'javascript:alert(1)')).toThrow();
179
- expect(() => registerColorIcon(REJECTED, 'data:text/html,<svg></svg>')).toThrow();
180
- expect(isIconAvailable(REJECTED)).toBe(false);
181
-
182
- const { el, host } = mount(REJECTED);
183
- expect(maskOf(el)).toBe(fallbackMask());
184
- host.remove();
185
- });
186
- });
187
132
  };