@dynatrace/strato-components-testing 3.11.3 → 3.12.1

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 (130) hide show
  1. package/esm/jest/index.js +9 -1
  2. package/esm/jest/index.js.map +2 -2
  3. package/esm/jest/mocks/create-range-mock.js +4 -22
  4. package/esm/jest/mocks/create-range-mock.js.map +2 -2
  5. package/esm/jest/mocks/fetch-mock.js +4 -14
  6. package/esm/jest/mocks/fetch-mock.js.map +2 -2
  7. package/esm/jest/mocks/framer-motion-mock.js +3 -48
  8. package/esm/jest/mocks/framer-motion-mock.js.map +3 -3
  9. package/esm/jest/mocks/intersection-observer-mock.js +4 -35
  10. package/esm/jest/mocks/intersection-observer-mock.js.map +2 -2
  11. package/esm/jest/mocks/match-media-mock.js +4 -21
  12. package/esm/jest/mocks/match-media-mock.js.map +2 -2
  13. package/esm/jest/mocks/resize-observer-mock.js +4 -18
  14. package/esm/jest/mocks/resize-observer-mock.js.map +2 -2
  15. package/esm/jest/mocks/scroll-into-view-mock.js +4 -10
  16. package/esm/jest/mocks/scroll-into-view-mock.js.map +2 -2
  17. package/esm/jest/testing-helpers/filters/filter-field.js +12 -415
  18. package/esm/jest/testing-helpers/filters/filter-field.js.map +2 -2
  19. package/esm/jest/testing-helpers/tables/datatable.js +1 -11
  20. package/esm/jest/testing-helpers/tables/datatable.js.map +2 -2
  21. package/esm/jest/testing-helpers/utils/setup-user-event.js +1 -1
  22. package/esm/jest/testing-helpers/utils/setup-user-event.js.map +2 -2
  23. package/esm/shared/mocks/create-range-mock.js +29 -0
  24. package/esm/shared/mocks/create-range-mock.js.map +7 -0
  25. package/esm/shared/mocks/fetch-mock.js +23 -0
  26. package/esm/shared/mocks/fetch-mock.js.map +7 -0
  27. package/esm/shared/mocks/framer-motion-mock.js +54 -0
  28. package/esm/shared/mocks/framer-motion-mock.js.map +7 -0
  29. package/esm/shared/mocks/intersection-observer-mock.js +39 -0
  30. package/esm/shared/mocks/intersection-observer-mock.js.map +7 -0
  31. package/esm/shared/mocks/match-media-mock.js +28 -0
  32. package/esm/shared/mocks/match-media-mock.js.map +7 -0
  33. package/esm/shared/mocks/resize-observer-mock.js +25 -0
  34. package/esm/shared/mocks/resize-observer-mock.js.map +7 -0
  35. package/esm/shared/mocks/scroll-into-view-mock.js +17 -0
  36. package/esm/shared/mocks/scroll-into-view-mock.js.map +7 -0
  37. package/esm/shared/testing-helpers/filters/filter-field.js +439 -0
  38. package/esm/shared/testing-helpers/filters/filter-field.js.map +7 -0
  39. package/esm/vitest/index.js +149 -0
  40. package/esm/vitest/index.js.map +7 -0
  41. package/esm/vitest/mocks/create-range-mock.js +10 -0
  42. package/esm/vitest/mocks/create-range-mock.js.map +7 -0
  43. package/esm/vitest/mocks/fetch-mock.js +10 -0
  44. package/esm/vitest/mocks/fetch-mock.js.map +7 -0
  45. package/esm/vitest/mocks/framer-motion-mock.js +13 -0
  46. package/esm/vitest/mocks/framer-motion-mock.js.map +7 -0
  47. package/esm/vitest/mocks/intersection-observer-mock.js +10 -0
  48. package/esm/vitest/mocks/intersection-observer-mock.js.map +7 -0
  49. package/esm/vitest/mocks/match-media-mock.js +10 -0
  50. package/esm/vitest/mocks/match-media-mock.js.map +7 -0
  51. package/esm/vitest/mocks/resize-observer-mock.js +10 -0
  52. package/esm/vitest/mocks/resize-observer-mock.js.map +7 -0
  53. package/esm/vitest/mocks/scroll-into-view-mock.js +10 -0
  54. package/esm/vitest/mocks/scroll-into-view-mock.js.map +7 -0
  55. package/esm/vitest/preset/index.js +5 -0
  56. package/esm/vitest/preset/index.js.map +7 -0
  57. package/esm/vitest/preset/vitest-preset.js +9 -0
  58. package/esm/vitest/preset/vitest-preset.js.map +7 -0
  59. package/esm/vitest/setup/index.js +77 -0
  60. package/esm/vitest/setup/index.js.map +7 -0
  61. package/esm/vitest/testing-helpers/filters/filter-field.js +24 -0
  62. package/esm/vitest/testing-helpers/filters/filter-field.js.map +7 -0
  63. package/esm/vitest/testing-helpers/utils/isFakeTimersEnabled.js +8 -0
  64. package/esm/vitest/testing-helpers/utils/isFakeTimersEnabled.js.map +7 -0
  65. package/esm/vitest/testing-helpers/utils/setup-user-event.js +16 -0
  66. package/esm/vitest/testing-helpers/utils/setup-user-event.js.map +7 -0
  67. package/jest/index.d.ts +3 -1
  68. package/jest/index.js +4 -0
  69. package/jest/mocks/create-range-mock.d.ts +4 -11
  70. package/jest/mocks/create-range-mock.js +4 -22
  71. package/jest/mocks/fetch-mock.d.ts +4 -10
  72. package/jest/mocks/fetch-mock.js +4 -14
  73. package/jest/mocks/framer-motion-mock.js +3 -46
  74. package/jest/mocks/intersection-observer-mock.d.ts +4 -11
  75. package/jest/mocks/intersection-observer-mock.js +4 -35
  76. package/jest/mocks/match-media-mock.d.ts +4 -11
  77. package/jest/mocks/match-media-mock.js +4 -21
  78. package/jest/mocks/resize-observer-mock.d.ts +4 -13
  79. package/jest/mocks/resize-observer-mock.js +4 -18
  80. package/jest/mocks/scroll-into-view-mock.d.ts +4 -10
  81. package/jest/mocks/scroll-into-view-mock.js +4 -10
  82. package/jest/testing-helpers/filters/filter-field.d.ts +5 -109
  83. package/jest/testing-helpers/filters/filter-field.js +11 -414
  84. package/jest/testing-helpers/tables/datatable.js +1 -11
  85. package/jest/testing-helpers/utils/setup-user-event.js +1 -1
  86. package/package.json +3 -3
  87. package/shared/mocks/create-range-mock.d.ts +7 -0
  88. package/shared/mocks/create-range-mock.js +47 -0
  89. package/shared/mocks/fetch-mock.d.ts +13 -0
  90. package/shared/mocks/fetch-mock.js +41 -0
  91. package/shared/mocks/framer-motion-mock.d.ts +15 -0
  92. package/shared/mocks/framer-motion-mock.js +70 -0
  93. package/shared/mocks/intersection-observer-mock.d.ts +7 -0
  94. package/shared/mocks/intersection-observer-mock.js +57 -0
  95. package/shared/mocks/match-media-mock.d.ts +7 -0
  96. package/shared/mocks/match-media-mock.js +46 -0
  97. package/shared/mocks/resize-observer-mock.d.ts +8 -0
  98. package/shared/mocks/resize-observer-mock.js +43 -0
  99. package/shared/mocks/scroll-into-view-mock.d.ts +7 -0
  100. package/shared/mocks/scroll-into-view-mock.js +35 -0
  101. package/shared/testing-helpers/filters/filter-field.d.ts +128 -0
  102. package/shared/testing-helpers/filters/filter-field.js +449 -0
  103. package/vitest/index.d.ts +32 -0
  104. package/vitest/index.js +98 -0
  105. package/vitest/mocks/create-range-mock.d.ts +5 -0
  106. package/vitest/mocks/create-range-mock.js +28 -0
  107. package/vitest/mocks/fetch-mock.d.ts +5 -0
  108. package/vitest/mocks/fetch-mock.js +28 -0
  109. package/vitest/mocks/framer-motion-mock.d.ts +27 -0
  110. package/vitest/mocks/framer-motion-mock.js +31 -0
  111. package/vitest/mocks/intersection-observer-mock.d.ts +5 -0
  112. package/vitest/mocks/intersection-observer-mock.js +28 -0
  113. package/vitest/mocks/match-media-mock.d.ts +5 -0
  114. package/vitest/mocks/match-media-mock.js +28 -0
  115. package/vitest/mocks/resize-observer-mock.d.ts +5 -0
  116. package/vitest/mocks/resize-observer-mock.js +28 -0
  117. package/vitest/mocks/scroll-into-view-mock.d.ts +5 -0
  118. package/vitest/mocks/scroll-into-view-mock.js +28 -0
  119. package/vitest/preset/index.d.ts +2 -0
  120. package/vitest/preset/index.js +23 -0
  121. package/vitest/preset/vitest-preset.d.ts +24 -0
  122. package/vitest/preset/vitest-preset.js +27 -0
  123. package/vitest/setup/index.d.ts +2 -0
  124. package/vitest/setup/index.js +40 -0
  125. package/vitest/testing-helpers/filters/filter-field.d.ts +6 -0
  126. package/vitest/testing-helpers/filters/filter-field.js +36 -0
  127. package/vitest/testing-helpers/utils/isFakeTimersEnabled.d.ts +6 -0
  128. package/vitest/testing-helpers/utils/isFakeTimersEnabled.js +26 -0
  129. package/vitest/testing-helpers/utils/setup-user-event.d.ts +7 -0
  130. package/vitest/testing-helpers/utils/setup-user-event.js +44 -0
