ngwr 11.1.0 → 11.2.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 (205) hide show
  1. package/circular-text/styles/_index.scss +11 -0
  2. package/command-palette/styles/_index.scss +12 -0
  3. package/fesm2022/ngwr-action-sheet-testing.mjs +313 -0
  4. package/fesm2022/ngwr-action-sheet-testing.mjs.map +1 -0
  5. package/fesm2022/ngwr-bar-chart-testing.mjs +89 -0
  6. package/fesm2022/ngwr-bar-chart-testing.mjs.map +1 -0
  7. package/fesm2022/ngwr-blur-text-testing.mjs +203 -0
  8. package/fesm2022/ngwr-blur-text-testing.mjs.map +1 -0
  9. package/fesm2022/ngwr-blur-text.mjs +15 -1
  10. package/fesm2022/ngwr-blur-text.mjs.map +1 -1
  11. package/fesm2022/ngwr-border-glow-testing.mjs +325 -0
  12. package/fesm2022/ngwr-border-glow-testing.mjs.map +1 -0
  13. package/fesm2022/ngwr-calendar-heatmap-testing.mjs +95 -0
  14. package/fesm2022/ngwr-calendar-heatmap-testing.mjs.map +1 -0
  15. package/fesm2022/ngwr-calendar-testing.mjs +368 -0
  16. package/fesm2022/ngwr-calendar-testing.mjs.map +1 -0
  17. package/fesm2022/ngwr-calendar.mjs +43 -3
  18. package/fesm2022/ngwr-calendar.mjs.map +1 -1
  19. package/fesm2022/ngwr-cascader.mjs +12 -0
  20. package/fesm2022/ngwr-cascader.mjs.map +1 -1
  21. package/fesm2022/ngwr-checkbox.mjs +8 -0
  22. package/fesm2022/ngwr-checkbox.mjs.map +1 -1
  23. package/fesm2022/ngwr-circular-text-testing.mjs +230 -0
  24. package/fesm2022/ngwr-circular-text-testing.mjs.map +1 -0
  25. package/fesm2022/ngwr-circular-text.mjs +2 -2
  26. package/fesm2022/ngwr-circular-text.mjs.map +1 -1
  27. package/fesm2022/ngwr-color-picker-testing.mjs +432 -0
  28. package/fesm2022/ngwr-color-picker-testing.mjs.map +1 -0
  29. package/fesm2022/ngwr-color-picker.mjs +29 -3
  30. package/fesm2022/ngwr-color-picker.mjs.map +1 -1
  31. package/fesm2022/ngwr-context-menu.mjs +25 -5
  32. package/fesm2022/ngwr-context-menu.mjs.map +1 -1
  33. package/fesm2022/ngwr-date-picker-testing.mjs +10 -79
  34. package/fesm2022/ngwr-date-picker-testing.mjs.map +1 -1
  35. package/fesm2022/ngwr-decrypt-text-testing.mjs +261 -0
  36. package/fesm2022/ngwr-decrypt-text-testing.mjs.map +1 -0
  37. package/fesm2022/ngwr-decrypt-text.mjs +6 -1
  38. package/fesm2022/ngwr-decrypt-text.mjs.map +1 -1
  39. package/fesm2022/ngwr-donut-chart-testing.mjs +93 -0
  40. package/fesm2022/ngwr-donut-chart-testing.mjs.map +1 -0
  41. package/fesm2022/ngwr-drawer.mjs +36 -7
  42. package/fesm2022/ngwr-drawer.mjs.map +1 -1
  43. package/fesm2022/ngwr-dropdown.mjs +32 -2
  44. package/fesm2022/ngwr-dropdown.mjs.map +1 -1
  45. package/fesm2022/ngwr-event-calendar-testing.mjs +446 -0
  46. package/fesm2022/ngwr-event-calendar-testing.mjs.map +1 -0
  47. package/fesm2022/ngwr-event-calendar.mjs +43 -18
  48. package/fesm2022/ngwr-event-calendar.mjs.map +1 -1
  49. package/fesm2022/ngwr-falling-text-testing.mjs +218 -0
  50. package/fesm2022/ngwr-falling-text-testing.mjs.map +1 -0
  51. package/fesm2022/ngwr-fuzzy-text-testing.mjs +134 -0
  52. package/fesm2022/ngwr-fuzzy-text-testing.mjs.map +1 -0
  53. package/fesm2022/ngwr-gauge-testing.mjs +86 -0
  54. package/fesm2022/ngwr-gauge-testing.mjs.map +1 -0
  55. package/fesm2022/ngwr-glitch-text-testing.mjs +181 -0
  56. package/fesm2022/ngwr-glitch-text-testing.mjs.map +1 -0
  57. package/fesm2022/ngwr-gradient-text-testing.mjs +223 -0
  58. package/fesm2022/ngwr-gradient-text-testing.mjs.map +1 -0
  59. package/fesm2022/ngwr-i18n-en.mjs +14 -0
  60. package/fesm2022/ngwr-i18n-en.mjs.map +1 -1
  61. package/fesm2022/ngwr-i18n-ru.mjs +14 -0
  62. package/fesm2022/ngwr-i18n-ru.mjs.map +1 -1
  63. package/fesm2022/ngwr-image-cropper-testing.mjs +156 -0
  64. package/fesm2022/ngwr-image-cropper-testing.mjs.map +1 -0
  65. package/fesm2022/ngwr-image-cropper.mjs +10 -3
  66. package/fesm2022/ngwr-image-cropper.mjs.map +1 -1
  67. package/fesm2022/ngwr-input-number.mjs +73 -6
  68. package/fesm2022/ngwr-input-number.mjs.map +1 -1
  69. package/fesm2022/ngwr-knob-testing.mjs +220 -0
  70. package/fesm2022/ngwr-knob-testing.mjs.map +1 -0
  71. package/fesm2022/ngwr-lightbox-testing.mjs +223 -0
  72. package/fesm2022/ngwr-lightbox-testing.mjs.map +1 -0
  73. package/fesm2022/ngwr-lightbox.mjs +9 -2
  74. package/fesm2022/ngwr-lightbox.mjs.map +1 -1
  75. package/fesm2022/ngwr-line-chart-testing.mjs +119 -0
  76. package/fesm2022/ngwr-line-chart-testing.mjs.map +1 -0
  77. package/fesm2022/ngwr-marquee-testing.mjs +336 -0
  78. package/fesm2022/ngwr-marquee-testing.mjs.map +1 -0
  79. package/fesm2022/ngwr-meter-group-testing.mjs +80 -0
  80. package/fesm2022/ngwr-meter-group-testing.mjs.map +1 -0
  81. package/fesm2022/ngwr-overlay.mjs +28 -1
  82. package/fesm2022/ngwr-overlay.mjs.map +1 -1
  83. package/fesm2022/ngwr-popover.mjs +14 -0
  84. package/fesm2022/ngwr-popover.mjs.map +1 -1
  85. package/fesm2022/ngwr-rotating-text-testing.mjs +220 -0
  86. package/fesm2022/ngwr-rotating-text-testing.mjs.map +1 -0
  87. package/fesm2022/ngwr-rotating-text.mjs +2 -2
  88. package/fesm2022/ngwr-rotating-text.mjs.map +1 -1
  89. package/fesm2022/ngwr-select.mjs +70 -11
  90. package/fesm2022/ngwr-select.mjs.map +1 -1
  91. package/fesm2022/ngwr-shiny-text-testing.mjs +237 -0
  92. package/fesm2022/ngwr-shiny-text-testing.mjs.map +1 -0
  93. package/fesm2022/ngwr-slider.mjs +44 -6
  94. package/fesm2022/ngwr-slider.mjs.map +1 -1
  95. package/fesm2022/ngwr-sparkline-testing.mjs +76 -0
  96. package/fesm2022/ngwr-sparkline-testing.mjs.map +1 -0
  97. package/fesm2022/ngwr-speed-dial-testing.mjs +279 -0
  98. package/fesm2022/ngwr-speed-dial-testing.mjs.map +1 -0
  99. package/fesm2022/ngwr-splash-cursor-testing.mjs +89 -0
  100. package/fesm2022/ngwr-splash-cursor-testing.mjs.map +1 -0
  101. package/fesm2022/ngwr-split-text-testing.mjs +224 -0
  102. package/fesm2022/ngwr-split-text-testing.mjs.map +1 -0
  103. package/fesm2022/ngwr-split-text.mjs +15 -1
  104. package/fesm2022/ngwr-split-text.mjs.map +1 -1
  105. package/fesm2022/ngwr-splitter-testing.mjs +245 -0
  106. package/fesm2022/ngwr-splitter-testing.mjs.map +1 -0
  107. package/fesm2022/ngwr-spotlight-card-testing.mjs +368 -0
  108. package/fesm2022/ngwr-spotlight-card-testing.mjs.map +1 -0
  109. package/fesm2022/ngwr-star-border-testing.mjs +172 -0
  110. package/fesm2022/ngwr-star-border-testing.mjs.map +1 -0
  111. package/fesm2022/ngwr-table.mjs +13 -3
  112. package/fesm2022/ngwr-table.mjs.map +1 -1
  113. package/fesm2022/ngwr-tabs.mjs +15 -1
  114. package/fesm2022/ngwr-tabs.mjs.map +1 -1
  115. package/fesm2022/ngwr-tilt-card-testing.mjs +190 -0
  116. package/fesm2022/ngwr-tilt-card-testing.mjs.map +1 -0
  117. package/fesm2022/ngwr-tilt-card.mjs +19 -1
  118. package/fesm2022/ngwr-tilt-card.mjs.map +1 -1
  119. package/fesm2022/ngwr-toast.mjs +36 -7
  120. package/fesm2022/ngwr-toast.mjs.map +1 -1
  121. package/fesm2022/ngwr-tour-testing.mjs +173 -0
  122. package/fesm2022/ngwr-tour-testing.mjs.map +1 -0
  123. package/fesm2022/ngwr-tour.mjs +2 -2
  124. package/fesm2022/ngwr-tour.mjs.map +1 -1
  125. package/fesm2022/ngwr-tree.mjs +10 -3
  126. package/fesm2022/ngwr-tree.mjs.map +1 -1
  127. package/fesm2022/ngwr-typewriter-testing.mjs +228 -0
  128. package/fesm2022/ngwr-typewriter-testing.mjs.map +1 -0
  129. package/fesm2022/ngwr-version.mjs +1 -1
  130. package/fesm2022/ngwr-version.mjs.map +1 -1
  131. package/fesm2022/ngwr-waves-testing.mjs +128 -0
  132. package/fesm2022/ngwr-waves-testing.mjs.map +1 -0
  133. package/fesm2022/ngwr-window-testing.mjs +361 -0
  134. package/fesm2022/ngwr-window-testing.mjs.map +1 -0
  135. package/fesm2022/ngwr-window.mjs +84 -10
  136. package/fesm2022/ngwr-window.mjs.map +1 -1
  137. package/i18n/en.json +16 -2
  138. package/i18n/ru.json +16 -2
  139. package/llms-full.txt +147 -0
  140. package/llms.txt +27 -8
  141. package/mcp/providers.js +29 -0
  142. package/mcp/tools.js +1 -29
  143. package/package.json +145 -1
  144. package/skills/ngwr/SKILL.md +115 -0
  145. package/skills/ngwr/references/catalog.md +212 -0
  146. package/skills/ngwr/references/setup.md +73 -0
  147. package/types/ngwr-action-sheet-testing.d.ts +260 -0
  148. package/types/ngwr-bar-chart-testing.d.ts +77 -0
  149. package/types/ngwr-blur-text-testing.d.ts +180 -0
  150. package/types/ngwr-blur-text.d.ts +12 -0
  151. package/types/ngwr-border-glow-testing.d.ts +219 -0
  152. package/types/ngwr-calendar-heatmap-testing.d.ts +79 -0
  153. package/types/ngwr-calendar-testing.d.ts +277 -0
  154. package/types/ngwr-calendar.d.ts +10 -0
  155. package/types/ngwr-checkbox.d.ts +13 -0
  156. package/types/ngwr-circular-text-testing.d.ts +196 -0
  157. package/types/ngwr-color-picker-testing.d.ts +291 -0
  158. package/types/ngwr-color-picker.d.ts +13 -1
  159. package/types/ngwr-context-menu.d.ts +15 -1
  160. package/types/ngwr-date-picker-testing.d.ts +6 -66
  161. package/types/ngwr-decrypt-text-testing.d.ts +222 -0
  162. package/types/ngwr-donut-chart-testing.d.ts +79 -0
  163. package/types/ngwr-drawer.d.ts +12 -0
  164. package/types/ngwr-dropdown.d.ts +9 -0
  165. package/types/ngwr-event-calendar-testing.d.ts +301 -0
  166. package/types/ngwr-event-calendar.d.ts +9 -6
  167. package/types/ngwr-falling-text-testing.d.ts +186 -0
  168. package/types/ngwr-fuzzy-text-testing.d.ts +130 -0
  169. package/types/ngwr-gauge-testing.d.ts +71 -0
  170. package/types/ngwr-glitch-text-testing.d.ts +172 -0
  171. package/types/ngwr-gradient-text-testing.d.ts +173 -0
  172. package/types/ngwr-image-cropper-testing.d.ts +134 -0
  173. package/types/ngwr-image-cropper.d.ts +1 -0
  174. package/types/ngwr-input-number.d.ts +33 -1
  175. package/types/ngwr-knob-testing.d.ts +150 -0
  176. package/types/ngwr-lightbox-testing.d.ts +154 -0
  177. package/types/ngwr-lightbox.d.ts +6 -0
  178. package/types/ngwr-line-chart-testing.d.ts +93 -0
  179. package/types/ngwr-marquee-testing.d.ts +280 -0
  180. package/types/ngwr-meter-group-testing.d.ts +67 -0
  181. package/types/ngwr-overlay.d.ts +19 -1
  182. package/types/ngwr-rotating-text-testing.d.ts +191 -0
  183. package/types/ngwr-select.d.ts +32 -0
  184. package/types/ngwr-shiny-text-testing.d.ts +165 -0
  185. package/types/ngwr-slider.d.ts +15 -0
  186. package/types/ngwr-sparkline-testing.d.ts +69 -0
  187. package/types/ngwr-speed-dial-testing.d.ts +195 -0
  188. package/types/ngwr-splash-cursor-testing.d.ts +95 -0
  189. package/types/ngwr-split-text-testing.d.ts +209 -0
  190. package/types/ngwr-split-text.d.ts +12 -0
  191. package/types/ngwr-splitter-testing.d.ts +174 -0
  192. package/types/ngwr-spotlight-card-testing.d.ts +303 -0
  193. package/types/ngwr-star-border-testing.d.ts +135 -0
  194. package/types/ngwr-table.d.ts +10 -1
  195. package/types/ngwr-tilt-card-testing.d.ts +158 -0
  196. package/types/ngwr-tilt-card.d.ts +2 -0
  197. package/types/ngwr-toast.d.ts +14 -5
  198. package/types/ngwr-tour-testing.d.ts +143 -0
  199. package/types/ngwr-tree.d.ts +7 -0
  200. package/types/ngwr-typewriter-testing.d.ts +201 -0
  201. package/types/ngwr-version.d.ts +1 -1
  202. package/types/ngwr-waves-testing.d.ts +121 -0
  203. package/types/ngwr-window-testing.d.ts +263 -0
  204. package/types/ngwr-window.d.ts +27 -10
  205. package/window/styles/_index.scss +44 -16
