ngwr 10.2.1 → 11.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (425) hide show
  1. package/README.md +39 -19
  2. package/anchor/styles/_index.scss +4 -4
  3. package/back-top/styles/_index.scss +6 -1
  4. package/blur-text/styles/_index.scss +13 -0
  5. package/button/styles/_index.scss +11 -6
  6. package/carousel/styles/_index.scss +13 -2
  7. package/cascader/styles/_index.scss +2 -2
  8. package/circular-text/styles/_index.scss +2 -0
  9. package/collapse/styles/_index.scss +1 -1
  10. package/color-picker/styles/_index.scss +6 -1
  11. package/compare/styles/_index.scss +1 -2
  12. package/context-menu/styles/_index.scss +6 -1
  13. package/descriptions/styles/_index.scss +2 -2
  14. package/dialog/styles/_index.scss +7 -0
  15. package/drawer/styles/_index.scss +20 -1
  16. package/dropdown/styles/_index.scss +19 -5
  17. package/empty/styles/_index.scss +10 -0
  18. package/fesm2022/ngwr-action-sheet.mjs +13 -3
  19. package/fesm2022/ngwr-action-sheet.mjs.map +1 -1
  20. package/fesm2022/ngwr-affix.mjs +40 -13
  21. package/fesm2022/ngwr-affix.mjs.map +1 -1
  22. package/fesm2022/ngwr-anchor.mjs +18 -6
  23. package/fesm2022/ngwr-anchor.mjs.map +1 -1
  24. package/fesm2022/ngwr-aurora.mjs +2 -2
  25. package/fesm2022/ngwr-aurora.mjs.map +1 -1
  26. package/fesm2022/ngwr-avatar.mjs +31 -7
  27. package/fesm2022/ngwr-avatar.mjs.map +1 -1
  28. package/fesm2022/ngwr-back-top.mjs +6 -2
  29. package/fesm2022/ngwr-back-top.mjs.map +1 -1
  30. package/fesm2022/ngwr-badge.mjs +1 -1
  31. package/fesm2022/ngwr-badge.mjs.map +1 -1
  32. package/fesm2022/ngwr-bar-chart.mjs +30 -9
  33. package/fesm2022/ngwr-bar-chart.mjs.map +1 -1
  34. package/fesm2022/ngwr-blur-text.mjs +2 -2
  35. package/fesm2022/ngwr-blur-text.mjs.map +1 -1
  36. package/fesm2022/ngwr-border-glow.mjs +2 -2
  37. package/fesm2022/ngwr-border-glow.mjs.map +1 -1
  38. package/fesm2022/ngwr-breadcrumbs.mjs +2 -2
  39. package/fesm2022/ngwr-breadcrumbs.mjs.map +1 -1
  40. package/fesm2022/ngwr-burger.mjs +9 -4
  41. package/fesm2022/ngwr-burger.mjs.map +1 -1
  42. package/fesm2022/ngwr-button-testing.mjs +96 -0
  43. package/fesm2022/ngwr-button-testing.mjs.map +1 -0
  44. package/fesm2022/ngwr-button.mjs +31 -14
  45. package/fesm2022/ngwr-button.mjs.map +1 -1
  46. package/fesm2022/ngwr-calendar-heatmap.mjs +40 -8
  47. package/fesm2022/ngwr-calendar-heatmap.mjs.map +1 -1
  48. package/fesm2022/ngwr-calendar.mjs +20 -1
  49. package/fesm2022/ngwr-calendar.mjs.map +1 -1
  50. package/fesm2022/ngwr-card.mjs +2 -2
  51. package/fesm2022/ngwr-card.mjs.map +1 -1
  52. package/fesm2022/ngwr-carousel-testing.mjs +377 -0
  53. package/fesm2022/ngwr-carousel-testing.mjs.map +1 -0
  54. package/fesm2022/ngwr-carousel.mjs +67 -13
  55. package/fesm2022/ngwr-carousel.mjs.map +1 -1
  56. package/fesm2022/ngwr-cascader-testing.mjs +534 -0
  57. package/fesm2022/ngwr-cascader-testing.mjs.map +1 -0
  58. package/fesm2022/ngwr-cascader.mjs +2 -2
  59. package/fesm2022/ngwr-cascader.mjs.map +1 -1
  60. package/fesm2022/ngwr-checkbox-testing.mjs +90 -0
  61. package/fesm2022/ngwr-checkbox-testing.mjs.map +1 -0
  62. package/fesm2022/ngwr-checkbox.mjs +9 -3
  63. package/fesm2022/ngwr-checkbox.mjs.map +1 -1
  64. package/fesm2022/ngwr-circular-text.mjs +2 -2
  65. package/fesm2022/ngwr-circular-text.mjs.map +1 -1
  66. package/fesm2022/ngwr-click-spark.mjs +2 -2
  67. package/fesm2022/ngwr-click-spark.mjs.map +1 -1
  68. package/fesm2022/ngwr-collapse-testing.mjs +368 -0
  69. package/fesm2022/ngwr-collapse-testing.mjs.map +1 -0
  70. package/fesm2022/ngwr-color-picker.mjs +134 -14
  71. package/fesm2022/ngwr-color-picker.mjs.map +1 -1
  72. package/fesm2022/ngwr-command-palette-testing.mjs +589 -0
  73. package/fesm2022/ngwr-command-palette-testing.mjs.map +1 -0
  74. package/fesm2022/ngwr-command-palette.mjs +58 -8
  75. package/fesm2022/ngwr-command-palette.mjs.map +1 -1
  76. package/fesm2022/ngwr-compare.mjs +103 -13
  77. package/fesm2022/ngwr-compare.mjs.map +1 -1
  78. package/fesm2022/ngwr-config.mjs +106 -0
  79. package/fesm2022/ngwr-config.mjs.map +1 -0
  80. package/fesm2022/ngwr-context-menu-testing.mjs +631 -0
  81. package/fesm2022/ngwr-context-menu-testing.mjs.map +1 -0
  82. package/fesm2022/ngwr-context-menu.mjs +94 -8
  83. package/fesm2022/ngwr-context-menu.mjs.map +1 -1
  84. package/fesm2022/ngwr-cookie.mjs +5 -1
  85. package/fesm2022/ngwr-cookie.mjs.map +1 -1
  86. package/fesm2022/ngwr-counter.mjs +11 -3
  87. package/fesm2022/ngwr-counter.mjs.map +1 -1
  88. package/fesm2022/ngwr-date-adapter-luxon.mjs +52 -4
  89. package/fesm2022/ngwr-date-adapter-luxon.mjs.map +1 -1
  90. package/fesm2022/ngwr-date-adapter.mjs +54 -8
  91. package/fesm2022/ngwr-date-adapter.mjs.map +1 -1
  92. package/fesm2022/ngwr-date-picker-testing.mjs +766 -0
  93. package/fesm2022/ngwr-date-picker-testing.mjs.map +1 -0
  94. package/fesm2022/ngwr-date-picker.mjs +73 -3
  95. package/fesm2022/ngwr-date-picker.mjs.map +1 -1
  96. package/fesm2022/ngwr-decrypt-text.mjs +27 -9
  97. package/fesm2022/ngwr-decrypt-text.mjs.map +1 -1
  98. package/fesm2022/ngwr-dialog-testing.mjs +89 -0
  99. package/fesm2022/ngwr-dialog-testing.mjs.map +1 -0
  100. package/fesm2022/ngwr-directives.mjs +36 -12
  101. package/fesm2022/ngwr-directives.mjs.map +1 -1
  102. package/fesm2022/ngwr-donut-chart.mjs +22 -5
  103. package/fesm2022/ngwr-donut-chart.mjs.map +1 -1
  104. package/fesm2022/ngwr-drag-drop.mjs +2 -2
  105. package/fesm2022/ngwr-drag-drop.mjs.map +1 -1
  106. package/fesm2022/ngwr-drawer-testing.mjs +250 -0
  107. package/fesm2022/ngwr-drawer-testing.mjs.map +1 -0
  108. package/fesm2022/ngwr-dropdown-testing.mjs +291 -0
  109. package/fesm2022/ngwr-dropdown-testing.mjs.map +1 -0
  110. package/fesm2022/ngwr-dropdown.mjs +10 -2
  111. package/fesm2022/ngwr-dropdown.mjs.map +1 -1
  112. package/fesm2022/ngwr-empty.mjs +24 -7
  113. package/fesm2022/ngwr-empty.mjs.map +1 -1
  114. package/fesm2022/ngwr-event-calendar.mjs +57 -10
  115. package/fesm2022/ngwr-event-calendar.mjs.map +1 -1
  116. package/fesm2022/ngwr-falling-text.mjs +2 -2
  117. package/fesm2022/ngwr-falling-text.mjs.map +1 -1
  118. package/fesm2022/ngwr-file-upload-testing.mjs +332 -0
  119. package/fesm2022/ngwr-file-upload-testing.mjs.map +1 -0
  120. package/fesm2022/ngwr-form-testing.mjs +451 -0
  121. package/fesm2022/ngwr-form-testing.mjs.map +1 -0
  122. package/fesm2022/ngwr-form.mjs +19 -3
  123. package/fesm2022/ngwr-form.mjs.map +1 -1
  124. package/fesm2022/ngwr-fuzzy-text.mjs +9 -2
  125. package/fesm2022/ngwr-fuzzy-text.mjs.map +1 -1
  126. package/fesm2022/ngwr-gauge.mjs +19 -5
  127. package/fesm2022/ngwr-gauge.mjs.map +1 -1
  128. package/fesm2022/ngwr-glitch-text.mjs +2 -2
  129. package/fesm2022/ngwr-glitch-text.mjs.map +1 -1
  130. package/fesm2022/ngwr-gradient-text.mjs +2 -2
  131. package/fesm2022/ngwr-gradient-text.mjs.map +1 -1
  132. package/fesm2022/ngwr-i18n-en.mjs +50 -0
  133. package/fesm2022/ngwr-i18n-en.mjs.map +1 -1
  134. package/fesm2022/ngwr-i18n-ru.mjs +48 -0
  135. package/fesm2022/ngwr-i18n-ru.mjs.map +1 -1
  136. package/fesm2022/ngwr-image-cropper.mjs +23 -0
  137. package/fesm2022/ngwr-image-cropper.mjs.map +1 -1
  138. package/fesm2022/ngwr-input-number-testing.mjs +306 -0
  139. package/fesm2022/ngwr-input-number-testing.mjs.map +1 -0
  140. package/fesm2022/ngwr-input-number.mjs +35 -3
  141. package/fesm2022/ngwr-input-number.mjs.map +1 -1
  142. package/fesm2022/ngwr-input-otp-testing.mjs +419 -0
  143. package/fesm2022/ngwr-input-otp-testing.mjs.map +1 -0
  144. package/fesm2022/ngwr-input-otp.mjs +36 -5
  145. package/fesm2022/ngwr-input-otp.mjs.map +1 -1
  146. package/fesm2022/ngwr-input-testing.mjs +102 -0
  147. package/fesm2022/ngwr-input-testing.mjs.map +1 -0
  148. package/fesm2022/ngwr-input.mjs +47 -10
  149. package/fesm2022/ngwr-input.mjs.map +1 -1
  150. package/fesm2022/ngwr-knob.mjs +44 -8
  151. package/fesm2022/ngwr-knob.mjs.map +1 -1
  152. package/fesm2022/ngwr-layout.mjs +21 -5
  153. package/fesm2022/ngwr-layout.mjs.map +1 -1
  154. package/fesm2022/ngwr-lightbox.mjs +33 -6
  155. package/fesm2022/ngwr-lightbox.mjs.map +1 -1
  156. package/fesm2022/ngwr-line-chart.mjs +17 -4
  157. package/fesm2022/ngwr-line-chart.mjs.map +1 -1
  158. package/fesm2022/ngwr-list.mjs +2 -2
  159. package/fesm2022/ngwr-list.mjs.map +1 -1
  160. package/fesm2022/ngwr-loading-bar.mjs +2 -2
  161. package/fesm2022/ngwr-loading-bar.mjs.map +1 -1
  162. package/fesm2022/ngwr-markdown-testing.mjs +516 -0
  163. package/fesm2022/ngwr-markdown-testing.mjs.map +1 -0
  164. package/fesm2022/ngwr-markdown.mjs +1492 -0
  165. package/fesm2022/ngwr-markdown.mjs.map +1 -0
  166. package/fesm2022/ngwr-marquee.mjs +17 -6
  167. package/fesm2022/ngwr-marquee.mjs.map +1 -1
  168. package/fesm2022/ngwr-mention-testing.mjs +513 -0
  169. package/fesm2022/ngwr-mention-testing.mjs.map +1 -0
  170. package/fesm2022/ngwr-mention.mjs +10 -1
  171. package/fesm2022/ngwr-mention.mjs.map +1 -1
  172. package/fesm2022/ngwr-meta.mjs +22 -7
  173. package/fesm2022/ngwr-meta.mjs.map +1 -1
  174. package/fesm2022/ngwr-meter-group.mjs +19 -5
  175. package/fesm2022/ngwr-meter-group.mjs.map +1 -1
  176. package/fesm2022/ngwr-overlay.mjs +41 -1
  177. package/fesm2022/ngwr-overlay.mjs.map +1 -1
  178. package/fesm2022/ngwr-pagination-testing.mjs +389 -0
  179. package/fesm2022/ngwr-pagination-testing.mjs.map +1 -0
  180. package/fesm2022/ngwr-pagination.mjs +14 -2
  181. package/fesm2022/ngwr-pagination.mjs.map +1 -1
  182. package/fesm2022/ngwr-popconfirm-testing.mjs +309 -0
  183. package/fesm2022/ngwr-popconfirm-testing.mjs.map +1 -0
  184. package/fesm2022/ngwr-popconfirm.mjs +95 -17
  185. package/fesm2022/ngwr-popconfirm.mjs.map +1 -1
  186. package/fesm2022/ngwr-popover-testing.mjs +316 -0
  187. package/fesm2022/ngwr-popover-testing.mjs.map +1 -0
  188. package/fesm2022/ngwr-popover.mjs +21 -3
  189. package/fesm2022/ngwr-popover.mjs.map +1 -1
  190. package/fesm2022/ngwr-pull-to-refresh.mjs +17 -2
  191. package/fesm2022/ngwr-pull-to-refresh.mjs.map +1 -1
  192. package/fesm2022/ngwr-qr.mjs +13 -3
  193. package/fesm2022/ngwr-qr.mjs.map +1 -1
  194. package/fesm2022/ngwr-radio-testing.mjs +392 -0
  195. package/fesm2022/ngwr-radio-testing.mjs.map +1 -0
  196. package/fesm2022/ngwr-radio.mjs +10 -3
  197. package/fesm2022/ngwr-radio.mjs.map +1 -1
  198. package/fesm2022/ngwr-rating-testing.mjs +384 -0
  199. package/fesm2022/ngwr-rating-testing.mjs.map +1 -0
  200. package/fesm2022/ngwr-rating.mjs +37 -2
  201. package/fesm2022/ngwr-rating.mjs.map +1 -1
  202. package/fesm2022/ngwr-segmented-testing.mjs +512 -0
  203. package/fesm2022/ngwr-segmented-testing.mjs.map +1 -0
  204. package/fesm2022/ngwr-select-testing.mjs +268 -0
  205. package/fesm2022/ngwr-select-testing.mjs.map +1 -0
  206. package/fesm2022/ngwr-select.mjs +87 -16
  207. package/fesm2022/ngwr-select.mjs.map +1 -1
  208. package/fesm2022/ngwr-shiny-text.mjs +2 -2
  209. package/fesm2022/ngwr-shiny-text.mjs.map +1 -1
  210. package/fesm2022/ngwr-sidebar.mjs +65 -12
  211. package/fesm2022/ngwr-sidebar.mjs.map +1 -1
  212. package/fesm2022/ngwr-slider-testing.mjs +478 -0
  213. package/fesm2022/ngwr-slider-testing.mjs.map +1 -0
  214. package/fesm2022/ngwr-slider.mjs +57 -13
  215. package/fesm2022/ngwr-slider.mjs.map +1 -1
  216. package/fesm2022/ngwr-sparkline.mjs +26 -6
  217. package/fesm2022/ngwr-sparkline.mjs.map +1 -1
  218. package/fesm2022/ngwr-speed-dial.mjs +37 -4
  219. package/fesm2022/ngwr-speed-dial.mjs.map +1 -1
  220. package/fesm2022/ngwr-spinner.mjs +12 -3
  221. package/fesm2022/ngwr-spinner.mjs.map +1 -1
  222. package/fesm2022/ngwr-splash-cursor.mjs +2 -2
  223. package/fesm2022/ngwr-splash-cursor.mjs.map +1 -1
  224. package/fesm2022/ngwr-split-text.mjs +2 -2
  225. package/fesm2022/ngwr-split-text.mjs.map +1 -1
  226. package/fesm2022/ngwr-splitter.mjs +70 -9
  227. package/fesm2022/ngwr-splitter.mjs.map +1 -1
  228. package/fesm2022/ngwr-spotlight-card.mjs +2 -2
  229. package/fesm2022/ngwr-spotlight-card.mjs.map +1 -1
  230. package/fesm2022/ngwr-star-border.mjs +2 -2
  231. package/fesm2022/ngwr-star-border.mjs.map +1 -1
  232. package/fesm2022/ngwr-statistic.mjs +24 -7
  233. package/fesm2022/ngwr-statistic.mjs.map +1 -1
  234. package/fesm2022/ngwr-stepper-testing.mjs +477 -0
  235. package/fesm2022/ngwr-stepper-testing.mjs.map +1 -0
  236. package/fesm2022/ngwr-switch-testing.mjs +77 -0
  237. package/fesm2022/ngwr-switch-testing.mjs.map +1 -0
  238. package/fesm2022/ngwr-switch.mjs +9 -3
  239. package/fesm2022/ngwr-switch.mjs.map +1 -1
  240. package/fesm2022/ngwr-table-testing.mjs +580 -0
  241. package/fesm2022/ngwr-table-testing.mjs.map +1 -0
  242. package/fesm2022/ngwr-table.mjs +87 -14
  243. package/fesm2022/ngwr-table.mjs.map +1 -1
  244. package/fesm2022/ngwr-tabs-testing.mjs +617 -0
  245. package/fesm2022/ngwr-tabs-testing.mjs.map +1 -0
  246. package/fesm2022/ngwr-tabs.mjs +38 -6
  247. package/fesm2022/ngwr-tabs.mjs.map +1 -1
  248. package/fesm2022/ngwr-textarea-testing.mjs +238 -0
  249. package/fesm2022/ngwr-textarea-testing.mjs.map +1 -0
  250. package/fesm2022/ngwr-textarea.mjs +32 -4
  251. package/fesm2022/ngwr-textarea.mjs.map +1 -1
  252. package/fesm2022/ngwr-tilt-card.mjs +2 -2
  253. package/fesm2022/ngwr-tilt-card.mjs.map +1 -1
  254. package/fesm2022/ngwr-toast-testing.mjs +101 -0
  255. package/fesm2022/ngwr-toast-testing.mjs.map +1 -0
  256. package/fesm2022/ngwr-toast.mjs +2 -2
  257. package/fesm2022/ngwr-toast.mjs.map +1 -1
  258. package/fesm2022/ngwr-tour.mjs +61 -9
  259. package/fesm2022/ngwr-tour.mjs.map +1 -1
  260. package/fesm2022/ngwr-transfer-testing.mjs +538 -0
  261. package/fesm2022/ngwr-transfer-testing.mjs.map +1 -0
  262. package/fesm2022/ngwr-transfer.mjs +45 -14
  263. package/fesm2022/ngwr-transfer.mjs.map +1 -1
  264. package/fesm2022/ngwr-tree-testing.mjs +734 -0
  265. package/fesm2022/ngwr-tree-testing.mjs.map +1 -0
  266. package/fesm2022/ngwr-tree.mjs +73 -7
  267. package/fesm2022/ngwr-tree.mjs.map +1 -1
  268. package/fesm2022/ngwr-typewriter.mjs +5 -3
  269. package/fesm2022/ngwr-typewriter.mjs.map +1 -1
  270. package/fesm2022/ngwr-version.mjs +1 -1
  271. package/fesm2022/ngwr-version.mjs.map +1 -1
  272. package/fesm2022/ngwr-virtual-scroll.mjs +31 -5
  273. package/fesm2022/ngwr-virtual-scroll.mjs.map +1 -1
  274. package/fesm2022/ngwr-waves.mjs +3 -3
  275. package/fesm2022/ngwr-waves.mjs.map +1 -1
  276. package/fesm2022/ngwr-window.mjs +8 -0
  277. package/fesm2022/ngwr-window.mjs.map +1 -1
  278. package/gauge/styles/_index.scss +1 -1
  279. package/glitch-text/styles/_index.scss +4 -0
  280. package/i18n/en.json +48 -0
  281. package/i18n/ru.json +48 -0
  282. package/image-cropper/styles/_index.scss +13 -1
  283. package/input/styles/_index.scss +2 -2
  284. package/input-number/styles/_index.scss +7 -2
  285. package/knob/styles/_index.scss +8 -1
  286. package/layout/styles/_index.scss +3 -3
  287. package/lightbox/styles/_index.scss +10 -2
  288. package/line-chart/styles/_index.scss +6 -0
  289. package/llms-full.txt +151 -4
  290. package/llms.txt +57 -8
  291. package/loading-bar/styles/_index.scss +1 -2
  292. package/markdown/styles/_index.scss +296 -0
  293. package/marquee/styles/_index.scss +12 -0
  294. package/mcp/api.js +512 -0
  295. package/mcp/catalog.js +157 -0
  296. package/mcp/server.js +273 -0
  297. package/mcp/tools.js +366 -0
  298. package/meter-group/styles/_index.scss +1 -1
  299. package/package.json +186 -35
  300. package/pagination/styles/_index.scss +6 -6
  301. package/popover/styles/_index.scss +37 -5
  302. package/rating/styles/_index.scss +10 -0
  303. package/schematics/use/symbol-map.json +2 -0
  304. package/segmented/styles/_index.scss +7 -0
  305. package/select/styles/_index.scss +29 -9
  306. package/sidebar/styles/_index.scss +30 -5
  307. package/slider/styles/_index.scss +15 -0
  308. package/speed-dial/styles/_index.scss +20 -0
  309. package/split-text/styles/_index.scss +13 -0
  310. package/star-border/styles/_index.scss +2 -0
  311. package/stepper/styles/_index.scss +1 -1
  312. package/styles/_grid.scss +4 -4
  313. package/styles.scss +1 -0
  314. package/switch/styles/_index.scss +5 -3
  315. package/table/styles/_index.scss +22 -11
  316. package/tabs/styles/_index.scss +11 -1
  317. package/textarea/styles/_index.scss +5 -0
  318. package/theme/styles/_colors.scss +29 -13
  319. package/theme/styles/_dark.scss +11 -3
  320. package/theme/styles/_mixins.scss +3 -0
  321. package/timeline/styles/_index.scss +2 -2
  322. package/toast/styles/_index.scss +17 -8
  323. package/toolbar/styles/_index.scss +3 -3
  324. package/tour/styles/_index.scss +7 -1
  325. package/tree/styles/_index.scss +5 -3
  326. package/types/ngwr-action-sheet.d.ts +9 -1
  327. package/types/ngwr-affix.d.ts +7 -1
  328. package/types/ngwr-anchor.d.ts +8 -1
  329. package/types/ngwr-avatar.d.ts +13 -3
  330. package/types/ngwr-bar-chart.d.ts +16 -0
  331. package/types/ngwr-burger.d.ts +6 -2
  332. package/types/ngwr-button-testing.d.ts +85 -0
  333. package/types/ngwr-button.d.ts +26 -10
  334. package/types/ngwr-calendar-heatmap.d.ts +16 -2
  335. package/types/ngwr-calendar.d.ts +10 -0
  336. package/types/ngwr-carousel-testing.d.ts +257 -0
  337. package/types/ngwr-carousel.d.ts +20 -4
  338. package/types/ngwr-cascader-testing.d.ts +392 -0
  339. package/types/ngwr-checkbox-testing.d.ts +77 -0
  340. package/types/ngwr-checkbox.d.ts +7 -2
  341. package/types/ngwr-collapse-testing.d.ts +290 -0
  342. package/types/ngwr-color-picker.d.ts +20 -11
  343. package/types/ngwr-command-palette-testing.d.ts +437 -0
  344. package/types/ngwr-command-palette.d.ts +25 -2
  345. package/types/ngwr-compare.d.ts +50 -2
  346. package/types/ngwr-config.d.ts +176 -0
  347. package/types/ngwr-context-menu-testing.d.ts +401 -0
  348. package/types/ngwr-context-menu.d.ts +65 -17
  349. package/types/ngwr-counter.d.ts +6 -1
  350. package/types/ngwr-date-adapter-luxon.d.ts +26 -0
  351. package/types/ngwr-date-picker-testing.d.ts +518 -0
  352. package/types/ngwr-date-picker.d.ts +4 -0
  353. package/types/ngwr-dialog-testing.d.ts +79 -0
  354. package/types/ngwr-directives.d.ts +10 -1
  355. package/types/ngwr-donut-chart.d.ts +8 -1
  356. package/types/ngwr-drawer-testing.d.ts +196 -0
  357. package/types/ngwr-dropdown-testing.d.ts +202 -0
  358. package/types/ngwr-dropdown.d.ts +7 -1
  359. package/types/ngwr-empty.d.ts +19 -4
  360. package/types/ngwr-event-calendar.d.ts +26 -1
  361. package/types/ngwr-file-upload-testing.d.ts +194 -0
  362. package/types/ngwr-form-testing.d.ts +379 -0
  363. package/types/ngwr-form.d.ts +17 -0
  364. package/types/ngwr-gauge.d.ts +13 -1
  365. package/types/ngwr-image-cropper.d.ts +10 -0
  366. package/types/ngwr-input-number-testing.d.ts +226 -0
  367. package/types/ngwr-input-number.d.ts +29 -1
  368. package/types/ngwr-input-otp-testing.d.ts +299 -0
  369. package/types/ngwr-input-otp.d.ts +23 -2
  370. package/types/ngwr-input-testing.d.ts +84 -0
  371. package/types/ngwr-input.d.ts +20 -6
  372. package/types/ngwr-knob.d.ts +18 -0
  373. package/types/ngwr-layout.d.ts +1 -0
  374. package/types/ngwr-lightbox.d.ts +17 -3
  375. package/types/ngwr-line-chart.d.ts +9 -2
  376. package/types/ngwr-markdown-testing.d.ts +462 -0
  377. package/types/ngwr-markdown.d.ts +534 -0
  378. package/types/ngwr-marquee.d.ts +12 -2
  379. package/types/ngwr-mention-testing.d.ts +359 -0
  380. package/types/ngwr-meta.d.ts +4 -2
  381. package/types/ngwr-meter-group.d.ts +11 -1
  382. package/types/ngwr-overlay.d.ts +33 -2
  383. package/types/ngwr-pagination-testing.d.ts +291 -0
  384. package/types/ngwr-pagination.d.ts +1 -0
  385. package/types/ngwr-popconfirm-testing.d.ts +235 -0
  386. package/types/ngwr-popconfirm.d.ts +31 -21
  387. package/types/ngwr-popover-testing.d.ts +221 -0
  388. package/types/ngwr-popover.d.ts +9 -0
  389. package/types/ngwr-pull-to-refresh.d.ts +7 -0
  390. package/types/ngwr-qr.d.ts +9 -1
  391. package/types/ngwr-radio-testing.d.ts +296 -0
  392. package/types/ngwr-radio.d.ts +8 -2
  393. package/types/ngwr-rating-testing.d.ts +291 -0
  394. package/types/ngwr-rating.d.ts +23 -0
  395. package/types/ngwr-segmented-testing.d.ts +401 -0
  396. package/types/ngwr-select-testing.d.ts +175 -0
  397. package/types/ngwr-select.d.ts +45 -4
  398. package/types/ngwr-sidebar.d.ts +17 -2
  399. package/types/ngwr-slider-testing.d.ts +329 -0
  400. package/types/ngwr-slider.d.ts +35 -1
  401. package/types/ngwr-sparkline.d.ts +12 -2
  402. package/types/ngwr-speed-dial.d.ts +17 -18
  403. package/types/ngwr-spinner.d.ts +8 -1
  404. package/types/ngwr-splitter.d.ts +24 -1
  405. package/types/ngwr-statistic.d.ts +12 -2
  406. package/types/ngwr-stepper-testing.d.ts +346 -0
  407. package/types/ngwr-switch-testing.d.ts +66 -0
  408. package/types/ngwr-switch.d.ts +7 -2
  409. package/types/ngwr-table-testing.d.ts +423 -0
  410. package/types/ngwr-table.d.ts +42 -0
  411. package/types/ngwr-tabs-testing.d.ts +470 -0
  412. package/types/ngwr-tabs.d.ts +9 -0
  413. package/types/ngwr-textarea-testing.d.ts +184 -0
  414. package/types/ngwr-textarea.d.ts +7 -2
  415. package/types/ngwr-toast-testing.d.ts +80 -0
  416. package/types/ngwr-tour.d.ts +27 -0
  417. package/types/ngwr-transfer-testing.d.ts +444 -0
  418. package/types/ngwr-transfer.d.ts +16 -1
  419. package/types/ngwr-tree-testing.d.ts +501 -0
  420. package/types/ngwr-tree.d.ts +33 -0
  421. package/types/ngwr-version.d.ts +1 -1
  422. package/types/ngwr-virtual-scroll.d.ts +13 -1
  423. package/typewriter/styles/_index.scss +1 -1
  424. package/typography/styles/_index.scss +1 -1
  425. package/window/styles/_index.scss +32 -9