@@ -0,0 +1,439 @@
1
+ import {
2
+ act,
3
+ screen,
4
+ waitFor,
5
+ waitForElementToBeRemoved,
6
+ within
7
+ } from "@testing-library/react";
8
+ import {
9
+ PointerEventsCheckLevel
10
+ } from "@testing-library/user-event";
11
+ class FilterFieldPinnedAndRecentSuggestionBase {
12
+ /** @internal */
13
+ constructor(suggestionRoot, deps) {
14
+ this.suggestionRoot = suggestionRoot;
15
+ this._deps = deps;
16
+ }
17
+ suggestionRoot;
18
+ _deps;
19
+ get pinButtonElement() {
20
+ return this.suggestionRoot.querySelector(
21
+ "button[aria-checked]"
22
+ );
23
+ }
24
+ /**
25
+ * Receives the value of the suggestion that would be applied.
26
+ */
27
+ get value() {
28
+ return this.suggestionRoot.childNodes.item(1).textContent;
29
+ }
30
+ /**
31
+ * Returns if the current suggestion is currently pinned or not.
32
+ */
33
+ get pinned() {
34
+ if (this.pinButtonElement) {
35
+ return this.pinButtonElement.getAttribute("aria-checked") === "true";
36
+ }
37
+ return false;
38
+ }
39
+ /**
40
+ * Applies the suggestion to the filter field.
41
+ */
42
+ async apply() {
43
+ await this._deps.setupUserEvent({
44
+ pointerEventsCheck: PointerEventsCheckLevel.Never
45
+ }).click(this.suggestionRoot);
46
+ }
47
+ get pinEnabled() {
48
+ if (this.pinButtonElement) {
49
+ return this.pinButtonElement.getAttribute("aria-disabled") === "false";
50
+ }
51
+ return false;
52
+ }
53
+ /**
54
+ * Allows the suggestion to be pinned if not pinned already.
55
+ */
56
+ async pin() {
57
+ if (this.pinned === true) {
58
+ return;
59
+ }
60
+ if (this.pinButtonElement) {
61
+ await this._deps.setupUserEvent({
62
+ pointerEventsCheck: PointerEventsCheckLevel.Never
63
+ }).click(this.pinButtonElement);
64
+ if (this._deps.isFakeTimersEnabled()) {
65
+ await act(async () => {
66
+ this._deps.runOnlyPendingTimers();
67
+ });
68
+ }
69
+ }
70
+ }
71
+ /**
72
+ * Allows the suggestion to be unpinned if not unpinned already.
73
+ */
74
+ async unpin() {
75
+ if (this.pinned === false) {
76
+ return;
77
+ }
78
+ if (this.pinButtonElement) {
79
+ await this._deps.setupUserEvent({
80
+ pointerEventsCheck: PointerEventsCheckLevel.Never
81
+ }).click(this.pinButtonElement);
82
+ if (this._deps.isFakeTimersEnabled()) {
83
+ await act(async () => {
84
+ this._deps.runOnlyPendingTimers();
85
+ });
86
+ }
87
+ }
88
+ }
89
+ }
90
+ class FilterFieldPinnedSuggestion extends FilterFieldPinnedAndRecentSuggestionBase {
91
+ /**
92
+ * Receives the value of the suggestion that would be applied.
93
+ */
94
+ get value() {
95
+ return this.suggestionRoot.childNodes.item(0).textContent;
96
+ }
97
+ }
98
+ class FilterFieldRecentSuggestion extends FilterFieldPinnedAndRecentSuggestionBase {
99
+ /**
100
+ * Receives the value of the suggestion that would be applied.
101
+ */
102
+ get value() {
103
+ return this.suggestionRoot.querySelector('[role="option"]')?.childNodes.item(0).textContent ?? null;
104
+ }
105
+ /**
106
+ * Receives time offset indicating when the filter statement has last been used.
107
+ */
108
+ get lastUsed() {
109
+ return this.suggestionRoot.querySelector('[role="option"]')?.childNodes.item(1).textContent ?? null;
110
+ }
111
+ }
112
+ class FilterFieldHelper {
113
+ /** @internal */
114
+ constructor(dataTestId, deps) {
115
+ this.dataTestId = dataTestId;
116
+ this._deps = deps;
117
+ }
118
+ dataTestId;
119
+ _deps;
120
+ get filterFieldWrapper() {
121
+ return screen.getByTestId(this.dataTestId);
122
+ }
123
+ get editorView() {
124
+ return this.filterFieldWrapper.querySelector(
125
+ ".cm-content"
126
+ )?.cmTile?.view;
127
+ }
128
+ get inputElement() {
129
+ return within(this.filterFieldWrapper).getAllByRole(
130
+ "textbox",
131
+ { hidden: true }
132
+ )[1];
133
+ }
134
+ get filterFieldElement() {
135
+ return within(this.filterFieldWrapper).getByRole(
136
+ "textbox",
137
+ {
138
+ name: "Filter field",
139
+ hidden: true
140
+ }
141
+ );
142
+ }
143
+ get placeholderElement() {
144
+ return this.filterFieldWrapper.querySelector(".cm-placeholder");
145
+ }
146
+ get suggestionsContainer() {
147
+ return screen.queryByTestId(`filterfield-suggestions-${this.dataTestId}`);
148
+ }
149
+ /** Returns the value of the native text input. */
150
+ get value() {
151
+ return this.inputElement.value;
152
+ }
153
+ /** Returns the value of the filter field. */
154
+ get filterFieldValue() {
155
+ return this.filterFieldElement.textContent || "";
156
+ }
157
+ /**
158
+ * The placeholder text shown in the filter field.
159
+ * Undefined if the placeholder is currently not rendered.
160
+ */
161
+ get placeholder() {
162
+ return this.placeholderElement?.textContent || void 0;
163
+ }
164
+ /** Focus the filter field if not already focused. */
165
+ async focus() {
166
+ if (!this.filterFieldWrapper.contains(document.activeElement)) {
167
+ await act(async () => {
168
+ this.inputElement.focus();
169
+ });
170
+ expect(
171
+ this.filterFieldWrapper.contains(document.activeElement)
172
+ ).toBeTruthy();
173
+ if (this._deps.isFakeTimersEnabled()) {
174
+ await act(async () => {
175
+ this._deps.runOnlyPendingTimers();
176
+ });
177
+ }
178
+ }
179
+ }
180
+ /** Blur the filter field by tabbing out of it. */
181
+ async blur() {
182
+ await act(async () => {
183
+ this.filterFieldElement.blur();
184
+ });
185
+ if (this._deps.isFakeTimersEnabled()) {
186
+ await act(async () => {
187
+ this._deps.runOnlyPendingTimers();
188
+ });
189
+ }
190
+ }
191
+ /** Fill a provided value into the filter field. */
192
+ async type(text, cursorIndex) {
193
+ const regEx = new RegExp(text, "g");
194
+ const matches = this.value.match(regEx);
195
+ await this.moveCursorTo(cursorIndex ?? -1);
196
+ await this._deps.setupUserEvent({
197
+ pointerEventsCheck: PointerEventsCheckLevel.Never
198
+ }).keyboard(text);
199
+ await waitFor(() => {
200
+ expect(this.value.match(regEx) ?? []).toHaveLength(
201
+ (matches?.length ?? 0) + 1
202
+ );
203
+ });
204
+ if (this._deps.isFakeTimersEnabled()) {
205
+ await act(async () => {
206
+ this._deps.runOnlyPendingTimers();
207
+ });
208
+ }
209
+ }
210
+ /** Clears the filter field value. */
211
+ async clear() {
212
+ if (this.value !== "") {
213
+ await this._deps.setupUserEvent({
214
+ pointerEventsCheck: PointerEventsCheckLevel.Never
215
+ }).click(
216
+ await within(this.filterFieldWrapper).findByRole("button", {
217
+ name: "Clear filter",
218
+ hidden: true
219
+ })
220
+ );
221
+ if (this._deps.isFakeTimersEnabled()) {
222
+ await act(async () => {
223
+ this._deps.runOnlyPendingTimers();
224
+ });
225
+ }
226
+ }
227
+ }
228
+ /**
229
+ * Moves the cursor to the given index.
230
+ * -1 moves to the end of the input.
231
+ */
232
+ async moveCursorTo(index) {
233
+ const user = this._deps.setupUserEvent({
234
+ pointerEventsCheck: PointerEventsCheckLevel.Never
235
+ });
236
+ if (index === -1) {
237
+ await act(async () => {
238
+ await this.focus();
239
+ });
240
+ await user.keyboard("{PageDown}");
241
+ return;
242
+ }
243
+ const editorView = this.editorView;
244
+ if (!editorView) {
245
+ console.warn("FilterField editor could not be found. Panic now.");
246
+ return;
247
+ }
248
+ await act(async () => {
249
+ await this.focus();
250
+ editorView.dispatch({
251
+ selection: {
252
+ anchor: Math.min(index, editorView.state.doc.length)
253
+ }
254
+ });
255
+ });
256
+ if (this._deps.isFakeTimersEnabled()) {
257
+ await act(async () => {
258
+ this._deps.runOnlyPendingTimers();
259
+ });
260
+ }
261
+ }
262
+ /** Open the suggestions overlay with the keyboard shortcut. */
263
+ async openSuggestions() {
264
+ await act(async () => {
265
+ await this.focus();
266
+ });
267
+ await this._deps.setupUserEvent({
268
+ pointerEventsCheck: PointerEventsCheckLevel.Never
269
+ }).keyboard("{Control>}{Space}{/Control}");
270
+ await waitFor(
271
+ async () => expect(this.suggestionsContainer).toBeInTheDocument()
272
+ );
273
+ if (this._deps.isFakeTimersEnabled()) {
274
+ await act(async () => {
275
+ this._deps.runOnlyPendingTimers();
276
+ });
277
+ }
278
+ }
279
+ /**
280
+ * Close the suggestions overlay using the `Escape` key.
281
+ * If the filter field is focused, the focus will stay in the filter field.
282
+ */
283
+ async closeSuggestions() {
284
+ await act(async () => {
285
+ await this.focus();
286
+ });
287
+ await this._deps.setupUserEvent({
288
+ pointerEventsCheck: PointerEventsCheckLevel.Never
289
+ }).keyboard("{Escape}");
290
+ if (this.suggestionsContainer !== null) {
291
+ await waitForElementToBeRemoved(() => this.suggestionsContainer);
292
+ }
293
+ }
294
+ /** Apply the suggestion with the given text. */
295
+ async applySuggestion(queryString) {
296
+ await act(async () => {
297
+ await this.focus();
298
+ });
299
+ await waitFor(() => {
300
+ expect(this.suggestionsContainer).toBeInTheDocument();
301
+ });
302
+ if (!this.suggestionsContainer) {
303
+ console.warn(
304
+ "The filter field suggestions overlay seems to be closed. Make sure to open it before applying a suggestion."
305
+ );
306
+ return;
307
+ }
308
+ const showMore = within(this.suggestionsContainer).queryByRole("option", {
309
+ name: "Show more"
310
+ });
311
+ if (showMore) {
312
+ await this._deps.setupUserEvent({
313
+ pointerEventsCheck: PointerEventsCheckLevel.Never
314
+ }).click(showMore);
315
+ }
316
+ let suggestions = within(this.suggestionsContainer).queryAllByTestId(
317
+ queryString,
318
+ { exact: true }
319
+ );
320
+ if (suggestions.length > 0) {
321
+ suggestions = suggestions.reduce((acc, container) => {
322
+ const suggestion = within(container).queryByRole("option");
323
+ if (suggestion) {
324
+ acc.push(suggestion);
325
+ }
326
+ return acc;
327
+ }, []);
328
+ }
329
+ if (suggestions.length === 0) {
330
+ suggestions.push(
331
+ ...within(this.suggestionsContainer).queryAllByText(
332
+ (_, element) => element?.getAttribute("role") === "option" && // The replace is needed to ignore non-breaking spaces
333
+ element.firstElementChild?.textContent?.replace(/\s+/g, " ") === queryString.replace(/\s+/g, " ")
334
+ )
335
+ );
336
+ }
337
+ if (suggestions.length === 0) {
338
+ console.warn(
339
+ `Could not query a suggestion with the given queryString '${queryString}' (trying queryByTestId and queryByText).`
340
+ );
341
+ return;
342
+ }
343
+ if (suggestions.length > 1) {
344
+ console.warn(
345
+ "Found multiple suggestions with the given testId. Will apply the first one"
346
+ );
347
+ }
348
+ await this._deps.setupUserEvent({
349
+ pointerEventsCheck: PointerEventsCheckLevel.Never
350
+ }).click(suggestions[0]);
351
+ }
352
+ /** Get recent suggestions */
353
+ async getRecentSuggestions() {
354
+ if (this.suggestionsContainer) {
355
+ const recentGroup = within(this.suggestionsContainer).getByRole("group", {
356
+ name: "Recently used filters"
357
+ });
358
+ const suggestions = within(recentGroup).getAllByRole(
359
+ "group",
360
+ {
361
+ hidden: true,
362
+ name(_accessibleName, element) {
363
+ return element.getAttribute(
364
+ "data-strato-filter-field-virtual-focus-type"
365
+ ) === "recent-suggestion";
366
+ }
367
+ }
368
+ );
369
+ return suggestions.map(
370
+ (el) => new FilterFieldRecentSuggestion(el, this._deps)
371
+ );
372
+ }
373
+ return [];
374
+ }
375
+ /** Get pinned suggestions */
376
+ async getPinnedSuggestions() {
377
+ if (this.suggestionsContainer) {
378
+ try {
379
+ await waitFor(
380
+ () => screen.getByRole("group", {
381
+ name: "Pinned filters",
382
+ hidden: true,
383
+ suggest: false
384
+ })
385
+ );
386
+ const pinnedGroup = within(this.suggestionsContainer).getByRole(
387
+ "group",
388
+ {
389
+ name: "Pinned filters",
390
+ hidden: true
391
+ }
392
+ );
393
+ const suggestions = within(pinnedGroup).getAllByRole(
394
+ "group",
395
+ {
396
+ hidden: true,
397
+ name(_accessibleName, element) {
398
+ return element.getAttribute(
399
+ "data-strato-filter-field-virtual-focus-type"
400
+ ) === "pinned-suggestion";
401
+ }
402
+ }
403
+ );
404
+ return suggestions.map(
405
+ (el) => new FilterFieldPinnedSuggestion(el, this._deps)
406
+ );
407
+ } catch {
408
+ return [];
409
+ }
410
+ }
411
+ return [];
412
+ }
413
+ /** Paste text at the current cursor position */
414
+ async paste(text, cursorIndex) {
415
+ await this.moveCursorTo(cursorIndex ?? -1);
416
+ await this._deps.setupUserEvent({
417
+ pointerEventsCheck: PointerEventsCheckLevel.Never
418
+ }).paste(text);
419
+ if (this._deps.isFakeTimersEnabled()) {
420
+ await act(async () => {
421
+ this._deps.runOnlyPendingTimers();
422
+ });
423
+ }
424
+ }
425
+ }
426
+ function createFilterFieldHelpers(deps) {
427
+ function getFilterFieldHelper(dataTestId) {
428
+ return new FilterFieldHelper(dataTestId, deps);
429
+ }
430
+ return { getFilterFieldHelper };
431
+ }
432
+ export {
433
+ FilterFieldHelper,
434
+ FilterFieldPinnedAndRecentSuggestionBase,
435
+ FilterFieldPinnedSuggestion,
436
+ FilterFieldRecentSuggestion,
437
+ createFilterFieldHelpers
438
+ };
439
+ //# sourceMappingURL=filter-field.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../src/shared/testing-helpers/filters/filter-field.ts"],
4
+ "sourcesContent": ["import {\n act,\n screen,\n waitFor,\n waitForElementToBeRemoved,\n within,\n} from '@testing-library/react';\nimport {\n type Options,\n PointerEventsCheckLevel,\n UserEvent,\n} from '@testing-library/user-event';\n\n/** @internal */\nexport interface _FilterFieldDeps {\n isFakeTimersEnabled: () => boolean;\n runOnlyPendingTimers: () => void;\n setupUserEvent: (options?: Options) => UserEvent;\n}\n\ninterface CmContentElement extends Element {\n cmTile?: {\n view: {\n dispatch: (spec: { selection: { anchor: number } }) => void;\n state: { doc: { length: number } };\n };\n };\n}\n\n/**\n * Base class for pinned or recent filter field suggestion.\n * @public */\nexport class FilterFieldPinnedAndRecentSuggestionBase {\n private readonly _deps: _FilterFieldDeps;\n\n /** @internal */\n constructor(readonly suggestionRoot: HTMLDivElement, deps: _FilterFieldDeps) {\n this._deps = deps;\n }\n\n get pinButtonElement(): HTMLButtonElement | null {\n return this.suggestionRoot.querySelector<HTMLButtonElement>(\n 'button[aria-checked]',\n );\n }\n\n /**\n * Receives the value of the suggestion that would be applied.\n */\n get value(): string | null {\n return this.suggestionRoot.childNodes.item(1).textContent;\n }\n\n /**\n * Returns if the current suggestion is currently pinned or not.\n */\n get pinned(): boolean {\n if (this.pinButtonElement) {\n return this.pinButtonElement.getAttribute('aria-checked') === 'true';\n }\n return false;\n }\n\n /**\n * Applies the suggestion to the filter field.\n */\n async apply() {\n await this._deps\n .setupUserEvent({\n pointerEventsCheck: PointerEventsCheckLevel.Never,\n })\n .click(this.suggestionRoot);\n }\n\n get pinEnabled(): boolean {\n if (this.pinButtonElement) {\n return this.pinButtonElement.getAttribute('aria-disabled') === 'false';\n }\n return false;\n }\n\n /**\n * Allows the suggestion to be pinned if not pinned already.\n */\n async pin() {\n if (this.pinned === true) {\n return;\n }\n if (this.pinButtonElement) {\n await this._deps\n .setupUserEvent({\n pointerEventsCheck: PointerEventsCheckLevel.Never,\n })\n .click(this.pinButtonElement);\n\n if (this._deps.isFakeTimersEnabled()) {\n await act(async () => {\n this._deps.runOnlyPendingTimers();\n });\n }\n }\n }\n\n /**\n * Allows the suggestion to be unpinned if not unpinned already.\n */\n async unpin() {\n if (this.pinned === false) {\n return;\n }\n if (this.pinButtonElement) {\n await this._deps\n .setupUserEvent({\n pointerEventsCheck: PointerEventsCheckLevel.Never,\n })\n .click(this.pinButtonElement);\n\n if (this._deps.isFakeTimersEnabled()) {\n await act(async () => {\n this._deps.runOnlyPendingTimers();\n });\n }\n }\n }\n}\n\n/**\n * Testing helper class for pinned suggestions in the filter field\n * @public */\nexport class FilterFieldPinnedSuggestion extends FilterFieldPinnedAndRecentSuggestionBase {\n /**\n * Receives the value of the suggestion that would be applied.\n */\n override get value(): string | null {\n return this.suggestionRoot.childNodes.item(0).textContent;\n }\n}\n\n/**\n * Testing helper class for recent suggestions in the filter field\n * @public */\nexport class FilterFieldRecentSuggestion extends FilterFieldPinnedAndRecentSuggestionBase {\n /**\n * Receives the value of the suggestion that would be applied.\n */\n override get value(): string | null {\n return (\n this.suggestionRoot.querySelector('[role=\"option\"]')?.childNodes.item(0)\n .textContent ?? null\n );\n }\n /**\n * Receives time offset indicating when the filter statement has last been used.\n */\n get lastUsed(): string | null {\n return (\n this.suggestionRoot.querySelector('[role=\"option\"]')?.childNodes.item(1)\n .textContent ?? null\n );\n }\n}\n\n/**\n * Helper class for interacting with the filter field.\n * @public\n */\nexport class FilterFieldHelper {\n private readonly _deps: _FilterFieldDeps;\n\n get filterFieldWrapper(): HTMLElement {\n return screen.getByTestId(this.dataTestId);\n }\n\n get editorView():\n | {\n dispatch: (spec: { selection: { anchor: number } }) => void;\n state: { doc: { length: number } };\n }\n | undefined {\n return (\n this.filterFieldWrapper.querySelector(\n '.cm-content',\n ) as CmContentElement | null\n )?.cmTile?.view;\n }\n\n get inputElement(): HTMLInputElement {\n return within(this.filterFieldWrapper).getAllByRole<HTMLInputElement>(\n 'textbox',\n { hidden: true },\n )[1];\n }\n\n get filterFieldElement(): HTMLDivElement {\n return within(this.filterFieldWrapper).getByRole<HTMLDivElement>(\n 'textbox',\n {\n name: 'Filter field',\n hidden: true,\n },\n );\n }\n\n get placeholderElement(): HTMLSpanElement | null {\n return this.filterFieldWrapper.querySelector('.cm-placeholder');\n }\n\n get suggestionsContainer(): HTMLDivElement | null {\n return screen.queryByTestId(`filterfield-suggestions-${this.dataTestId}`);\n }\n\n /** @internal */\n constructor(readonly dataTestId: string, deps: _FilterFieldDeps) {\n this._deps = deps;\n }\n\n /** Returns the value of the native text input. */\n get value(): string {\n return this.inputElement.value;\n }\n\n /** Returns the value of the filter field. */\n get filterFieldValue(): string {\n return this.filterFieldElement.textContent || '';\n }\n\n /**\n * The placeholder text shown in the filter field.\n * Undefined if the placeholder is currently not rendered.\n */\n get placeholder(): string | undefined {\n return this.placeholderElement?.textContent || undefined;\n }\n\n /** Focus the filter field if not already focused. */\n async focus() {\n if (!this.filterFieldWrapper.contains(document.activeElement)) {\n await act(async () => {\n this.inputElement.focus();\n });\n\n expect(\n this.filterFieldWrapper.contains(document.activeElement),\n ).toBeTruthy();\n\n if (this._deps.isFakeTimersEnabled()) {\n await act(async () => {\n this._deps.runOnlyPendingTimers();\n });\n }\n }\n }\n\n /** Blur the filter field by tabbing out of it. */\n async blur() {\n await act(async () => {\n this.filterFieldElement.blur();\n });\n\n if (this._deps.isFakeTimersEnabled()) {\n await act(async () => {\n this._deps.runOnlyPendingTimers();\n });\n }\n }\n\n /** Fill a provided value into the filter field. */\n async type(text: string, cursorIndex?: number) {\n const regEx = new RegExp(text, 'g');\n const matches = this.value.match(regEx);\n\n await this.moveCursorTo(cursorIndex ?? -1);\n\n await this._deps\n .setupUserEvent({\n pointerEventsCheck: PointerEventsCheckLevel.Never,\n })\n .keyboard(text);\n await waitFor(() => {\n expect(this.value.match(regEx) ?? []).toHaveLength(\n (matches?.length ?? 0) + 1,\n );\n });\n\n if (this._deps.isFakeTimersEnabled()) {\n await act(async () => {\n this._deps.runOnlyPendingTimers();\n });\n }\n }\n\n /** Clears the filter field value. */\n async clear() {\n if (this.value !== '') {\n await this._deps\n .setupUserEvent({\n pointerEventsCheck: PointerEventsCheckLevel.Never,\n })\n .click(\n await within(this.filterFieldWrapper).findByRole('button', {\n name: 'Clear filter',\n hidden: true,\n }),\n );\n\n if (this._deps.isFakeTimersEnabled()) {\n await act(async () => {\n this._deps.runOnlyPendingTimers();\n });\n }\n }\n }\n\n /**\n * Moves the cursor to the given index.\n * -1 moves to the end of the input.\n */\n async moveCursorTo(index: number) {\n const user = this._deps.setupUserEvent({\n pointerEventsCheck: PointerEventsCheckLevel.Never,\n });\n\n if (index === -1) {\n await act(async () => {\n await this.focus();\n });\n await user.keyboard('{PageDown}');\n\n return;\n }\n\n const editorView = this.editorView;\n if (!editorView) {\n // eslint-disable-next-line no-console\n console.warn('FilterField editor could not be found. Panic now.');\n return;\n }\n\n await act(async () => {\n await this.focus();\n editorView.dispatch({\n selection: {\n anchor: Math.min(index, editorView.state.doc.length),\n },\n });\n });\n\n if (this._deps.isFakeTimersEnabled()) {\n await act(async () => {\n this._deps.runOnlyPendingTimers();\n });\n }\n }\n\n /** Open the suggestions overlay with the keyboard shortcut. */\n async openSuggestions() {\n await act(async () => {\n await this.focus();\n });\n await this._deps\n .setupUserEvent({\n pointerEventsCheck: PointerEventsCheckLevel.Never,\n })\n .keyboard('{Control>}{Space}{/Control}');\n\n await waitFor(async () =>\n expect(this.suggestionsContainer).toBeInTheDocument(),\n );\n if (this._deps.isFakeTimersEnabled()) {\n await act(async () => {\n this._deps.runOnlyPendingTimers();\n });\n }\n }\n\n /**\n * Close the suggestions overlay using the `Escape` key.\n * If the filter field is focused, the focus will stay in the filter field.\n */\n async closeSuggestions() {\n await act(async () => {\n await this.focus();\n });\n await this._deps\n .setupUserEvent({\n pointerEventsCheck: PointerEventsCheckLevel.Never,\n })\n .keyboard('{Escape}');\n\n if (this.suggestionsContainer !== null) {\n await waitForElementToBeRemoved(() => this.suggestionsContainer);\n }\n }\n\n /** Apply the suggestion with the given text. */\n async applySuggestion(queryString: string) {\n await act(async () => {\n await this.focus();\n });\n\n // Wait for the suggestions overlay to open.\n await waitFor(() => {\n expect(this.suggestionsContainer).toBeInTheDocument();\n });\n\n if (!this.suggestionsContainer) {\n // eslint-disable-next-line no-console\n console.warn(\n 'The filter field suggestions overlay seems to be closed. Make sure to open it before applying a suggestion.',\n );\n return;\n }\n\n const showMore = within(this.suggestionsContainer).queryByRole('option', {\n name: 'Show more',\n });\n\n if (showMore) {\n await this._deps\n .setupUserEvent({\n pointerEventsCheck: PointerEventsCheckLevel.Never,\n })\n .click(showMore);\n }\n\n let suggestions = within(this.suggestionsContainer).queryAllByTestId(\n queryString,\n { exact: true },\n );\n\n if (suggestions.length > 0) {\n suggestions = suggestions.reduce<HTMLElement[]>((acc, container) => {\n const suggestion = within(container).queryByRole('option');\n if (suggestion) {\n acc.push(suggestion);\n }\n\n return acc;\n }, []);\n }\n\n if (suggestions.length === 0) {\n suggestions.push(\n ...within(this.suggestionsContainer).queryAllByText(\n (_, element) =>\n element?.getAttribute('role') === 'option' &&\n // The replace is needed to ignore non-breaking spaces\n element.firstElementChild?.textContent?.replace(/\\s+/g, ' ') ===\n queryString.replace(/\\s+/g, ' '),\n ),\n );\n }\n\n if (suggestions.length === 0) {\n // eslint-disable-next-line no-console\n console.warn(\n `Could not query a suggestion with the given queryString '${queryString}' (trying queryByTestId and queryByText).`,\n );\n return;\n }\n\n if (suggestions.length > 1) {\n // eslint-disable-next-line no-console\n console.warn(\n 'Found multiple suggestions with the given testId. Will apply the first one',\n );\n }\n\n await this._deps\n .setupUserEvent({\n pointerEventsCheck: PointerEventsCheckLevel.Never,\n })\n .click(suggestions[0]);\n }\n\n /** Get recent suggestions */\n async getRecentSuggestions(): Promise<FilterFieldRecentSuggestion[]> {\n if (this.suggestionsContainer) {\n const recentGroup = within(this.suggestionsContainer).getByRole('group', {\n name: 'Recently used filters',\n });\n const suggestions = within(recentGroup).getAllByRole<HTMLDivElement>(\n 'group',\n {\n hidden: true,\n name(_accessibleName: string, element: Element) {\n return (\n element.getAttribute(\n 'data-strato-filter-field-virtual-focus-type',\n ) === 'recent-suggestion'\n );\n },\n },\n );\n return suggestions.map(\n (el) => new FilterFieldRecentSuggestion(el, this._deps),\n );\n }\n return [];\n }\n\n /** Get pinned suggestions */\n async getPinnedSuggestions(): Promise<FilterFieldPinnedSuggestion[]> {\n if (this.suggestionsContainer) {\n try {\n await waitFor(() =>\n screen.getByRole('group', {\n name: 'Pinned filters',\n hidden: true,\n suggest: false,\n }),\n );\n const pinnedGroup = within(this.suggestionsContainer).getByRole(\n 'group',\n {\n name: 'Pinned filters',\n hidden: true,\n },\n );\n const suggestions = within(pinnedGroup).getAllByRole<HTMLDivElement>(\n 'group',\n {\n hidden: true,\n name(_accessibleName: string, element: Element) {\n return (\n element.getAttribute(\n 'data-strato-filter-field-virtual-focus-type',\n ) === 'pinned-suggestion'\n );\n },\n },\n );\n return suggestions.map(\n (el) => new FilterFieldPinnedSuggestion(el, this._deps),\n );\n } catch {\n return [];\n }\n }\n return [];\n }\n\n /** Paste text at the current cursor position */\n async paste(text: string, cursorIndex?: number) {\n await this.moveCursorTo(cursorIndex ?? -1);\n\n await this._deps\n .setupUserEvent({\n pointerEventsCheck: PointerEventsCheckLevel.Never,\n })\n .paste(text);\n\n if (this._deps.isFakeTimersEnabled()) {\n await act(async () => {\n this._deps.runOnlyPendingTimers();\n });\n }\n }\n}\n\nexport function createFilterFieldHelpers(deps: _FilterFieldDeps): {\n getFilterFieldHelper: (dataTestId: string) => FilterFieldHelper;\n} {\n /**\n * Helper function that returns the filter field with its testing utilities.\n * @public\n */\n function getFilterFieldHelper(\n /** The accessible test id used to locate the filter field wrapper. */\n dataTestId: string,\n ): FilterFieldHelper {\n return new FilterFieldHelper(dataTestId, deps);\n }\n\n return { getFilterFieldHelper };\n}\n"],
5
+ "mappings": "AAAA;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP;AAAA,EAEE;AAAA,OAEK;AAqBA,MAAM,yCAAyC;AAAA;AAAA,EAIpD,YAAqB,gBAAgC,MAAwB;AAAxD;AACnB,SAAK,QAAQ;AAAA,EACf;AAAA,EAFqB;AAAA,EAHJ;AAAA,EAOjB,IAAI,mBAA6C;AAC/C,WAAO,KAAK,eAAe;AAAA,MACzB;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,IAAI,QAAuB;AACzB,WAAO,KAAK,eAAe,WAAW,KAAK,CAAC,EAAE;AAAA,EAChD;AAAA;AAAA;AAAA;AAAA,EAKA,IAAI,SAAkB;AACpB,QAAI,KAAK,kBAAkB;AACzB,aAAO,KAAK,iBAAiB,aAAa,cAAc,MAAM;AAAA,IAChE;AACA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,QAAQ;AACZ,UAAM,KAAK,MACR,eAAe;AAAA,MACd,oBAAoB,wBAAwB;AAAA,IAC9C,CAAC,EACA,MAAM,KAAK,cAAc;AAAA,EAC9B;AAAA,EAEA,IAAI,aAAsB;AACxB,QAAI,KAAK,kBAAkB;AACzB,aAAO,KAAK,iBAAiB,aAAa,eAAe,MAAM;AAAA,IACjE;AACA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,MAAM;AACV,QAAI,KAAK,WAAW,MAAM;AACxB;AAAA,IACF;AACA,QAAI,KAAK,kBAAkB;AACzB,YAAM,KAAK,MACR,eAAe;AAAA,QACd,oBAAoB,wBAAwB;AAAA,MAC9C,CAAC,EACA,MAAM,KAAK,gBAAgB;AAE9B,UAAI,KAAK,MAAM,oBAAoB,GAAG;AACpC,cAAM,IAAI,YAAY;AACpB,eAAK,MAAM,qBAAqB;AAAA,QAClC,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,QAAQ;AACZ,QAAI,KAAK,WAAW,OAAO;AACzB;AAAA,IACF;AACA,QAAI,KAAK,kBAAkB;AACzB,YAAM,KAAK,MACR,eAAe;AAAA,QACd,oBAAoB,wBAAwB;AAAA,MAC9C,CAAC,EACA,MAAM,KAAK,gBAAgB;AAE9B,UAAI,KAAK,MAAM,oBAAoB,GAAG;AACpC,cAAM,IAAI,YAAY;AACpB,eAAK,MAAM,qBAAqB;AAAA,QAClC,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF;AACF;AAKO,MAAM,oCAAoC,yCAAyC;AAAA;AAAA;AAAA;AAAA,EAIxF,IAAa,QAAuB;AAClC,WAAO,KAAK,eAAe,WAAW,KAAK,CAAC,EAAE;AAAA,EAChD;AACF;AAKO,MAAM,oCAAoC,yCAAyC;AAAA;AAAA;AAAA;AAAA,EAIxF,IAAa,QAAuB;AAClC,WACE,KAAK,eAAe,cAAc,iBAAiB,GAAG,WAAW,KAAK,CAAC,EACpE,eAAe;AAAA,EAEtB;AAAA;AAAA;AAAA;AAAA,EAIA,IAAI,WAA0B;AAC5B,WACE,KAAK,eAAe,cAAc,iBAAiB,GAAG,WAAW,KAAK,CAAC,EACpE,eAAe;AAAA,EAEtB;AACF;AAMO,MAAM,kBAAkB;AAAA;AAAA,EA8C7B,YAAqB,YAAoB,MAAwB;AAA5C;AACnB,SAAK,QAAQ;AAAA,EACf;AAAA,EAFqB;AAAA,EA7CJ;AAAA,EAEjB,IAAI,qBAAkC;AACpC,WAAO,OAAO,YAAY,KAAK,UAAU;AAAA,EAC3C;AAAA,EAEA,IAAI,aAKU;AACZ,WACE,KAAK,mBAAmB;AAAA,MACtB;AAAA,IACF,GACC,QAAQ;AAAA,EACb;AAAA,EAEA,IAAI,eAAiC;AACnC,WAAO,OAAO,KAAK,kBAAkB,EAAE;AAAA,MACrC;AAAA,MACA,EAAE,QAAQ,KAAK;AAAA,IACjB,EAAE,CAAC;AAAA,EACL;AAAA,EAEA,IAAI,qBAAqC;AACvC,WAAO,OAAO,KAAK,kBAAkB,EAAE;AAAA,MACrC;AAAA,MACA;AAAA,QACE,MAAM;AAAA,QACN,QAAQ;AAAA,MACV;AAAA,IACF;AAAA,EACF;AAAA,EAEA,IAAI,qBAA6C;AAC/C,WAAO,KAAK,mBAAmB,cAAc,iBAAiB;AAAA,EAChE;AAAA,EAEA,IAAI,uBAA8C;AAChD,WAAO,OAAO,cAAc,2BAA2B,KAAK,UAAU,EAAE;AAAA,EAC1E;AAAA;AAAA,EAQA,IAAI,QAAgB;AAClB,WAAO,KAAK,aAAa;AAAA,EAC3B;AAAA;AAAA,EAGA,IAAI,mBAA2B;AAC7B,WAAO,KAAK,mBAAmB,eAAe;AAAA,EAChD;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,IAAI,cAAkC;AACpC,WAAO,KAAK,oBAAoB,eAAe;AAAA,EACjD;AAAA;AAAA,EAGA,MAAM,QAAQ;AACZ,QAAI,CAAC,KAAK,mBAAmB,SAAS,SAAS,aAAa,GAAG;AAC7D,YAAM,IAAI,YAAY;AACpB,aAAK,aAAa,MAAM;AAAA,MAC1B,CAAC;AAED;AAAA,QACE,KAAK,mBAAmB,SAAS,SAAS,aAAa;AAAA,MACzD,EAAE,WAAW;AAEb,UAAI,KAAK,MAAM,oBAAoB,GAAG;AACpC,cAAM,IAAI,YAAY;AACpB,eAAK,MAAM,qBAAqB;AAAA,QAClC,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF;AAAA;AAAA,EAGA,MAAM,OAAO;AACX,UAAM,IAAI,YAAY;AACpB,WAAK,mBAAmB,KAAK;AAAA,IAC/B,CAAC;AAED,QAAI,KAAK,MAAM,oBAAoB,GAAG;AACpC,YAAM,IAAI,YAAY;AACpB,aAAK,MAAM,qBAAqB;AAAA,MAClC,CAAC;AAAA,IACH;AAAA,EACF;AAAA;AAAA,EAGA,MAAM,KAAK,MAAc,aAAsB;AAC7C,UAAM,QAAQ,IAAI,OAAO,MAAM,GAAG;AAClC,UAAM,UAAU,KAAK,MAAM,MAAM,KAAK;AAEtC,UAAM,KAAK,aAAa,eAAe,EAAE;AAEzC,UAAM,KAAK,MACR,eAAe;AAAA,MACd,oBAAoB,wBAAwB;AAAA,IAC9C,CAAC,EACA,SAAS,IAAI;AAChB,UAAM,QAAQ,MAAM;AAClB,aAAO,KAAK,MAAM,MAAM,KAAK,KAAK,CAAC,CAAC,EAAE;AAAA,SACnC,SAAS,UAAU,KAAK;AAAA,MAC3B;AAAA,IACF,CAAC;AAED,QAAI,KAAK,MAAM,oBAAoB,GAAG;AACpC,YAAM,IAAI,YAAY;AACpB,aAAK,MAAM,qBAAqB;AAAA,MAClC,CAAC;AAAA,IACH;AAAA,EACF;AAAA;AAAA,EAGA,MAAM,QAAQ;AACZ,QAAI,KAAK,UAAU,IAAI;AACrB,YAAM,KAAK,MACR,eAAe;AAAA,QACd,oBAAoB,wBAAwB;AAAA,MAC9C,CAAC,EACA;AAAA,QACC,MAAM,OAAO,KAAK,kBAAkB,EAAE,WAAW,UAAU;AAAA,UACzD,MAAM;AAAA,UACN,QAAQ;AAAA,QACV,CAAC;AAAA,MACH;AAEF,UAAI,KAAK,MAAM,oBAAoB,GAAG;AACpC,cAAM,IAAI,YAAY;AACpB,eAAK,MAAM,qBAAqB;AAAA,QAClC,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,aAAa,OAAe;AAChC,UAAM,OAAO,KAAK,MAAM,eAAe;AAAA,MACrC,oBAAoB,wBAAwB;AAAA,IAC9C,CAAC;AAED,QAAI,UAAU,IAAI;AAChB,YAAM,IAAI,YAAY;AACpB,cAAM,KAAK,MAAM;AAAA,MACnB,CAAC;AACD,YAAM,KAAK,SAAS,YAAY;AAEhC;AAAA,IACF;AAEA,UAAM,aAAa,KAAK;AACxB,QAAI,CAAC,YAAY;AAEf,cAAQ,KAAK,mDAAmD;AAChE;AAAA,IACF;AAEA,UAAM,IAAI,YAAY;AACpB,YAAM,KAAK,MAAM;AACjB,iBAAW,SAAS;AAAA,QAClB,WAAW;AAAA,UACT,QAAQ,KAAK,IAAI,OAAO,WAAW,MAAM,IAAI,MAAM;AAAA,QACrD;AAAA,MACF,CAAC;AAAA,IACH,CAAC;AAED,QAAI,KAAK,MAAM,oBAAoB,GAAG;AACpC,YAAM,IAAI,YAAY;AACpB,aAAK,MAAM,qBAAqB;AAAA,MAClC,CAAC;AAAA,IACH;AAAA,EACF;AAAA;AAAA,EAGA,MAAM,kBAAkB;AACtB,UAAM,IAAI,YAAY;AACpB,YAAM,KAAK,MAAM;AAAA,IACnB,CAAC;AACD,UAAM,KAAK,MACR,eAAe;AAAA,MACd,oBAAoB,wBAAwB;AAAA,IAC9C,CAAC,EACA,SAAS,6BAA6B;AAEzC,UAAM;AAAA,MAAQ,YACZ,OAAO,KAAK,oBAAoB,EAAE,kBAAkB;AAAA,IACtD;AACA,QAAI,KAAK,MAAM,oBAAoB,GAAG;AACpC,YAAM,IAAI,YAAY;AACpB,aAAK,MAAM,qBAAqB;AAAA,MAClC,CAAC;AAAA,IACH;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,mBAAmB;AACvB,UAAM,IAAI,YAAY;AACpB,YAAM,KAAK,MAAM;AAAA,IACnB,CAAC;AACD,UAAM,KAAK,MACR,eAAe;AAAA,MACd,oBAAoB,wBAAwB;AAAA,IAC9C,CAAC,EACA,SAAS,UAAU;AAEtB,QAAI,KAAK,yBAAyB,MAAM;AACtC,YAAM,0BAA0B,MAAM,KAAK,oBAAoB;AAAA,IACjE;AAAA,EACF;AAAA;AAAA,EAGA,MAAM,gBAAgB,aAAqB;AACzC,UAAM,IAAI,YAAY;AACpB,YAAM,KAAK,MAAM;AAAA,IACnB,CAAC;AAGD,UAAM,QAAQ,MAAM;AAClB,aAAO,KAAK,oBAAoB,EAAE,kBAAkB;AAAA,IACtD,CAAC;AAED,QAAI,CAAC,KAAK,sBAAsB;AAE9B,cAAQ;AAAA,QACN;AAAA,MACF;AACA;AAAA,IACF;AAEA,UAAM,WAAW,OAAO,KAAK,oBAAoB,EAAE,YAAY,UAAU;AAAA,MACvE,MAAM;AAAA,IACR,CAAC;AAED,QAAI,UAAU;AACZ,YAAM,KAAK,MACR,eAAe;AAAA,QACd,oBAAoB,wBAAwB;AAAA,MAC9C,CAAC,EACA,MAAM,QAAQ;AAAA,IACnB;AAEA,QAAI,cAAc,OAAO,KAAK,oBAAoB,EAAE;AAAA,MAClD;AAAA,MACA,EAAE,OAAO,KAAK;AAAA,IAChB;AAEA,QAAI,YAAY,SAAS,GAAG;AAC1B,oBAAc,YAAY,OAAsB,CAAC,KAAK,cAAc;AAClE,cAAM,aAAa,OAAO,SAAS,EAAE,YAAY,QAAQ;AACzD,YAAI,YAAY;AACd,cAAI,KAAK,UAAU;AAAA,QACrB;AAEA,eAAO;AAAA,MACT,GAAG,CAAC,CAAC;AAAA,IACP;AAEA,QAAI,YAAY,WAAW,GAAG;AAC5B,kBAAY;AAAA,QACV,GAAG,OAAO,KAAK,oBAAoB,EAAE;AAAA,UACnC,CAAC,GAAG,YACF,SAAS,aAAa,MAAM,MAAM;AAAA,UAElC,QAAQ,mBAAmB,aAAa,QAAQ,QAAQ,GAAG,MACzD,YAAY,QAAQ,QAAQ,GAAG;AAAA,QACrC;AAAA,MACF;AAAA,IACF;AAEA,QAAI,YAAY,WAAW,GAAG;AAE5B,cAAQ;AAAA,QACN,4DAA4D,WAAW;AAAA,MACzE;AACA;AAAA,IACF;AAEA,QAAI,YAAY,SAAS,GAAG;AAE1B,cAAQ;AAAA,QACN;AAAA,MACF;AAAA,IACF;AAEA,UAAM,KAAK,MACR,eAAe;AAAA,MACd,oBAAoB,wBAAwB;AAAA,IAC9C,CAAC,EACA,MAAM,YAAY,CAAC,CAAC;AAAA,EACzB;AAAA;AAAA,EAGA,MAAM,uBAA+D;AACnE,QAAI,KAAK,sBAAsB;AAC7B,YAAM,cAAc,OAAO,KAAK,oBAAoB,EAAE,UAAU,SAAS;AAAA,QACvE,MAAM;AAAA,MACR,CAAC;AACD,YAAM,cAAc,OAAO,WAAW,EAAE;AAAA,QACtC;AAAA,QACA;AAAA,UACE,QAAQ;AAAA,UACR,KAAK,iBAAyB,SAAkB;AAC9C,mBACE,QAAQ;AAAA,cACN;AAAA,YACF,MAAM;AAAA,UAEV;AAAA,QACF;AAAA,MACF;AACA,aAAO,YAAY;AAAA,QACjB,CAAC,OAAO,IAAI,4BAA4B,IAAI,KAAK,KAAK;AAAA,MACxD;AAAA,IACF;AACA,WAAO,CAAC;AAAA,EACV;AAAA;AAAA,EAGA,MAAM,uBAA+D;AACnE,QAAI,KAAK,sBAAsB;AAC7B,UAAI;AACF,cAAM;AAAA,UAAQ,MACZ,OAAO,UAAU,SAAS;AAAA,YACxB,MAAM;AAAA,YACN,QAAQ;AAAA,YACR,SAAS;AAAA,UACX,CAAC;AAAA,QACH;AACA,cAAM,cAAc,OAAO,KAAK,oBAAoB,EAAE;AAAA,UACpD;AAAA,UACA;AAAA,YACE,MAAM;AAAA,YACN,QAAQ;AAAA,UACV;AAAA,QACF;AACA,cAAM,cAAc,OAAO,WAAW,EAAE;AAAA,UACtC;AAAA,UACA;AAAA,YACE,QAAQ;AAAA,YACR,KAAK,iBAAyB,SAAkB;AAC9C,qBACE,QAAQ;AAAA,gBACN;AAAA,cACF,MAAM;AAAA,YAEV;AAAA,UACF;AAAA,QACF;AACA,eAAO,YAAY;AAAA,UACjB,CAAC,OAAO,IAAI,4BAA4B,IAAI,KAAK,KAAK;AAAA,QACxD;AAAA,MACF,QAAQ;AACN,eAAO,CAAC;AAAA,MACV;AAAA,IACF;AACA,WAAO,CAAC;AAAA,EACV;AAAA;AAAA,EAGA,MAAM,MAAM,MAAc,aAAsB;AAC9C,UAAM,KAAK,aAAa,eAAe,EAAE;AAEzC,UAAM,KAAK,MACR,eAAe;AAAA,MACd,oBAAoB,wBAAwB;AAAA,IAC9C,CAAC,EACA,MAAM,IAAI;AAEb,QAAI,KAAK,MAAM,oBAAoB,GAAG;AACpC,YAAM,IAAI,YAAY;AACpB,aAAK,MAAM,qBAAqB;AAAA,MAClC,CAAC;AAAA,IACH;AAAA,EACF;AACF;AAEO,SAAS,yBAAyB,MAEvC;AAKA,WAAS,qBAEP,YACmB;AACnB,WAAO,IAAI,kBAAkB,YAAY,IAAI;AAAA,EAC/C;AAEA,SAAO,EAAE,qBAAqB;AAChC;",
6
+ "names": []
7
+ }
@@ -0,0 +1,149 @@
1
+ import "@testing-library/jest-dom/vitest";
2
+ import {
3
+ customRender
4
+ } from "../jest/custom-render.js";
5
+ import { stratoVitestPreset } from "./preset/index.js";
6
+ import {
7
+ getTextInputHelper
8
+ } from "../jest/testing-helpers/forms/text-input.js";
9
+ import {
10
+ getPasswordInputHelper
11
+ } from "../jest/testing-helpers/forms/password-input.js";
12
+ import {
13
+ getSearchInputHelper
14
+ } from "../jest/testing-helpers/forms/search-input.js";
15
+ import {
16
+ getDataTableHelper
17
+ } from "../jest/testing-helpers/tables/datatable.js";
18
+ import {
19
+ getFilterFieldHelper,
20
+ FilterFieldHelper,
21
+ FilterFieldPinnedAndRecentSuggestionBase,
22
+ FilterFieldPinnedSuggestion,
23
+ FilterFieldRecentSuggestion
24
+ } from "./testing-helpers/filters/filter-field.js";
25
+ import {
26
+ getSelectTestingHelper
27
+ } from "../jest/testing-helpers/forms/select.js";
28
+ import {
29
+ setupGetBoundingClientRectMock,
30
+ clearGetBoundingClientRectMock
31
+ } from "../jest/mocks/bounding-client-rect-mock.js";
32
+ import { createMockElement } from "../jest/mocks/create-mock-element.js";
33
+ import {
34
+ setupCanvasMeasureTextMock,
35
+ clearCanvasMeasureTextMock
36
+ } from "../jest/mocks/canvas-mock.js";
37
+ import {
38
+ setupOffsetWidthMock,
39
+ clearOffsetWidthMock
40
+ } from "../jest/mocks/offset-width-mock.js";
41
+ import {
42
+ setupOffsetHeightMock,
43
+ clearOffsetHeightMock
44
+ } from "../jest/mocks/offset-height-mock.js";
45
+ import {
46
+ setupDomRectMock,
47
+ clearDomRectMock
48
+ } from "../jest/mocks/dom-rect-mock.js";
49
+ import {
50
+ setupPointerEventMock,
51
+ clearPointerEventMock
52
+ } from "../jest/mocks/pointer-event-mock.js";
53
+ import {
54
+ setupScreenSizeMock,
55
+ clearScreenSizeMock
56
+ } from "../jest/mocks/screen-size-mock.js";
57
+ import {
58
+ setupScrollWidthMock,
59
+ clearScrollWidthMock
60
+ } from "../jest/mocks/scroll-width-mock.js";
61
+ import {
62
+ setupVirtualizationMock,
63
+ clearVirtualizationMock
64
+ } from "../jest/mocks/virtualization-mock.js";
65
+ import {
66
+ setupTextEllipsisMock,
67
+ clearTextEllipsisMock
68
+ } from "../jest/mocks/text-ellipsis-mock.js";
69
+ import {
70
+ setupElementFromPointMock,
71
+ clearElementFromPointMock
72
+ } from "../jest/mocks/element-from-point-mock.js";
73
+ import {
74
+ setupResizeObserverMock,
75
+ clearResizeObserverMock
76
+ } from "./mocks/resize-observer-mock.js";
77
+ import {
78
+ setupMatchMediaMock,
79
+ clearMatchMediaMock
80
+ } from "./mocks/match-media-mock.js";
81
+ import {
82
+ setupIntersectionObserverMock,
83
+ clearIntersectionObserverMock
84
+ } from "./mocks/intersection-observer-mock.js";
85
+ import {
86
+ setupScrollIntoViewMock,
87
+ clearScrollIntoViewMock
88
+ } from "./mocks/scroll-into-view-mock.js";
89
+ import {
90
+ setupCreateRangeMock,
91
+ clearCreateRangeMock
92
+ } from "./mocks/create-range-mock.js";
93
+ import { setupFetchMock, clearFetchMock } from "./mocks/fetch-mock.js";
94
+ import {
95
+ setupFramerMotionMock,
96
+ clearFramerMotionMock
97
+ } from "./mocks/framer-motion-mock.js";
98
+ export {
99
+ FilterFieldHelper,
100
+ FilterFieldPinnedAndRecentSuggestionBase,
101
+ FilterFieldPinnedSuggestion,
102
+ FilterFieldRecentSuggestion,
103
+ clearCanvasMeasureTextMock,
104
+ clearCreateRangeMock,
105
+ clearDomRectMock,
106
+ clearElementFromPointMock,
107
+ clearFetchMock,
108
+ clearFramerMotionMock,
109
+ clearGetBoundingClientRectMock,
110
+ clearIntersectionObserverMock,
111
+ clearMatchMediaMock,
112
+ clearOffsetHeightMock,
113
+ clearOffsetWidthMock,
114
+ clearPointerEventMock,
115
+ clearResizeObserverMock,
116
+ clearScreenSizeMock,
117
+ clearScrollIntoViewMock,
118
+ clearScrollWidthMock,
119
+ clearTextEllipsisMock,
120
+ clearVirtualizationMock,
121
+ createMockElement,
122
+ getDataTableHelper,
123
+ getFilterFieldHelper,
124
+ getPasswordInputHelper,
125
+ getSearchInputHelper,
126
+ getSelectTestingHelper,
127
+ getTextInputHelper,
128
+ customRender as render,
129
+ setupCanvasMeasureTextMock,
130
+ setupCreateRangeMock,
131
+ setupDomRectMock,
132
+ setupElementFromPointMock,
133
+ setupFetchMock,
134
+ setupFramerMotionMock,
135
+ setupGetBoundingClientRectMock,
136
+ setupIntersectionObserverMock,
137
+ setupMatchMediaMock,
138
+ setupOffsetHeightMock,
139
+ setupOffsetWidthMock,
140
+ setupPointerEventMock,
141
+ setupResizeObserverMock,
142
+ setupScreenSizeMock,
143
+ setupScrollIntoViewMock,
144
+ setupScrollWidthMock,
145
+ setupTextEllipsisMock,
146
+ setupVirtualizationMock,
147
+ stratoVitestPreset
148
+ };
149
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/vitest/index.ts"],
4
+ "sourcesContent": ["import '@testing-library/jest-dom/vitest';\n\nexport {\n customRender as render,\n type RenderWithIntlOptions,\n} from '../jest/custom-render.js';\n\nexport { stratoVitestPreset } from './preset/index.js';\n\n///#region Testing Helpers\nexport {\n getTextInputHelper,\n type TextInputHelper,\n} from '../jest/testing-helpers/forms/text-input.js';\nexport {\n getPasswordInputHelper,\n type PasswordInputHelper,\n} from '../jest/testing-helpers/forms/password-input.js';\nexport {\n getSearchInputHelper,\n type SearchInputHelper,\n} from '../jest/testing-helpers/forms/search-input.js';\n\nexport {\n getDataTableHelper,\n type DataTableHelper,\n type DataTablePaginationHelper,\n type DataTableUserActionHelper,\n type DataTableSortingHelper,\n type DataTableRowSelectionHelper,\n type DataTableSubRowsHelper,\n type DataTableInteractiveRowsHelper,\n type DataTableDownloadHelper,\n type DataTableExpandableRowHelper,\n} from '../jest/testing-helpers/tables/datatable.js';\n\nexport type { BaseInputHelper } from '../jest/testing-helpers/forms/base-input.js';\n\nexport {\n getFilterFieldHelper,\n FilterFieldHelper,\n FilterFieldPinnedAndRecentSuggestionBase,\n FilterFieldPinnedSuggestion,\n FilterFieldRecentSuggestion,\n} from './testing-helpers/filters/filter-field.js';\nexport type { _FilterFieldDeps } from './testing-helpers/filters/filter-field.js';\n\nexport {\n getSelectTestingHelper,\n type SelectTestingHelper,\n} from '../jest/testing-helpers/forms/select.js';\n///#endregion Testing Helpers\n\n///#region Mocks \u2014 portable (re-exported from jest)\nexport {\n setupGetBoundingClientRectMock,\n clearGetBoundingClientRectMock,\n} from '../jest/mocks/bounding-client-rect-mock.js';\n\nexport { createMockElement } from '../jest/mocks/create-mock-element.js';\n\nexport {\n setupCanvasMeasureTextMock,\n clearCanvasMeasureTextMock,\n} from '../jest/mocks/canvas-mock.js';\n\nexport {\n setupOffsetWidthMock,\n clearOffsetWidthMock,\n} from '../jest/mocks/offset-width-mock.js';\n\nexport {\n setupOffsetHeightMock,\n clearOffsetHeightMock,\n} from '../jest/mocks/offset-height-mock.js';\n\nexport {\n setupDomRectMock,\n clearDomRectMock,\n} from '../jest/mocks/dom-rect-mock.js';\n\nexport {\n setupPointerEventMock,\n clearPointerEventMock,\n} from '../jest/mocks/pointer-event-mock.js';\n\nexport {\n setupScreenSizeMock,\n clearScreenSizeMock,\n} from '../jest/mocks/screen-size-mock.js';\n\nexport {\n setupScrollWidthMock,\n clearScrollWidthMock,\n} from '../jest/mocks/scroll-width-mock.js';\n\nexport {\n setupVirtualizationMock,\n clearVirtualizationMock,\n} from '../jest/mocks/virtualization-mock.js';\n\nexport {\n setupTextEllipsisMock,\n clearTextEllipsisMock,\n} from '../jest/mocks/text-ellipsis-mock.js';\n\nexport {\n setupElementFromPointMock,\n clearElementFromPointMock,\n} from '../jest/mocks/element-from-point-mock.js';\n///#endregion Mocks \u2014 portable\n\n///#region Mocks \u2014 vitest-specific\nexport {\n setupResizeObserverMock,\n clearResizeObserverMock,\n} from './mocks/resize-observer-mock.js';\n\nexport {\n setupMatchMediaMock,\n clearMatchMediaMock,\n} from './mocks/match-media-mock.js';\n\nexport {\n setupIntersectionObserverMock,\n clearIntersectionObserverMock,\n} from './mocks/intersection-observer-mock.js';\n\nexport {\n setupScrollIntoViewMock,\n clearScrollIntoViewMock,\n} from './mocks/scroll-into-view-mock.js';\n\nexport {\n setupCreateRangeMock,\n clearCreateRangeMock,\n} from './mocks/create-range-mock.js';\n\nexport { setupFetchMock, clearFetchMock } from './mocks/fetch-mock.js';\nexport type { MockedResponse } from '../shared/mocks/fetch-mock.js';\n\nexport {\n setupFramerMotionMock,\n clearFramerMotionMock,\n type FramerMotionActual,\n} from './mocks/framer-motion-mock.js';\n///#endregion Mocks \u2014 vitest-specific\n"],
5
+ "mappings": "AAAA,OAAO;AAEP;AAAA,EACkB;AAAA,OAEX;AAEP,SAAS,0BAA0B;AAGnC;AAAA,EACE;AAAA,OAEK;AACP;AAAA,EACE;AAAA,OAEK;AACP;AAAA,EACE;AAAA,OAEK;AAEP;AAAA,EACE;AAAA,OAUK;AAIP;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAGP;AAAA,EACE;AAAA,OAEK;AAIP;AAAA,EACE;AAAA,EACA;AAAA,OACK;AAEP,SAAS,yBAAyB;AAElC;AAAA,EACE;AAAA,EACA;AAAA,OACK;AAEP;AAAA,EACE;AAAA,EACA;AAAA,OACK;AAEP;AAAA,EACE;AAAA,EACA;AAAA,OACK;AAEP;AAAA,EACE;AAAA,EACA;AAAA,OACK;AAEP;AAAA,EACE;AAAA,EACA;AAAA,OACK;AAEP;AAAA,EACE;AAAA,EACA;AAAA,OACK;AAEP;AAAA,EACE;AAAA,EACA;AAAA,OACK;AAEP;AAAA,EACE;AAAA,EACA;AAAA,OACK;AAEP;AAAA,EACE;AAAA,EACA;AAAA,OACK;AAEP;AAAA,EACE;AAAA,EACA;AAAA,OACK;AAIP;AAAA,EACE;AAAA,EACA;AAAA,OACK;AAEP;AAAA,EACE;AAAA,EACA;AAAA,OACK;AAEP;AAAA,EACE;AAAA,EACA;AAAA,OACK;AAEP;AAAA,EACE;AAAA,EACA;AAAA,OACK;AAEP;AAAA,EACE;AAAA,EACA;AAAA,OACK;AAEP,SAAS,gBAAgB,sBAAsB;AAG/C;AAAA,EACE;AAAA,EACA;AAAA,OAEK;",
6
+ "names": []
7
+ }
@@ -0,0 +1,10 @@
1
+ import { vi } from "vitest";
2
+ import { createCreateRangeMockHelpers } from "../../shared/mocks/create-range-mock.js";
3
+ const _helpers = createCreateRangeMockHelpers(vi.fn);
4
+ const setupCreateRangeMock = _helpers.setupCreateRangeMock;
5
+ const clearCreateRangeMock = _helpers.clearCreateRangeMock;
6
+ export {
7
+ clearCreateRangeMock,
8
+ setupCreateRangeMock
9
+ };
10
+ //# sourceMappingURL=create-range-mock.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../src/vitest/mocks/create-range-mock.ts"],
4
+ "sourcesContent": ["// eslint-disable-next-line no-restricted-imports\nimport { vi } from 'vitest';\n\nimport { createCreateRangeMockHelpers } from '../../shared/mocks/create-range-mock.js';\n\nconst _helpers = createCreateRangeMockHelpers(vi.fn);\n\n/** @public */\nexport const setupCreateRangeMock = _helpers.setupCreateRangeMock;\n\n/** @public */\nexport const clearCreateRangeMock = _helpers.clearCreateRangeMock;\n"],
5
+ "mappings": "AACA,SAAS,UAAU;AAEnB,SAAS,oCAAoC;AAE7C,MAAM,WAAW,6BAA6B,GAAG,EAAE;AAG5C,MAAM,uBAAuB,SAAS;AAGtC,MAAM,uBAAuB,SAAS;",
6
+ "names": []
7
+ }