@@ -0,0 +1,186 @@
1
+ import { BaseHarnessFilters, 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
+ /** Narrows which `<wr-falling-text>` a harness query matches. */
11
+ interface WrFallingTextHarnessFilters extends BaseHarnessFilters {
12
+ /**
13
+ * Match the rendered sentence — the only thing that tells two of these apart, the
14
+ * component having no name, id or role of its own. A string is an exact match, a
15
+ * RegExp is tested.
16
+ *
17
+ * Compared against `WrFallingTextHarness.getText()`, so the non-breaking spaces the
18
+ * component types between the words are already back to plain ones: pass the sentence
19
+ * as it was written.
20
+ */
21
+ readonly text?: string | RegExp;
22
+ }
23
+
24
+ /**
25
+ * @license
26
+ *
27
+ * Use of this source code is governed by an MIT-style license that can be
28
+ * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE
29
+ */
30
+
31
+ /**
32
+ * Test harness for `<wr-falling-text>`.
33
+ *
34
+ * **The assertion that matters here is that the animation did not eat the text.** The
35
+ * component cuts the sentence into one span per word so a physics loop can throw them
36
+ * around, and {@link getText} is the check that a screen reader still gets prose out of
37
+ * the other end. It normalises whitespace on the way, because the separators are real
38
+ * `&nbsp;` characters rather than plain spaces — so `textContent` is never byte-identical
39
+ * to the `[text]` input, and a spec comparing the two raw would fail on a component that
40
+ * works perfectly.
41
+ *
42
+ * The rest of the readable surface is the split itself: {@link getWords} pins the rule
43
+ * (split on spaces, empty tokens dropped) and {@link getHighlightedWords} pins the
44
+ * prefix match, which is genuine logic — `grav` highlights `gravity`, and a whole-word
45
+ * implementation would satisfy every naive test but not this one.
46
+ *
47
+ * **Nothing positional is offered, and that is not a jsdom concession.** The words'
48
+ * `transform` values are one frame of a loop that never stops, seeded by `Math.random()`,
49
+ * so they are not reproducible even in a real browser; there is no rest state to call
50
+ * settled; the drag path needs `setPointerCapture` and AABB hit-testing over boxes that
51
+ * a unit test measures as 0×0 at the origin, where every word contains every point. Nor
52
+ * is `[gravity]` or `[trigger]` readable — neither reaches the DOM, and answering them
53
+ * would mean reaching into the component instance. There is no `isReducedMotion()`
54
+ * either: under reduced motion the component simply returns before touching anything, so
55
+ * the only honest proxy is {@link hasReleased}, which reports what it did rather than
56
+ * what it was told.
57
+ *
58
+ * **{@link hasReleased} and {@link release} have a precondition, and without it they are
59
+ * worthless.** The simulator measures the host and each word before it starts, and bails
60
+ * on a zero-sized box — which is every box in a test with no layout. So the spec has to
61
+ * give them one, and it has to do it BEFORE the trigger fires: the component marks itself
62
+ * started on the way past that guard, and the trigger listener is `{ once: true }`, so a
63
+ * failed first attempt is permanent.
64
+ *
65
+ * ```ts
66
+ * // In your spec, on a fixture mounted with trigger="click", before calling release():
67
+ * const box = { width: 300, height: 120, left: 0, top: 0, right: 300, bottom: 120 } as DOMRect;
68
+ * for (const el of fixture.nativeElement.querySelectorAll('wr-falling-text, .wr-falling-text__word')) {
69
+ * el.getBoundingClientRect = () => box;
70
+ * }
71
+ * ```
72
+ *
73
+ * Assert `hasReleased()` is `true` in that control case before you assert it is `false`
74
+ * anywhere else, or a spec proves only that the guard fired.
75
+ *
76
+ * @example
77
+ * ```ts
78
+ * const loader = TestbedHarnessEnvironment.loader(fixture);
79
+ * const text = await loader.getHarness(WrFallingTextHarness);
80
+ *
81
+ * expect(await text.getText()).toBe('gravity pulls every word down');
82
+ * expect(await text.getHighlightedWords()).toEqual(['gravity']);
83
+ * ```
84
+ *
85
+ * @see https://ngwr.dev/guides/testing
86
+ */
87
+ declare class WrFallingTextHarness extends ComponentHarness {
88
+ static hostSelector: string;
89
+ /** Build a predicate that narrows the query — pass to `getHarness` / `getAllHarnesses`. */
90
+ static with(options?: WrFallingTextHarnessFilters): HarnessPredicate<WrFallingTextHarness>;
91
+ private readonly wordEls;
92
+ /**
93
+ * The whole sentence, as a screen reader reads it: whitespace collapsed and trimmed.
94
+ *
95
+ * The one assertion worth making about a component that dismantles a paragraph. The
96
+ * words are separated by `.wr-falling-text__space` spans holding U+00A0, so this
97
+ * collapses them back to ordinary spaces — losing those spans would be invisible to
98
+ * every other check here while turning the announcement into one run-on word.
99
+ *
100
+ * DOM order never changes once the physics starts. The words are absolutely positioned
101
+ * and scattered on screen, but they are still read in the order they were typeset, so
102
+ * this answers the same before and after {@link release}.
103
+ */
104
+ getText(): Promise<string>;
105
+ /**
106
+ * Each word, in DOM order.
107
+ *
108
+ * Pins the split rule: on a literal space, with empty tokens dropped. Each of these is
109
+ * also one body in the simulator, so the length is the body count — which is why there
110
+ * is no separate `getWordCount()`, a method that would invite asserting how many
111
+ * without asserting which.
112
+ */
113
+ getWords(): Promise<string[]>;
114
+ /**
115
+ * The words carrying the highlight, in DOM order.
116
+ *
117
+ * `[highlightWords]` is matched as a PREFIX and case-sensitively, so `['grav']` picks
118
+ * up `gravity` and `['Grav']` picks up nothing. The highlight is colour alone with no
119
+ * non-visual counterpart, which is acceptable only because it adds no meaning the
120
+ * sentence does not already carry.
121
+ */
122
+ getHighlightedWords(): Promise<string[]>;
123
+ /**
124
+ * Whether one word is highlighted — for asserting a keyword without depending on the
125
+ * order or the completeness of the whole list.
126
+ *
127
+ * Takes the word as it is rendered, not the keyword that matched it: ask for `gravity`,
128
+ * not for `grav`. Throws for a word the sentence does not contain, since `false` there
129
+ * would read as "present and not highlighted" and quietly survive a typo. A word that
130
+ * appears twice is answered once, which is safe — matching is on the text alone, so
131
+ * both copies always agree.
132
+ */
133
+ isHighlighted(word: string): Promise<boolean>;
134
+ /**
135
+ * The font size as the consumer wrote it — `'2rem'`, not `'32px'`.
136
+ *
137
+ * Read off the inline `style` attribute rather than through `getCssValue()`. The two
138
+ * agree here and would part company in a real browser, which resolves `font-size` to
139
+ * pixels; `[fontSize]` takes a CSS length and handing back a different unit than the
140
+ * one that went in is the sort of green unit test that hides a broken assertion.
141
+ *
142
+ * Throws when nothing is written, which takes an empty `[fontSize]` — the component
143
+ * writes this on every render otherwise, default included.
144
+ */
145
+ getFontSize(): Promise<string>;
146
+ /**
147
+ * Whether the simulator has taken the words over.
148
+ *
149
+ * Reads the `position: absolute` the component writes onto every word before its first
150
+ * frame — a value it wrote itself, not a measurement, and the only difference in the
151
+ * DOM between "the physics is running" and "the words are still in normal flow". That
152
+ * makes it the way to observe the trigger contract and the reduced-motion escape, since
153
+ * neither leaves anything else behind.
154
+ *
155
+ * **It is `false` by default in a test, for a reason that has nothing to do with the
156
+ * component.** Without layout every box measures 0×0 and the simulator refuses to start
157
+ * — see this class's docs for the stub, and assert a `true` before you trust a `false`.
158
+ *
159
+ * Throws for a component rendering no words at all, where the question does not apply.
160
+ */
161
+ hasReleased(): Promise<boolean>;
162
+ /**
163
+ * Fire the events a `click` or `hover` trigger is waiting for.
164
+ *
165
+ * Both, because the harness cannot tell which one it is: `[trigger]` never reaches the
166
+ * DOM. The component consumes at most one — its listener is registered `{ once: true }`
167
+ * and the simulator refuses to start twice — so this is safe to call whatever the
168
+ * trigger is, and safe to call again.
169
+ *
170
+ * It does nothing for `auto`, which released on mount, and nothing for `scroll`, which
171
+ * waits on an `IntersectionObserver`: jsdom has none, so a spec has to stub the global
172
+ * and fire the callback itself. Same precondition as {@link hasReleased} — stub the
173
+ * boxes BEFORE calling this, because a start that bails on a zero-sized host still
174
+ * burns the trigger.
175
+ *
176
+ * Both are dispatched as bare events rather than through the harness's `click()` and
177
+ * `hover()` helpers, which would bring a whole pointer sequence with them. Released
178
+ * words listen for `pointerdown` so they can be picked up and thrown, and that handler
179
+ * calls `setPointerCapture` — which a unit environment does not implement, so the
180
+ * realistic version of this method would throw the moment the simulator was running.
181
+ */
182
+ release(): Promise<void>;
183
+ }
184
+
185
+ export { WrFallingTextHarness };
186
+ export type { WrFallingTextHarnessFilters };
@@ -0,0 +1,130 @@
1
+ import { BaseHarnessFilters, 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
+ /** Narrows which `<wr-fuzzy-text>` a harness query matches. */
11
+ interface WrFuzzyTextHarnessFilters extends BaseHarnessFilters {
12
+ /**
13
+ * Match the headline — a string is an exact match, a RegExp is tested.
14
+ *
15
+ * The only filter, and the only one possible: everything else this component is told
16
+ * ends up as a canvas font string or a fill style and never reaches the DOM, so the
17
+ * readable copy of the text is the sole thing that tells two headlines apart. Matched
18
+ * through `WrFuzzyTextHarness.getText`, which reads the screen-reader span rather than
19
+ * the pixels.
20
+ */
21
+ readonly text?: string | RegExp;
22
+ }
23
+
24
+ /**
25
+ * @license
26
+ *
27
+ * Use of this source code is governed by an MIT-style license that can be
28
+ * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE
29
+ */
30
+
31
+ /**
32
+ * Test harness for `<wr-fuzzy-text>` — a headline drawn into a canvas, one row of pixels
33
+ * at a time, with a random offset per row.
34
+ *
35
+ * **The drawing is out of reach and the accessible half is not, which is the whole shape
36
+ * of this harness.** There is no 2D context in a unit test — `getContext('2d')` returns
37
+ * `null`, and the component's `init()` gives up on the line after — so nothing downstream
38
+ * of that ever happens: no font load, no animation frame, no canvas listeners, no sizing.
39
+ * What survives is the markup, and the markup is the part that regressed: everything this
40
+ * component rendered used to be a canvas, so a screen reader got an empty element where
41
+ * the page showed a headline. {@link getText} and {@link isDecorative} are that fix,
42
+ * asserted as the PAIR it has to be — a readable copy plus a hidden pixel copy. Reading
43
+ * one without the other passes just as happily on a component that announces the headline
44
+ * twice.
45
+ *
46
+ * **The canvas is selected by tag, not by a class.** It carries no BEM name, unlike the
47
+ * two sibling animation canvases — this entry point ships no stylesheet at all, which is
48
+ * also why {@link isTextVisuallyHidden} is a real question rather than a formality.
49
+ *
50
+ * Nothing about the animation is offered, and none of it is a gap that could be closed by
51
+ * trying harder. There is no `getIntensity()` / `isFuzzing()` / `isGlitching()`: those are
52
+ * closure variables inside `init()` that only ever manifest as pixels, and in a test the
53
+ * function returns before they exist. No `hover()` / `click()` / `getHoverIntensity()`:
54
+ * the pointer listeners are attached only after a real context AND a resolved
55
+ * `document.fonts` — neither of which a unit test has — so a dispatched event would land
56
+ * on nothing and the assertion would pass vacuously; the hit test also measures the canvas
57
+ * box, which is all zeros. No `getCanvasSize()`, which would be asserting jsdom's untouched
58
+ * 300×150 default rather than the component's layout maths. No `getColor()` /
59
+ * `getFontSize()` / `getFontFamily()` / `getGradient()`: every one of those becomes a
60
+ * canvas font string or a fill style and never reaches the DOM. No `getRenderedText()` or
61
+ * `readPixel()` — there are no pixels, and faking a context would change what every spec
62
+ * here is testing. And no `isReducedMotion()`: the preference is honoured by taking a
63
+ * different DRAWING path, with identical markup, classes and attributes either way, so the
64
+ * method would answer the same for a component that honoured it and one that ignored it.
65
+ *
66
+ * @example
67
+ * ```ts
68
+ * const headline = await loader.getHarness(WrFuzzyTextHarness.with({ text: '404' }));
69
+ *
70
+ * expect(await headline.getText()).toBe('404');
71
+ * expect(await headline.isDecorative()).toBe(true);
72
+ * ```
73
+ *
74
+ * @see https://ngwr.dev/guides/testing
75
+ */
76
+ declare class WrFuzzyTextHarness extends ComponentHarness {
77
+ static hostSelector: string;
78
+ /** Build a predicate that narrows the query — pass to `getHarness` / `getAllHarnesses`. */
79
+ static with(options?: WrFuzzyTextHarnessFilters): HarnessPredicate<WrFuzzyTextHarness>;
80
+ private readonly srOnly;
81
+ private readonly canvas;
82
+ /**
83
+ * The headline as TEXT, trimmed.
84
+ *
85
+ * From the screen-reader span, which is the only place the string exists as characters —
86
+ * the visible copy is glyphs painted into a canvas. This is the single assertion that
87
+ * separates the component from an empty box for anyone not looking at the screen, and it
88
+ * is the one that has already regressed once.
89
+ *
90
+ * There is no letter-by-letter split to worry about here, unlike the other text
91
+ * animations in the library: the span holds one text node, so what a reader announces is
92
+ * what {@link WrFuzzyTextHarness.getText} returns.
93
+ */
94
+ getText(): Promise<string>;
95
+ /**
96
+ * Whether the pixel copy stays out of the accessibility tree.
97
+ *
98
+ * The other half of {@link getText}. The canvas has no text and no name, so without its
99
+ * `aria-hidden` a reader meets an anonymous element next to the headline it already
100
+ * read — or, if the span ever goes away again, only the anonymous element. Asserting the
101
+ * two together is what pins the arrangement; asserting either alone does not.
102
+ */
103
+ isDecorative(): Promise<boolean>;
104
+ /**
105
+ * Whether the drawing surface is in the markup.
106
+ *
107
+ * True even where it can never be painted — under SSR, in a prerender, in a browser that
108
+ * refuses a 2D context, and in every unit test — because the element is rendered
109
+ * unconditionally and only the drawing depends on a context. That is precisely the state
110
+ * the readable copy exists to cover, so this is the method that says "the fallback is
111
+ * carrying the page right now".
112
+ */
113
+ hasCanvas(): Promise<boolean>;
114
+ /**
115
+ * Whether the readable copy hides itself without a stylesheet.
116
+ *
117
+ * This entry point ships no CSS. The span is therefore hidden with inline declarations
118
+ * rather than an `.sr-only` class, and that is load-bearing rather than a style choice:
119
+ * move the block into a class and every consumer who imported no CSS sees the headline
120
+ * twice — once as pixels, once as a stray line of markup underneath.
121
+ *
122
+ * Three of the declarations are checked, not one, because they fail apart: `position`
123
+ * takes the span out of flow, the 1px box collapses it, and `clip-path` is what keeps
124
+ * the character still reachable by a reader instead of `display: none`-ing it away.
125
+ */
126
+ isTextVisuallyHidden(): Promise<boolean>;
127
+ }
128
+
129
+ export { WrFuzzyTextHarness };
130
+ export type { WrFuzzyTextHarnessFilters };
@@ -0,0 +1,71 @@
1
+ import { BaseHarnessFilters, 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
+ /** Narrows which `<wr-gauge>` a harness query matches. */
11
+ interface WrGaugeHarnessFilters extends BaseHarnessFilters {
12
+ /** Match the dial's accessible name. */
13
+ readonly ariaLabel?: string | RegExp;
14
+ /** Match the value the dial announces. */
15
+ readonly value?: number;
16
+ }
17
+
18
+ /**
19
+ * @license
20
+ *
21
+ * Use of this source code is governed by an MIT-style license that can be
22
+ * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE
23
+ */
24
+
25
+ /**
26
+ * Test harness for `<wr-gauge>`.
27
+ *
28
+ * **The dial is `aria-hidden` and the printed number is optional, so the ARIA is the
29
+ * whole readable surface.** The component carries `role="meter"` with the value
30
+ * trio and an `aria-valuetext` that includes the suffix — which is what makes a
31
+ * gauge with `showValue` off readable at all. {@link getValue} is what it announces;
32
+ * {@link getDisplayValue} is what is drawn, and it is `null` when nothing is.
33
+ *
34
+ * The arc is an SVG path, and its `d` is a rendering detail rather than an
35
+ * assertion — it moves with the size, the stroke width and the sweep.
36
+ *
37
+ * @see https://ngwr.dev/guides/testing
38
+ */
39
+ declare class WrGaugeHarness extends ComponentHarness {
40
+ static hostSelector: string;
41
+ /** Build a predicate that narrows the query — pass to `getHarness` / `getAllHarnesses`. */
42
+ static with(options?: WrGaugeHarnessFilters): HarnessPredicate<WrGaugeHarness>;
43
+ private readonly surface;
44
+ /** The role the dial announces — `meter`, which is what a gauge is. */
45
+ getRole(): Promise<string | null>;
46
+ /** The dial's accessible name. */
47
+ getAccessibleName(): Promise<string | null>;
48
+ /** The value it announces, from `aria-valuenow`. */
49
+ getValue(): Promise<number>;
50
+ /** The bottom of the range, from `aria-valuemin`. */
51
+ getMin(): Promise<number>;
52
+ /** The top of the range, from `aria-valuemax`. */
53
+ getMax(): Promise<number>;
54
+ /**
55
+ * The spoken form of the value — the number with its suffix.
56
+ *
57
+ * The reason a gauge with no printed value is still readable, and worth asserting
58
+ * separately from {@link getValue}: a percentage announced as a bare `72` says
59
+ * something different from `72%`.
60
+ */
61
+ getValueText(): Promise<string | null>;
62
+ /** The number printed in the middle, suffix included, or `null` when `showValue` is off. */
63
+ getDisplayValue(): Promise<string | null>;
64
+ /** The suffix alone, or `null` when there is none — or nothing is printed. */
65
+ getSuffix(): Promise<string | null>;
66
+ /** The dial's width in pixels, as the component writes it inline. */
67
+ getSize(): Promise<number>;
68
+ }
69
+
70
+ export { WrGaugeHarness };
71
+ export type { WrGaugeHarnessFilters };
@@ -0,0 +1,172 @@
1
+ import { BaseHarnessFilters, 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
+ /** Narrows which `<wr-glitch-text>` a harness query matches. */
11
+ interface WrGlitchTextHarnessFilters extends BaseHarnessFilters {
12
+ /** Match the rendered text — a string is an exact match, a RegExp is tested. */
13
+ readonly text?: string | RegExp;
14
+ /**
15
+ * Match only the hover-gated variant (`true`) or only the always-on one (`false`).
16
+ * Note that hover-gated is the DEFAULT, so `{ hoverOnly: false }` selects the
17
+ * component that was explicitly told `[enableOnHover]="false"`.
18
+ */
19
+ readonly hoverOnly?: boolean;
20
+ }
21
+ /** How long each clone layer takes to complete one tear cycle, in seconds. */
22
+ interface WrGlitchTextDurations {
23
+ /** The `::before` clone — `speed × 2`. */
24
+ readonly before: number;
25
+ /** The `::after` clone — `speed × 3`. */
26
+ readonly after: number;
27
+ }
28
+ /** The two clone `text-shadow`s, exactly as the component wrote them. */
29
+ interface WrGlitchTextColourSplit {
30
+ /** The `::before` clone's shadow — offset right, and tinted `info`. */
31
+ readonly before: string;
32
+ /** The `::after` clone's shadow — offset left, and tinted `danger`. */
33
+ readonly after: string;
34
+ }
35
+
36
+ /**
37
+ * @license
38
+ *
39
+ * Use of this source code is governed by an MIT-style license that can be
40
+ * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE
41
+ */
42
+
43
+ /**
44
+ * Test harness for `<wr-glitch-text>`.
45
+ *
46
+ * **The one thing worth testing here is the pairing.** The two torn clones are
47
+ * `::before` / `::after` pseudo-elements whose glyphs come from `content:
48
+ * attr(data-text)`, so the string has to be in the host's text node AND in the host's
49
+ * `data-text` attribute. Drop either binding and the visible text stays flawless while
50
+ * the effect disappears — no error, no missing element, nothing else in the DOM
51
+ * changes. {@link isCloneTextInSync} is that assertion in one call.
52
+ *
53
+ * **Everything else the harness reads is an inline custom property**, because that is
54
+ * everything the component writes: the two clone durations, the two colour-split
55
+ * shadows and the optional slice background. They are worth pinning for the unit
56
+ * transforms — a duration that loses its `s`, or two clones that end up on the same
57
+ * multiple and tear in lockstep, is a regression that changes no class, no attribute
58
+ * and no text.
59
+ *
60
+ * **What the harness deliberately does not offer**, and why. There is no
61
+ * `isGlitching()`: the tear is a `@keyframes` animation on a pseudo-element, applied by
62
+ * a stylesheet a unit test never loads, so the only available answer is a class name
63
+ * repackaged as a state. There is no `hover()` / `isGlitchingOnHover()`: the gate is a
64
+ * `:hover` SELECTOR, and the DOM after a dispatched mouse event is byte-identical to
65
+ * the DOM before it — a spec built on that would pass against a component whose hover
66
+ * rules had been deleted. There is no `getCloneOffset()` / `getClipPath()`: the ±10px
67
+ * offsets and the tear bands live in the stylesheet and are pure layout besides. There
68
+ * is no `respectsReducedMotion()`: the handling is a `@media` block with no TypeScript
69
+ * counterpart, so the DOM is identical in both states and there is nothing to read.
70
+ * There is no resolver for the shadow colours — they hold `var(--wr-color-info)` /
71
+ * `var(--wr-color-danger)`, and resolving those needs the theme stylesheet. And there
72
+ * is no `getSpeed()`: the input is never written to the DOM, only its two products are,
73
+ * and dividing one back would invent a precision the component never published (at
74
+ * `[speed]="0.3"` the after-duration is literally `0.8999999999999999s`).
75
+ *
76
+ * @example
77
+ * ```ts
78
+ * const glitch = await loader.getHarness(WrGlitchTextHarness);
79
+ *
80
+ * expect(await glitch.isCloneTextInSync()).toBe(true);
81
+ * expect((await glitch.getDurations()).after).toBeCloseTo(3);
82
+ * ```
83
+ *
84
+ * @see https://ngwr.dev/guides/testing
85
+ */
86
+ declare class WrGlitchTextHarness extends ComponentHarness {
87
+ static hostSelector: string;
88
+ /** Build a predicate that narrows the query — pass to `getHarness` / `getAllHarnesses`. */
89
+ static with(options?: WrGlitchTextHarnessFilters): HarnessPredicate<WrGlitchTextHarness>;
90
+ /**
91
+ * The rendered text, trimmed.
92
+ *
93
+ * The half a sighted user reads, and the half every other check would notice
94
+ * breaking. The component interpolates the input straight onto the host, so there is
95
+ * no inner element to query.
96
+ */
97
+ getText(): Promise<string>;
98
+ /**
99
+ * The string the two clone layers draw from — the host's `data-text`.
100
+ *
101
+ * The half nothing else notices. `content: attr(data-text)` is how both pseudo-clones
102
+ * get their glyphs, so this attribute IS the effect. `null` means the mirror is gone
103
+ * and the component is rendering plain text with extra classes.
104
+ */
105
+ getCloneText(): Promise<string | null>;
106
+ /**
107
+ * Whether the clone attribute still mirrors the rendered text.
108
+ *
109
+ * The component's defining silent failure, as a single assertion — two separate reads
110
+ * are two things a spec can forget to write together. Compared raw on both sides,
111
+ * with no trimming: the template is the bare interpolation, so the text node and the
112
+ * attribute are byte-identical or the mirror is broken. An empty `text` is legal and
113
+ * counts as in sync — `data-text=""` is a mirror of nothing, not a missing binding,
114
+ * which is why {@link getCloneText} reports the absent case as `null` instead.
115
+ */
116
+ isCloneTextInSync(): Promise<boolean>;
117
+ /**
118
+ * Whether the effect idles until the pointer arrives (`enableOnHover`).
119
+ *
120
+ * The `wr-glitch-text--hover-only` class is the only DOM trace of that input, the gate
121
+ * itself being a `:hover` rule. It defaults to `true`, so this is also how a spec
122
+ * catches the day someone "fixes" the default to match the component's own JSDoc,
123
+ * which describes the opposite.
124
+ */
125
+ isHoverOnly(): Promise<boolean>;
126
+ /**
127
+ * How long each clone takes to complete a cycle, in seconds.
128
+ *
129
+ * The two must NOT be equal: they run at 2× and 3× the `speed` multiplier so the tears
130
+ * fall out of phase, and clones tearing in lockstep read as one shifted copy rather
131
+ * than as a glitch. That regression changes no class, no attribute and no text, and no
132
+ * visual gate in this repo can see it either.
133
+ *
134
+ * These are raw JavaScript floats — `[speed]="0.3"` writes `0.8999999999999999s` —
135
+ * so compare with `toBeCloseTo`, never against a formatted string.
136
+ */
137
+ getDurations(): Promise<WrGlitchTextDurations>;
138
+ /**
139
+ * Whether the red / cyan colour split is switched on (`enableShadows`).
140
+ *
141
+ * The component writes the literal `none` rather than removing the properties when the
142
+ * split is off, so "disabled" and "never set" stay distinguishable — an absent property
143
+ * would mean a broken binding, and this reports that as no split either.
144
+ */
145
+ hasColourSplit(): Promise<boolean>;
146
+ /**
147
+ * The two clone `text-shadow`s, exactly as written — `var()` tokens and all.
148
+ *
149
+ * Returned unresolved on purpose: resolving `var(--wr-color-info)` needs the theme
150
+ * stylesheet, and a unit test that answered with a colour would be inventing one.
151
+ * What the raw pair does prove is the thing a copy-paste breaks — the offsets must
152
+ * have OPPOSITE signs and the two tints must be DIFFERENT intents. Give both clones
153
+ * the same colour, or the same sign, and the chromatic aberration is gone while every
154
+ * other reading stays green.
155
+ */
156
+ getColourSplit(): Promise<WrGlitchTextColourSplit>;
157
+ /**
158
+ * The masking colour behind the clone slices, or `null` when none was given.
159
+ *
160
+ * The binding emits `null` for an empty input, so the property is ABSENT rather than
161
+ * set to `transparent`, and the stylesheet's own `var(--wr-glitch-text-bg, transparent)`
162
+ * fallback does the work. A component that started writing `transparent` inline would
163
+ * look identical and break nothing until a consumer relied on that fallback — which is
164
+ * the whole reason this reports the absent case as `null` instead of as a colour.
165
+ */
166
+ getSliceBackground(): Promise<string | null>;
167
+ /** One of the host's inline duration properties, as a number of seconds. */
168
+ private seconds;
169
+ }
170
+
171
+ export { WrGlitchTextHarness };
172
+ export type { WrGlitchTextColourSplit, WrGlitchTextDurations, WrGlitchTextHarnessFilters };