@@ -0,0 +1,516 @@
1
+ import { ComponentHarness, HarnessPredicate } from '@angular/cdk/testing';
2
+
3
+ /**
4
+ * @license
5
+ *
6
+ * Use of this source code is governed by an MIT-style license that can be
7
+ * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE
8
+ */
9
+ /**
10
+ * Test harness for one fenced code block of a `<wr-markdown>` document.
11
+ *
12
+ * A block of its own rather than a field on the document harness, because it is
13
+ * the one piece of rendered markdown that has BEHAVIOUR: it carries a language, it
14
+ * may or may not be coloured yet, and it may offer a copy button — three states
15
+ * that belong to the block and not to the page it is on.
16
+ *
17
+ * Its host is the `.wr-markdown__code-block` wrapper, which is what holds the
18
+ * `<pre>` and the copy button together. Blocks nested inside a quote or a list item
19
+ * are blocks too, and this harness finds them: `<wr-markdown>` renders them the
20
+ * same way, and the copy affordance is theirs as well.
21
+ *
22
+ * @example
23
+ * ```ts
24
+ * const block = await (await loader.getHarness(WrMarkdownHarness)).getCodeBlock({ language: 'ts' });
25
+ *
26
+ * expect(await block.getCode()).toBe('const a = 1;');
27
+ * await block.copy();
28
+ * ```
29
+ *
30
+ * @see https://ngwr.dev/reference/components/markdown
31
+ * @see https://ngwr.dev/guides/testing
32
+ */
33
+ class WrMarkdownCodeBlockHarness extends ComponentHarness {
34
+ static hostSelector = '.wr-markdown__code-block';
35
+ /** Build a predicate that narrows the query — pass to `getHarness` / `getAllHarnesses`. */
36
+ static with(options = {}) {
37
+ return new HarnessPredicate(WrMarkdownCodeBlockHarness, options)
38
+ .addOption('language', options.language, (harness, language) => HarnessPredicate.stringMatches(harness.getLanguage(), language))
39
+ .addOption('code', options.code, (harness, code) => HarnessPredicate.stringMatches(harness.getCode(), code));
40
+ }
41
+ pre = this.locatorFor('pre.wr-markdown__pre');
42
+ code = this.locatorFor('code.wr-markdown__code');
43
+ copyButton = this.locatorForOptional('button.wr-markdown__copy');
44
+ /**
45
+ * The block's language — the info string's first word, lowercased — or `null` for
46
+ * a bare fence.
47
+ *
48
+ * Read from `data-language`, which is the only place it reaches the DOM, and the
49
+ * reason it is there at all: a highlighter picks a grammar by it, and the
50
+ * showcase's markdown export needs it to re-fence the block. `null` is a real
51
+ * answer, not a gap: a bare fence names no grammar, so nothing is ever asked to
52
+ * colour it.
53
+ */
54
+ async getLanguage() {
55
+ return (await this.code()).getAttribute('data-language');
56
+ }
57
+ /**
58
+ * The block's code, EXACTLY — every space, every blank line, every newline.
59
+ *
60
+ * Nothing is collapsed here, unlike every prose read on the document harness,
61
+ * and it is read off the `<pre>` rather than off the host so the copy button is
62
+ * not part of the answer. Two failures live in this one string. Angular strips
63
+ * whitespace-only text nodes from templates, so the code has to arrive as an
64
+ * interpolation — a template that types it instead loses the indentation. And a
65
+ * HIGHLIGHTED block is not one text node but a span per token: what separates its
66
+ * lines is a real `\n` interpolated between them, so a spec that only ever
67
+ * asserts the unhighlighted case would not notice a highlighted block copying out
68
+ * as one run-together line.
69
+ */
70
+ async getCode() {
71
+ return (await (await this.pre()).getProperty('textContent')) ?? '';
72
+ }
73
+ /**
74
+ * Whether the block is coloured right now.
75
+ *
76
+ * A block is plain text until a highlighter answers for it, and that is the
77
+ * intended order rather than a compromise: a grammar engine loads WASM, and
78
+ * blocking on it would leave the reader with an empty box instead of their code.
79
+ * So this is `false` with no `WR_MARKDOWN_HIGHLIGHTER` provided, `false` for a
80
+ * language the highlighter declined or threw on, `false` under SSR — a
81
+ * highlighter resolving after prerender cannot contribute to the HTML — and
82
+ * `false` for the first render of an async one.
83
+ */
84
+ async isHighlighted() {
85
+ return (await this.locatorForOptional('.wr-markdown__line')()) !== null;
86
+ }
87
+ /**
88
+ * Whether this block offers a copy button.
89
+ *
90
+ * Two conditions, and both are the component's policy rather than an accident:
91
+ * `copyable` has to be on (bound, or defaulted through
92
+ * `provideWrConfig({ markdown: { copyable: true } })`), and the fence has to be
93
+ * CLOSED. Mid-stream a block's code is not all there, and a button that copies
94
+ * half a snippet is worse than no button.
95
+ */
96
+ async canCopy() {
97
+ return (await this.copyButton()) !== null;
98
+ }
99
+ /**
100
+ * The copy button's accessible name, or `null` when there is no button.
101
+ *
102
+ * Worth reading rather than assuming, because it is the whole of the copy
103
+ * feedback: the button swaps its label — and its icon — for the "copied" one for
104
+ * two seconds after a successful write, and both strings come from the i18n
105
+ * catalog (`markdown.copy` / `markdown.copied`) or the matching inputs. Which is
106
+ * also why this harness reports the name instead of a `isCopied()` boolean: only
107
+ * the consuming app knows which of its own two strings means which.
108
+ */
109
+ async getCopyLabel() {
110
+ const button = await this.copyButton();
111
+ return button ? button.getAttribute('aria-label') : null;
112
+ }
113
+ /**
114
+ * Copy the block, the way a pointer does.
115
+ *
116
+ * Throws when there is no button, naming both reasons — a silent no-op here
117
+ * surfaces as an empty clipboard assertion several lines later. Note what a
118
+ * successful click needs from the environment: the directive behind the button
119
+ * writes through `navigator.clipboard`, which jsdom does not implement at all, so
120
+ * a unit test has to stand one in before the write can succeed and the label can
121
+ * swap.
122
+ */
123
+ async copy() {
124
+ const button = await this.copyButton();
125
+ if (!button) {
126
+ throw new Error('WrMarkdownCodeBlockHarness.copy(): this block has no copy button. Either `copyable` is off — it ' +
127
+ 'falls back to `markdown.copyable` from provideWrConfig(), then to false — or the fence is still ' +
128
+ 'open, and <wr-markdown> only offers one on a CLOSED block.');
129
+ }
130
+ await button.click();
131
+ }
132
+ }
133
+
134
+ /**
135
+ * @license
136
+ *
137
+ * Use of this source code is governed by an MIT-style license that can be
138
+ * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE
139
+ */
140
+ /**
141
+ * Collapse the whitespace of a prose read to the line a reader sees.
142
+ *
143
+ * `TestElement.text()` only trims the ends, and markdown prose is never one text
144
+ * node: a paragraph is text spliced with `<strong>`, `<code>` and `<a>`, and a
145
+ * SOFT line break inside it is rendered as a real `\n` — the parser keeps it as
146
+ * whitespace and lets CSS collapse it, which is what CommonMark asks for. Reading
147
+ * `textContent` raw therefore reports a paragraph the author wrote on three lines
148
+ * as three lines, while the browser shows one; every run becomes a single space so
149
+ * a spec can assert what is on screen.
150
+ *
151
+ * Deliberately NOT used on a code block. There the whitespace IS the content —
152
+ * indentation, blank lines, the newlines between highlighted lines — so
153
+ * `WrMarkdownCodeBlockHarness.getCode()` reads it exactly and this function never
154
+ * touches it.
155
+ */
156
+ function wrMarkdownHarnessText(text) {
157
+ return text.replace(/\s+/g, ' ').trim();
158
+ }
159
+
160
+ /**
161
+ * @license
162
+ *
163
+ * Use of this source code is governed by an MIT-style license that can be
164
+ * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE
165
+ */
166
+ /**
167
+ * The task-state label — hidden from the page, read out in front of the item.
168
+ *
169
+ * Left out of every prose read here, so a spec asserting an item's text does not
170
+ * have to know that `'Done: '` is spliced in front of it. It is not lost: it is a
171
+ * field of its own on {@link WrMarkdownHarnessTaskItem}, which is where its
172
+ * absence is a failure rather than a nuisance.
173
+ */
174
+ const SR_ONLY = '.wr-markdown__sr-only';
175
+ /** The alignment the renderer wrote as an inline style, if it wrote one. */
176
+ const TEXT_ALIGN_RE = /text-align:\s*([a-z]+)/;
177
+ /** Every prose read goes through here: collapsed, and without the hidden labels. */
178
+ async function proseText(element) {
179
+ return wrMarkdownHarnessText(await element.text({ exclude: SR_ONLY }));
180
+ }
181
+ /**
182
+ * A cell's or a header's alignment, read off the inline `style` attribute.
183
+ *
184
+ * Not through `getCssValue()`, and the difference is not cosmetic: a column the
185
+ * delimiter row said nothing about carries no declaration at all, and computed
186
+ * style answers for it anyway — with whatever the user-agent stylesheet says a
187
+ * `<th>` or a `<td>` is aligned to. That makes "no alignment" indistinguishable
188
+ * from `center`, in the one direction that matters (a `<th>` centres by default),
189
+ * and it makes the answer depend on the browser rather than on the document. The
190
+ * attribute is what the component wrote, which is the fact being asserted.
191
+ */
192
+ async function alignOf(cell) {
193
+ const match = TEXT_ALIGN_RE.exec((await cell.getAttribute('style')) ?? '');
194
+ switch (match?.[1]) {
195
+ case 'start':
196
+ return 'start';
197
+ case 'center':
198
+ return 'center';
199
+ case 'end':
200
+ return 'end';
201
+ default:
202
+ return null;
203
+ }
204
+ }
205
+ /**
206
+ * One body row of a rendered table.
207
+ *
208
+ * Not exported, and not because rows do not matter: a `TestElement` cannot be
209
+ * queried INTO, so reading one row's own cells needs a harness rooted on that row.
210
+ * Nothing here is drivable — rendered prose is inert — so the row's job is to hand
211
+ * its cells up to {@link WrMarkdownHarness.getTables}, which is where a consumer
212
+ * asserts the whole table in one comparison.
213
+ */
214
+ class WrMarkdownTableRowReader extends ComponentHarness {
215
+ /** Body rows only: the header row lives in the `<thead>`. */
216
+ static hostSelector = 'tbody tr';
217
+ async getCellTexts() {
218
+ const cells = await this.locatorForAll('.wr-markdown__td')();
219
+ return Promise.all(cells.map(cell => proseText(cell)));
220
+ }
221
+ }
222
+ /**
223
+ * One rendered task-list item — a harness for the same reason as the row reader.
224
+ *
225
+ * Its three reads have to be SCOPED to the item: querying the tick and the hidden
226
+ * label from the document root would hand every item in the document the first
227
+ * one's state, which is the kind of green spec that reports a broken checklist as
228
+ * intact. A nested task list lives inside its parent `<li>`, and the parent's own
229
+ * tick and label are rendered before it — so the first match under an item is
230
+ * always the item's own.
231
+ */
232
+ class WrMarkdownTaskItemReader extends ComponentHarness {
233
+ static hostSelector = '.wr-markdown__item--task';
234
+ tick = this.locatorForOptional('.wr-markdown__task--checked');
235
+ stateLabel = this.locatorForOptional(SR_ONLY);
236
+ async read() {
237
+ const label = await this.stateLabel();
238
+ return {
239
+ text: await proseText(await this.host()),
240
+ checked: (await this.tick()) !== null,
241
+ stateLabel: label ? await label.text() : null,
242
+ };
243
+ }
244
+ }
245
+ /** One rendered table — see {@link WrMarkdownTableRowReader} for why this is a harness. */
246
+ class WrMarkdownTableReader extends ComponentHarness {
247
+ static hostSelector = 'table.wr-markdown__table';
248
+ async read() {
249
+ const headers = await this.locatorForAll('th.wr-markdown__th')();
250
+ const rows = await this.locatorForAll(WrMarkdownTableRowReader)();
251
+ return {
252
+ headers: await Promise.all(headers.map(header => proseText(header))),
253
+ rows: await Promise.all(rows.map(row => row.getCellTexts())),
254
+ align: await Promise.all(headers.map(header => alignOf(header))),
255
+ };
256
+ }
257
+ }
258
+ /**
259
+ * Test harness for `<wr-markdown>`, with {@link WrMarkdownCodeBlockHarness} for one
260
+ * fenced code block.
261
+ *
262
+ * What a consumer of a markdown renderer actually asserts is that a piece of
263
+ * SOURCE became the right DOM, so everything here reads rendered elements: the
264
+ * heading levels, the `href` / `target` / `rel` triple on a link, the tick state of
265
+ * a task item, a table as a rectangle of text. The parse tree is not exposed on
266
+ * purpose — `parseMarkdown()` is a pure function with its own spec, and a harness
267
+ * that reported its nodes would pass straight through the half that breaks people,
268
+ * which is the template between the tree and the page.
269
+ *
270
+ * Three things worth knowing before writing expectations:
271
+ *
272
+ * - **Prose reads are whitespace-collapsed, code is not.** A soft line break inside
273
+ * a paragraph is a real `\n` in the DOM (CommonMark says CSS collapses it), so
274
+ * every text answer here is normalized to the line a reader sees.
275
+ * {@link WrMarkdownCodeBlockHarness.getCode} is the exception and reads its
276
+ * snippet exactly.
277
+ * - **The hidden task-state labels are left out of prose.** They are announced text,
278
+ * not visible text; {@link getTaskItems} reports them as a field.
279
+ * - **Everything is flat and in document order.** Markdown nests — a quote holds
280
+ * blocks, a list item holds blocks — and these readers do not re-derive that
281
+ * hierarchy: {@link getCodeBlocks} finds a block inside a quote, and a nested
282
+ * list item's text is part of its parent's, because that is what the DOM says.
283
+ *
284
+ * @example
285
+ * ```ts
286
+ * const md = await loader.getHarness(WrMarkdownHarness);
287
+ *
288
+ * expect(await md.getHeadings()).toEqual([{ level: 1, text: 'Release notes', id: 'release-notes' }]);
289
+ * expect(await md.getLinks()).toEqual([
290
+ * { text: 'the docs', href: 'https://ngwr.dev', title: null, target: '_blank', rel: 'noopener noreferrer' },
291
+ * ]);
292
+ * ```
293
+ *
294
+ * @see https://ngwr.dev/reference/components/markdown
295
+ * @see https://ngwr.dev/guides/testing
296
+ */
297
+ class WrMarkdownHarness extends ComponentHarness {
298
+ static hostSelector = 'wr-markdown';
299
+ /** Build a predicate that narrows the query — pass to `getHarness` / `getAllHarnesses`. */
300
+ static with(options = {}) {
301
+ return new HarnessPredicate(WrMarkdownHarness, options)
302
+ .addOption('text', options.text, (harness, text) => HarnessPredicate.stringMatches(harness.getText(), text))
303
+ .addOption('headingText', options.headingText, async (harness, text) => {
304
+ for (const heading of await harness.getHeadings()) {
305
+ if (await HarnessPredicate.stringMatches(heading.text, text))
306
+ return true;
307
+ }
308
+ return false;
309
+ })
310
+ .addOption('codeLanguage', options.codeLanguage, async (harness, language) => {
311
+ for (const block of await harness.getCodeBlocks()) {
312
+ if (await HarnessPredicate.stringMatches(block.getLanguage(), language))
313
+ return true;
314
+ }
315
+ return false;
316
+ })
317
+ .addOption('streaming', options.streaming, async (harness, streaming) => (await harness.isStreaming()) === streaming);
318
+ }
319
+ /**
320
+ * The whole document as text — whitespace collapsed, the hidden task-state labels
321
+ * left out.
322
+ *
323
+ * The coarse read, and the useful one for the security cases: raw HTML in the
324
+ * source is ESCAPED rather than rendered, so `<img onerror=…>` shows up here as
325
+ * characters, and a refused `javascript:` URL leaves its label behind as text with
326
+ * no anchor around it. A code block's newlines are collapsed like everything else
327
+ * — {@link getCodeBlocks} is where a snippet stays exact.
328
+ *
329
+ * It is `textContent`, which means sibling BLOCKS are not separated: a document of
330
+ * `<h1>Title</h1><p>Body</p>` reads as `'TitleBody'`, because the renderer emits
331
+ * no whitespace between them and none is invented here. Assert a fragment with
332
+ * `toContain`, or read the blocks themselves — {@link getHeadings},
333
+ * {@link getParagraphs} — when the boundary matters.
334
+ */
335
+ async getText() {
336
+ return proseText(await this.host());
337
+ }
338
+ /**
339
+ * Whether the component rendered nothing at all.
340
+ *
341
+ * True for an empty `value`, and for one that parses to nothing — a lone `<!--`
342
+ * or whitespace. Distinct from `getText() === ''`, which a document made only of
343
+ * a thematic break or an image would also answer.
344
+ */
345
+ async isEmpty() {
346
+ const host = await this.host();
347
+ if ((await host.getProperty('childElementCount')) > 0)
348
+ return false;
349
+ return (await this.getText()) === '';
350
+ }
351
+ /**
352
+ * Whether the document is being streamed.
353
+ *
354
+ * Read from the `--streaming` host modifier rather than from the input, because
355
+ * the modifier is the whole public API of the feature on the styling side: it is
356
+ * what paints the caret after the last block. What it does NOT say is that
357
+ * partial-safe parsing is on — the same input drives both, but only this reaches
358
+ * the DOM.
359
+ */
360
+ async isStreaming() {
361
+ return (await this.host()).hasClass('wr-markdown--streaming');
362
+ }
363
+ /** Every heading, in document order. */
364
+ async getHeadings() {
365
+ const headings = await this.locatorForAll('.wr-markdown__heading')();
366
+ return Promise.all(headings.map(async (heading) => ({
367
+ // `H3` → 3. The element is where the level lives; see the field's docs.
368
+ level: Number.parseInt((await heading.getProperty('tagName')).slice(1), 10),
369
+ text: await proseText(heading),
370
+ id: await heading.getAttribute('id'),
371
+ })));
372
+ }
373
+ /**
374
+ * The text of every paragraph, in document order.
375
+ *
376
+ * Including the ones inside a LOOSE list item — CommonMark drops the `<p>` in a
377
+ * tight list and keeps it in a loose one, which is what stops a one-line list from
378
+ * rendering double-spaced, so an empty answer for `- a\n- b` is the correct one
379
+ * rather than a missed paragraph.
380
+ */
381
+ async getParagraphs() {
382
+ const paragraphs = await this.locatorForAll('.wr-markdown__paragraph')();
383
+ return Promise.all(paragraphs.map(paragraph => proseText(paragraph)));
384
+ }
385
+ /**
386
+ * Every link, in document order, with the attributes that make it safe.
387
+ *
388
+ * A URL the renderer refused is NOT here — it renders as plain text instead of as
389
+ * a live anchor that announces as a link and does nothing — so an empty answer
390
+ * where a link was expected is the sanitizer speaking, and {@link getText} still
391
+ * has the label.
392
+ */
393
+ async getLinks() {
394
+ const links = await this.locatorForAll('.wr-markdown__link')();
395
+ return Promise.all(links.map(async (link) => ({
396
+ text: await proseText(link),
397
+ href: await link.getAttribute('href'),
398
+ title: await link.getAttribute('title'),
399
+ target: await link.getAttribute('target'),
400
+ rel: await link.getAttribute('rel'),
401
+ })));
402
+ }
403
+ /**
404
+ * Every image, in document order.
405
+ *
406
+ * A refused source renders no image at all and leaves the alt text as prose,
407
+ * which is the information the author actually wrote — so assert this together
408
+ * with {@link getText} when the point is the refusal.
409
+ */
410
+ async getImages() {
411
+ const images = await this.locatorForAll('.wr-markdown__image')();
412
+ return Promise.all(images.map(async (image) => ({
413
+ src: await image.getAttribute('src'),
414
+ alt: await image.getAttribute('alt'),
415
+ title: await image.getAttribute('title'),
416
+ })));
417
+ }
418
+ /**
419
+ * The text of every inline code span, in document order.
420
+ *
421
+ * A different element from a fenced block (`<code>` on its own, not inside a
422
+ * `<pre>`), and never one of {@link getCodeBlocks}. Read exactly rather than
423
+ * collapsed, for the same reason a block is: this is code, and the spacing in it
424
+ * is content.
425
+ */
426
+ async getInlineCode() {
427
+ const spans = await this.locatorForAll('.wr-markdown__code-inline')();
428
+ return Promise.all(spans.map(async (span) => (await span.getProperty('textContent')) ?? ''));
429
+ }
430
+ /**
431
+ * The fenced code blocks, in document order — nested ones included.
432
+ *
433
+ * A block inside a quote or a list item is a block: it renders the same way and it
434
+ * gets the same copy affordance, so leaving it out would report a document as
435
+ * having no code because its snippet is indented under a bullet.
436
+ */
437
+ async getCodeBlocks(filters = {}) {
438
+ return this.locatorForAll(WrMarkdownCodeBlockHarness.with(filters))();
439
+ }
440
+ /**
441
+ * The first code block matching the filters.
442
+ *
443
+ * Throws when nothing matches, naming the languages the document does offer —
444
+ * a `getCodeBlocks(…)[0]` that quietly resolves to `undefined` fails several lines
445
+ * later, on an unrelated line, with `Cannot read properties of undefined`.
446
+ */
447
+ async getCodeBlock(filters = {}) {
448
+ const [block] = await this.getCodeBlocks(filters);
449
+ if (block)
450
+ return block;
451
+ const languages = await Promise.all((await this.getCodeBlocks()).map(other => other.getLanguage()));
452
+ const offered = languages.length > 0
453
+ ? `This document has ${languages.length} block(s), in: ${languages.map(name => name ?? '(none)').join(', ')}.`
454
+ : 'This document renders no code blocks at all.';
455
+ throw new Error(`WrMarkdownHarness.getCodeBlock(): no block matched ${JSON.stringify(filters)}. ${offered}`);
456
+ }
457
+ /**
458
+ * The text of every list item, in document order.
459
+ *
460
+ * Bullets, numbers and task items alike — a task item's tick state is in
461
+ * {@link getTaskItems} instead. NESTING is not re-derived: a nested list lives
462
+ * INSIDE its parent `<li>` (which is what makes the list's accessibility tree
463
+ * valid), so a parent's text contains its children's, and each child is also an
464
+ * entry of its own.
465
+ */
466
+ async getListItems() {
467
+ const items = await this.locatorForAll('.wr-markdown__item')();
468
+ return Promise.all(items.map(item => proseText(item)));
469
+ }
470
+ /**
471
+ * Every task-list item (`- [x]` / `- [ ]`), in document order.
472
+ *
473
+ * Ordinary items are not here, so the count is the number of checkboxes in the
474
+ * document. Both halves of each entry are worth asserting: the `checked` flag is
475
+ * what a reader sees, and `stateLabel` is the only thing a screen reader gets —
476
+ * the tick itself is `aria-hidden`.
477
+ */
478
+ async getTaskItems() {
479
+ const items = await this.locatorForAll(WrMarkdownTaskItemReader)();
480
+ return Promise.all(items.map(item => item.read()));
481
+ }
482
+ /** Every table, in document order, each as a rectangle of text plus its alignment. */
483
+ async getTables() {
484
+ const tables = await this.locatorForAll(WrMarkdownTableReader)();
485
+ return Promise.all(tables.map(table => table.read()));
486
+ }
487
+ /**
488
+ * The text of every blockquote, in document order.
489
+ *
490
+ * A quote holds BLOCKS, so this is everything inside it collapsed to one line —
491
+ * its paragraphs are also in {@link getParagraphs} and a snippet inside it is
492
+ * also one of {@link getCodeBlocks}.
493
+ */
494
+ async getQuotes() {
495
+ const quotes = await this.locatorForAll('.wr-markdown__quote')();
496
+ return Promise.all(quotes.map(quote => proseText(quote)));
497
+ }
498
+ /**
499
+ * How many thematic breaks the document rendered.
500
+ *
501
+ * A count rather than a list because an `<hr>` has no content — and it is worth
502
+ * counting: `---` is the one construct this renderer resolves differently from
503
+ * some others (it is always a break here, never a setext heading underline), so a
504
+ * document that lost a rule gained a heading somewhere.
505
+ */
506
+ async getRuleCount() {
507
+ return (await this.locatorForAll('.wr-markdown__rule')()).length;
508
+ }
509
+ }
510
+
511
+ /**
512
+ * Generated bundle index. Do not edit.
513
+ */
514
+
515
+ export { WrMarkdownCodeBlockHarness, WrMarkdownHarness };
516
+ //# sourceMappingURL=ngwr-markdown-testing.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ngwr-markdown-testing.mjs","sources":["../../../projects/lib/markdown/testing/wr-markdown-code-block-harness.ts","../../../projects/lib/markdown/testing/wr-markdown-harness-text.ts","../../../projects/lib/markdown/testing/wr-markdown-harness.ts","../../../projects/lib/markdown/testing/ngwr-markdown-testing.ts"],"sourcesContent":["/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport { ComponentHarness, HarnessPredicate } from '@angular/cdk/testing';\n\nimport type { WrMarkdownCodeBlockHarnessFilters } from './interfaces';\n\n/**\n * Test harness for one fenced code block of a `<wr-markdown>` document.\n *\n * A block of its own rather than a field on the document harness, because it is\n * the one piece of rendered markdown that has BEHAVIOUR: it carries a language, it\n * may or may not be coloured yet, and it may offer a copy button — three states\n * that belong to the block and not to the page it is on.\n *\n * Its host is the `.wr-markdown__code-block` wrapper, which is what holds the\n * `<pre>` and the copy button together. Blocks nested inside a quote or a list item\n * are blocks too, and this harness finds them: `<wr-markdown>` renders them the\n * same way, and the copy affordance is theirs as well.\n *\n * @example\n * ```ts\n * const block = await (await loader.getHarness(WrMarkdownHarness)).getCodeBlock({ language: 'ts' });\n *\n * expect(await block.getCode()).toBe('const a = 1;');\n * await block.copy();\n * ```\n *\n * @see https://ngwr.dev/reference/components/markdown\n * @see https://ngwr.dev/guides/testing\n */\nexport class WrMarkdownCodeBlockHarness extends ComponentHarness {\n static hostSelector = '.wr-markdown__code-block';\n\n /** Build a predicate that narrows the query — pass to `getHarness` / `getAllHarnesses`. */\n static with(options: WrMarkdownCodeBlockHarnessFilters = {}): HarnessPredicate<WrMarkdownCodeBlockHarness> {\n return new HarnessPredicate(WrMarkdownCodeBlockHarness, options)\n .addOption('language', options.language, (harness, language) =>\n HarnessPredicate.stringMatches(harness.getLanguage(), language)\n )\n .addOption('code', options.code, (harness, code) => HarnessPredicate.stringMatches(harness.getCode(), code));\n }\n\n private readonly pre = this.locatorFor('pre.wr-markdown__pre');\n private readonly code = this.locatorFor('code.wr-markdown__code');\n private readonly copyButton = this.locatorForOptional('button.wr-markdown__copy');\n\n /**\n * The block's language — the info string's first word, lowercased — or `null` for\n * a bare fence.\n *\n * Read from `data-language`, which is the only place it reaches the DOM, and the\n * reason it is there at all: a highlighter picks a grammar by it, and the\n * showcase's markdown export needs it to re-fence the block. `null` is a real\n * answer, not a gap: a bare fence names no grammar, so nothing is ever asked to\n * colour it.\n */\n async getLanguage(): Promise<string | null> {\n return (await this.code()).getAttribute('data-language');\n }\n\n /**\n * The block's code, EXACTLY — every space, every blank line, every newline.\n *\n * Nothing is collapsed here, unlike every prose read on the document harness,\n * and it is read off the `<pre>` rather than off the host so the copy button is\n * not part of the answer. Two failures live in this one string. Angular strips\n * whitespace-only text nodes from templates, so the code has to arrive as an\n * interpolation — a template that types it instead loses the indentation. And a\n * HIGHLIGHTED block is not one text node but a span per token: what separates its\n * lines is a real `\\n` interpolated between them, so a spec that only ever\n * asserts the unhighlighted case would not notice a highlighted block copying out\n * as one run-together line.\n */\n async getCode(): Promise<string> {\n return (await (await this.pre()).getProperty<string | null>('textContent')) ?? '';\n }\n\n /**\n * Whether the block is coloured right now.\n *\n * A block is plain text until a highlighter answers for it, and that is the\n * intended order rather than a compromise: a grammar engine loads WASM, and\n * blocking on it would leave the reader with an empty box instead of their code.\n * So this is `false` with no `WR_MARKDOWN_HIGHLIGHTER` provided, `false` for a\n * language the highlighter declined or threw on, `false` under SSR — a\n * highlighter resolving after prerender cannot contribute to the HTML — and\n * `false` for the first render of an async one.\n */\n async isHighlighted(): Promise<boolean> {\n return (await this.locatorForOptional('.wr-markdown__line')()) !== null;\n }\n\n /**\n * Whether this block offers a copy button.\n *\n * Two conditions, and both are the component's policy rather than an accident:\n * `copyable` has to be on (bound, or defaulted through\n * `provideWrConfig({ markdown: { copyable: true } })`), and the fence has to be\n * CLOSED. Mid-stream a block's code is not all there, and a button that copies\n * half a snippet is worse than no button.\n */\n async canCopy(): Promise<boolean> {\n return (await this.copyButton()) !== null;\n }\n\n /**\n * The copy button's accessible name, or `null` when there is no button.\n *\n * Worth reading rather than assuming, because it is the whole of the copy\n * feedback: the button swaps its label — and its icon — for the \"copied\" one for\n * two seconds after a successful write, and both strings come from the i18n\n * catalog (`markdown.copy` / `markdown.copied`) or the matching inputs. Which is\n * also why this harness reports the name instead of a `isCopied()` boolean: only\n * the consuming app knows which of its own two strings means which.\n */\n async getCopyLabel(): Promise<string | null> {\n const button = await this.copyButton();\n return button ? button.getAttribute('aria-label') : null;\n }\n\n /**\n * Copy the block, the way a pointer does.\n *\n * Throws when there is no button, naming both reasons — a silent no-op here\n * surfaces as an empty clipboard assertion several lines later. Note what a\n * successful click needs from the environment: the directive behind the button\n * writes through `navigator.clipboard`, which jsdom does not implement at all, so\n * a unit test has to stand one in before the write can succeed and the label can\n * swap.\n */\n async copy(): Promise<void> {\n const button = await this.copyButton();\n if (!button) {\n throw new Error(\n 'WrMarkdownCodeBlockHarness.copy(): this block has no copy button. Either `copyable` is off — it ' +\n 'falls back to `markdown.copyable` from provideWrConfig(), then to false — or the fence is still ' +\n 'open, and <wr-markdown> only offers one on a CLOSED block.'\n );\n }\n await button.click();\n }\n}\n","/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\n/**\n * Collapse the whitespace of a prose read to the line a reader sees.\n *\n * `TestElement.text()` only trims the ends, and markdown prose is never one text\n * node: a paragraph is text spliced with `<strong>`, `<code>` and `<a>`, and a\n * SOFT line break inside it is rendered as a real `\\n` — the parser keeps it as\n * whitespace and lets CSS collapse it, which is what CommonMark asks for. Reading\n * `textContent` raw therefore reports a paragraph the author wrote on three lines\n * as three lines, while the browser shows one; every run becomes a single space so\n * a spec can assert what is on screen.\n *\n * Deliberately NOT used on a code block. There the whitespace IS the content —\n * indentation, blank lines, the newlines between highlighted lines — so\n * `WrMarkdownCodeBlockHarness.getCode()` reads it exactly and this function never\n * touches it.\n */\nexport function wrMarkdownHarnessText(text: string): string {\n return text.replace(/\\s+/g, ' ').trim();\n}\n","/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport { ComponentHarness, HarnessPredicate, type TestElement } from '@angular/cdk/testing';\n\nimport type {\n WrMarkdownCodeBlockHarnessFilters,\n WrMarkdownHarnessAlign,\n WrMarkdownHarnessFilters,\n WrMarkdownHarnessHeading,\n WrMarkdownHarnessImage,\n WrMarkdownHarnessLink,\n WrMarkdownHarnessTable,\n WrMarkdownHarnessTaskItem,\n} from './interfaces';\nimport { WrMarkdownCodeBlockHarness } from './wr-markdown-code-block-harness';\nimport { wrMarkdownHarnessText } from './wr-markdown-harness-text';\n\n/**\n * The task-state label — hidden from the page, read out in front of the item.\n *\n * Left out of every prose read here, so a spec asserting an item's text does not\n * have to know that `'Done: '` is spliced in front of it. It is not lost: it is a\n * field of its own on {@link WrMarkdownHarnessTaskItem}, which is where its\n * absence is a failure rather than a nuisance.\n */\nconst SR_ONLY = '.wr-markdown__sr-only';\n\n/** The alignment the renderer wrote as an inline style, if it wrote one. */\nconst TEXT_ALIGN_RE = /text-align:\\s*([a-z]+)/;\n\n/** Every prose read goes through here: collapsed, and without the hidden labels. */\nasync function proseText(element: TestElement): Promise<string> {\n return wrMarkdownHarnessText(await element.text({ exclude: SR_ONLY }));\n}\n\n/**\n * A cell's or a header's alignment, read off the inline `style` attribute.\n *\n * Not through `getCssValue()`, and the difference is not cosmetic: a column the\n * delimiter row said nothing about carries no declaration at all, and computed\n * style answers for it anyway — with whatever the user-agent stylesheet says a\n * `<th>` or a `<td>` is aligned to. That makes \"no alignment\" indistinguishable\n * from `center`, in the one direction that matters (a `<th>` centres by default),\n * and it makes the answer depend on the browser rather than on the document. The\n * attribute is what the component wrote, which is the fact being asserted.\n */\nasync function alignOf(cell: TestElement): Promise<WrMarkdownHarnessAlign> {\n const match = TEXT_ALIGN_RE.exec((await cell.getAttribute('style')) ?? '');\n\n switch (match?.[1]) {\n case 'start':\n return 'start';\n case 'center':\n return 'center';\n case 'end':\n return 'end';\n default:\n return null;\n }\n}\n\n/**\n * One body row of a rendered table.\n *\n * Not exported, and not because rows do not matter: a `TestElement` cannot be\n * queried INTO, so reading one row's own cells needs a harness rooted on that row.\n * Nothing here is drivable — rendered prose is inert — so the row's job is to hand\n * its cells up to {@link WrMarkdownHarness.getTables}, which is where a consumer\n * asserts the whole table in one comparison.\n */\nclass WrMarkdownTableRowReader extends ComponentHarness {\n /** Body rows only: the header row lives in the `<thead>`. */\n static hostSelector = 'tbody tr';\n\n async getCellTexts(): Promise<string[]> {\n const cells = await this.locatorForAll('.wr-markdown__td')();\n return Promise.all(cells.map(cell => proseText(cell)));\n }\n}\n\n/**\n * One rendered task-list item — a harness for the same reason as the row reader.\n *\n * Its three reads have to be SCOPED to the item: querying the tick and the hidden\n * label from the document root would hand every item in the document the first\n * one's state, which is the kind of green spec that reports a broken checklist as\n * intact. A nested task list lives inside its parent `<li>`, and the parent's own\n * tick and label are rendered before it — so the first match under an item is\n * always the item's own.\n */\nclass WrMarkdownTaskItemReader extends ComponentHarness {\n static hostSelector = '.wr-markdown__item--task';\n\n private readonly tick = this.locatorForOptional('.wr-markdown__task--checked');\n private readonly stateLabel = this.locatorForOptional(SR_ONLY);\n\n async read(): Promise<WrMarkdownHarnessTaskItem> {\n const label = await this.stateLabel();\n\n return {\n text: await proseText(await this.host()),\n checked: (await this.tick()) !== null,\n stateLabel: label ? await label.text() : null,\n };\n }\n}\n\n/** One rendered table — see {@link WrMarkdownTableRowReader} for why this is a harness. */\nclass WrMarkdownTableReader extends ComponentHarness {\n static hostSelector = 'table.wr-markdown__table';\n\n async read(): Promise<WrMarkdownHarnessTable> {\n const headers = await this.locatorForAll('th.wr-markdown__th')();\n const rows = await this.locatorForAll(WrMarkdownTableRowReader)();\n\n return {\n headers: await Promise.all(headers.map(header => proseText(header))),\n rows: await Promise.all(rows.map(row => row.getCellTexts())),\n align: await Promise.all(headers.map(header => alignOf(header))),\n };\n }\n}\n\n/**\n * Test harness for `<wr-markdown>`, with {@link WrMarkdownCodeBlockHarness} for one\n * fenced code block.\n *\n * What a consumer of a markdown renderer actually asserts is that a piece of\n * SOURCE became the right DOM, so everything here reads rendered elements: the\n * heading levels, the `href` / `target` / `rel` triple on a link, the tick state of\n * a task item, a table as a rectangle of text. The parse tree is not exposed on\n * purpose — `parseMarkdown()` is a pure function with its own spec, and a harness\n * that reported its nodes would pass straight through the half that breaks people,\n * which is the template between the tree and the page.\n *\n * Three things worth knowing before writing expectations:\n *\n * - **Prose reads are whitespace-collapsed, code is not.** A soft line break inside\n * a paragraph is a real `\\n` in the DOM (CommonMark says CSS collapses it), so\n * every text answer here is normalized to the line a reader sees.\n * {@link WrMarkdownCodeBlockHarness.getCode} is the exception and reads its\n * snippet exactly.\n * - **The hidden task-state labels are left out of prose.** They are announced text,\n * not visible text; {@link getTaskItems} reports them as a field.\n * - **Everything is flat and in document order.** Markdown nests — a quote holds\n * blocks, a list item holds blocks — and these readers do not re-derive that\n * hierarchy: {@link getCodeBlocks} finds a block inside a quote, and a nested\n * list item's text is part of its parent's, because that is what the DOM says.\n *\n * @example\n * ```ts\n * const md = await loader.getHarness(WrMarkdownHarness);\n *\n * expect(await md.getHeadings()).toEqual([{ level: 1, text: 'Release notes', id: 'release-notes' }]);\n * expect(await md.getLinks()).toEqual([\n * { text: 'the docs', href: 'https://ngwr.dev', title: null, target: '_blank', rel: 'noopener noreferrer' },\n * ]);\n * ```\n *\n * @see https://ngwr.dev/reference/components/markdown\n * @see https://ngwr.dev/guides/testing\n */\nexport class WrMarkdownHarness extends ComponentHarness {\n static hostSelector = 'wr-markdown';\n\n /** Build a predicate that narrows the query — pass to `getHarness` / `getAllHarnesses`. */\n static with(options: WrMarkdownHarnessFilters = {}): HarnessPredicate<WrMarkdownHarness> {\n return new HarnessPredicate(WrMarkdownHarness, options)\n .addOption('text', options.text, (harness, text) => HarnessPredicate.stringMatches(harness.getText(), text))\n .addOption('headingText', options.headingText, async (harness, text) => {\n for (const heading of await harness.getHeadings()) {\n if (await HarnessPredicate.stringMatches(heading.text, text)) return true;\n }\n return false;\n })\n .addOption('codeLanguage', options.codeLanguage, async (harness, language) => {\n for (const block of await harness.getCodeBlocks()) {\n if (await HarnessPredicate.stringMatches(block.getLanguage(), language)) return true;\n }\n return false;\n })\n .addOption(\n 'streaming',\n options.streaming,\n async (harness, streaming) => (await harness.isStreaming()) === streaming\n );\n }\n\n /**\n * The whole document as text — whitespace collapsed, the hidden task-state labels\n * left out.\n *\n * The coarse read, and the useful one for the security cases: raw HTML in the\n * source is ESCAPED rather than rendered, so `<img onerror=…>` shows up here as\n * characters, and a refused `javascript:` URL leaves its label behind as text with\n * no anchor around it. A code block's newlines are collapsed like everything else\n * — {@link getCodeBlocks} is where a snippet stays exact.\n *\n * It is `textContent`, which means sibling BLOCKS are not separated: a document of\n * `<h1>Title</h1><p>Body</p>` reads as `'TitleBody'`, because the renderer emits\n * no whitespace between them and none is invented here. Assert a fragment with\n * `toContain`, or read the blocks themselves — {@link getHeadings},\n * {@link getParagraphs} — when the boundary matters.\n */\n async getText(): Promise<string> {\n return proseText(await this.host());\n }\n\n /**\n * Whether the component rendered nothing at all.\n *\n * True for an empty `value`, and for one that parses to nothing — a lone `<!--`\n * or whitespace. Distinct from `getText() === ''`, which a document made only of\n * a thematic break or an image would also answer.\n */\n async isEmpty(): Promise<boolean> {\n const host = await this.host();\n if ((await host.getProperty<number>('childElementCount')) > 0) return false;\n return (await this.getText()) === '';\n }\n\n /**\n * Whether the document is being streamed.\n *\n * Read from the `--streaming` host modifier rather than from the input, because\n * the modifier is the whole public API of the feature on the styling side: it is\n * what paints the caret after the last block. What it does NOT say is that\n * partial-safe parsing is on — the same input drives both, but only this reaches\n * the DOM.\n */\n async isStreaming(): Promise<boolean> {\n return (await this.host()).hasClass('wr-markdown--streaming');\n }\n\n /** Every heading, in document order. */\n async getHeadings(): Promise<WrMarkdownHarnessHeading[]> {\n const headings = await this.locatorForAll('.wr-markdown__heading')();\n\n return Promise.all(\n headings.map(async heading => ({\n // `H3` → 3. The element is where the level lives; see the field's docs.\n level: Number.parseInt((await heading.getProperty<string>('tagName')).slice(1), 10),\n text: await proseText(heading),\n id: await heading.getAttribute('id'),\n }))\n );\n }\n\n /**\n * The text of every paragraph, in document order.\n *\n * Including the ones inside a LOOSE list item — CommonMark drops the `<p>` in a\n * tight list and keeps it in a loose one, which is what stops a one-line list from\n * rendering double-spaced, so an empty answer for `- a\\n- b` is the correct one\n * rather than a missed paragraph.\n */\n async getParagraphs(): Promise<string[]> {\n const paragraphs = await this.locatorForAll('.wr-markdown__paragraph')();\n return Promise.all(paragraphs.map(paragraph => proseText(paragraph)));\n }\n\n /**\n * Every link, in document order, with the attributes that make it safe.\n *\n * A URL the renderer refused is NOT here — it renders as plain text instead of as\n * a live anchor that announces as a link and does nothing — so an empty answer\n * where a link was expected is the sanitizer speaking, and {@link getText} still\n * has the label.\n */\n async getLinks(): Promise<WrMarkdownHarnessLink[]> {\n const links = await this.locatorForAll('.wr-markdown__link')();\n\n return Promise.all(\n links.map(async link => ({\n text: await proseText(link),\n href: await link.getAttribute('href'),\n title: await link.getAttribute('title'),\n target: await link.getAttribute('target'),\n rel: await link.getAttribute('rel'),\n }))\n );\n }\n\n /**\n * Every image, in document order.\n *\n * A refused source renders no image at all and leaves the alt text as prose,\n * which is the information the author actually wrote — so assert this together\n * with {@link getText} when the point is the refusal.\n */\n async getImages(): Promise<WrMarkdownHarnessImage[]> {\n const images = await this.locatorForAll('.wr-markdown__image')();\n\n return Promise.all(\n images.map(async image => ({\n src: await image.getAttribute('src'),\n alt: await image.getAttribute('alt'),\n title: await image.getAttribute('title'),\n }))\n );\n }\n\n /**\n * The text of every inline code span, in document order.\n *\n * A different element from a fenced block (`<code>` on its own, not inside a\n * `<pre>`), and never one of {@link getCodeBlocks}. Read exactly rather than\n * collapsed, for the same reason a block is: this is code, and the spacing in it\n * is content.\n */\n async getInlineCode(): Promise<string[]> {\n const spans = await this.locatorForAll('.wr-markdown__code-inline')();\n return Promise.all(spans.map(async span => (await span.getProperty<string | null>('textContent')) ?? ''));\n }\n\n /**\n * The fenced code blocks, in document order — nested ones included.\n *\n * A block inside a quote or a list item is a block: it renders the same way and it\n * gets the same copy affordance, so leaving it out would report a document as\n * having no code because its snippet is indented under a bullet.\n */\n async getCodeBlocks(filters: WrMarkdownCodeBlockHarnessFilters = {}): Promise<WrMarkdownCodeBlockHarness[]> {\n return this.locatorForAll(WrMarkdownCodeBlockHarness.with(filters))();\n }\n\n /**\n * The first code block matching the filters.\n *\n * Throws when nothing matches, naming the languages the document does offer —\n * a `getCodeBlocks(…)[0]` that quietly resolves to `undefined` fails several lines\n * later, on an unrelated line, with `Cannot read properties of undefined`.\n */\n async getCodeBlock(filters: WrMarkdownCodeBlockHarnessFilters = {}): Promise<WrMarkdownCodeBlockHarness> {\n const [block] = await this.getCodeBlocks(filters);\n if (block) return block;\n\n const languages = await Promise.all((await this.getCodeBlocks()).map(other => other.getLanguage()));\n const offered =\n languages.length > 0\n ? `This document has ${languages.length} block(s), in: ${languages.map(name => name ?? '(none)').join(', ')}.`\n : 'This document renders no code blocks at all.';\n\n throw new Error(`WrMarkdownHarness.getCodeBlock(): no block matched ${JSON.stringify(filters)}. ${offered}`);\n }\n\n /**\n * The text of every list item, in document order.\n *\n * Bullets, numbers and task items alike — a task item's tick state is in\n * {@link getTaskItems} instead. NESTING is not re-derived: a nested list lives\n * INSIDE its parent `<li>` (which is what makes the list's accessibility tree\n * valid), so a parent's text contains its children's, and each child is also an\n * entry of its own.\n */\n async getListItems(): Promise<string[]> {\n const items = await this.locatorForAll('.wr-markdown__item')();\n return Promise.all(items.map(item => proseText(item)));\n }\n\n /**\n * Every task-list item (`- [x]` / `- [ ]`), in document order.\n *\n * Ordinary items are not here, so the count is the number of checkboxes in the\n * document. Both halves of each entry are worth asserting: the `checked` flag is\n * what a reader sees, and `stateLabel` is the only thing a screen reader gets —\n * the tick itself is `aria-hidden`.\n */\n async getTaskItems(): Promise<WrMarkdownHarnessTaskItem[]> {\n const items = await this.locatorForAll(WrMarkdownTaskItemReader)();\n return Promise.all(items.map(item => item.read()));\n }\n\n /** Every table, in document order, each as a rectangle of text plus its alignment. */\n async getTables(): Promise<WrMarkdownHarnessTable[]> {\n const tables = await this.locatorForAll(WrMarkdownTableReader)();\n return Promise.all(tables.map(table => table.read()));\n }\n\n /**\n * The text of every blockquote, in document order.\n *\n * A quote holds BLOCKS, so this is everything inside it collapsed to one line —\n * its paragraphs are also in {@link getParagraphs} and a snippet inside it is\n * also one of {@link getCodeBlocks}.\n */\n async getQuotes(): Promise<string[]> {\n const quotes = await this.locatorForAll('.wr-markdown__quote')();\n return Promise.all(quotes.map(quote => proseText(quote)));\n }\n\n /**\n * How many thematic breaks the document rendered.\n *\n * A count rather than a list because an `<hr>` has no content — and it is worth\n * counting: `---` is the one construct this renderer resolves differently from\n * some others (it is always a break here, never a setext heading underline), so a\n * document that lost a rule gained a heading somewhere.\n */\n async getRuleCount(): Promise<number> {\n return (await this.locatorForAll('.wr-markdown__rule')()).length;\n }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}