@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.
- package/esm/jest/index.js +9 -1
- package/esm/jest/index.js.map +2 -2
- package/esm/jest/mocks/create-range-mock.js +4 -22
- package/esm/jest/mocks/create-range-mock.js.map +2 -2
- package/esm/jest/mocks/fetch-mock.js +4 -14
- package/esm/jest/mocks/fetch-mock.js.map +2 -2
- package/esm/jest/mocks/framer-motion-mock.js +3 -48
- package/esm/jest/mocks/framer-motion-mock.js.map +3 -3
- package/esm/jest/mocks/intersection-observer-mock.js +4 -35
- package/esm/jest/mocks/intersection-observer-mock.js.map +2 -2
- package/esm/jest/mocks/match-media-mock.js +4 -21
- package/esm/jest/mocks/match-media-mock.js.map +2 -2
- package/esm/jest/mocks/resize-observer-mock.js +4 -18
- package/esm/jest/mocks/resize-observer-mock.js.map +2 -2
- package/esm/jest/mocks/scroll-into-view-mock.js +4 -10
- package/esm/jest/mocks/scroll-into-view-mock.js.map +2 -2
- package/esm/jest/testing-helpers/filters/filter-field.js +12 -415
- package/esm/jest/testing-helpers/filters/filter-field.js.map +2 -2
- package/esm/jest/testing-helpers/tables/datatable.js +1 -11
- package/esm/jest/testing-helpers/tables/datatable.js.map +2 -2
- package/esm/jest/testing-helpers/utils/setup-user-event.js +1 -1
- package/esm/jest/testing-helpers/utils/setup-user-event.js.map +2 -2
- package/esm/shared/mocks/create-range-mock.js +29 -0
- package/esm/shared/mocks/create-range-mock.js.map +7 -0
- package/esm/shared/mocks/fetch-mock.js +23 -0
- package/esm/shared/mocks/fetch-mock.js.map +7 -0
- package/esm/shared/mocks/framer-motion-mock.js +54 -0
- package/esm/shared/mocks/framer-motion-mock.js.map +7 -0
- package/esm/shared/mocks/intersection-observer-mock.js +39 -0
- package/esm/shared/mocks/intersection-observer-mock.js.map +7 -0
- package/esm/shared/mocks/match-media-mock.js +28 -0
- package/esm/shared/mocks/match-media-mock.js.map +7 -0
- package/esm/shared/mocks/resize-observer-mock.js +25 -0
- package/esm/shared/mocks/resize-observer-mock.js.map +7 -0
- package/esm/shared/mocks/scroll-into-view-mock.js +17 -0
- package/esm/shared/mocks/scroll-into-view-mock.js.map +7 -0
- package/esm/shared/testing-helpers/filters/filter-field.js +439 -0
- package/esm/shared/testing-helpers/filters/filter-field.js.map +7 -0
- package/esm/vitest/index.js +149 -0
- package/esm/vitest/index.js.map +7 -0
- package/esm/vitest/mocks/create-range-mock.js +10 -0
- package/esm/vitest/mocks/create-range-mock.js.map +7 -0
- package/esm/vitest/mocks/fetch-mock.js +10 -0
- package/esm/vitest/mocks/fetch-mock.js.map +7 -0
- package/esm/vitest/mocks/framer-motion-mock.js +13 -0
- package/esm/vitest/mocks/framer-motion-mock.js.map +7 -0
- package/esm/vitest/mocks/intersection-observer-mock.js +10 -0
- package/esm/vitest/mocks/intersection-observer-mock.js.map +7 -0
- package/esm/vitest/mocks/match-media-mock.js +10 -0
- package/esm/vitest/mocks/match-media-mock.js.map +7 -0
- package/esm/vitest/mocks/resize-observer-mock.js +10 -0
- package/esm/vitest/mocks/resize-observer-mock.js.map +7 -0
- package/esm/vitest/mocks/scroll-into-view-mock.js +10 -0
- package/esm/vitest/mocks/scroll-into-view-mock.js.map +7 -0
- package/esm/vitest/preset/index.js +5 -0
- package/esm/vitest/preset/index.js.map +7 -0
- package/esm/vitest/preset/vitest-preset.js +9 -0
- package/esm/vitest/preset/vitest-preset.js.map +7 -0
- package/esm/vitest/setup/index.js +77 -0
- package/esm/vitest/setup/index.js.map +7 -0
- package/esm/vitest/testing-helpers/filters/filter-field.js +24 -0
- package/esm/vitest/testing-helpers/filters/filter-field.js.map +7 -0
- package/esm/vitest/testing-helpers/utils/isFakeTimersEnabled.js +8 -0
- package/esm/vitest/testing-helpers/utils/isFakeTimersEnabled.js.map +7 -0
- package/esm/vitest/testing-helpers/utils/setup-user-event.js +16 -0
- package/esm/vitest/testing-helpers/utils/setup-user-event.js.map +7 -0
- package/jest/index.d.ts +3 -1
- package/jest/index.js +4 -0
- package/jest/mocks/create-range-mock.d.ts +4 -11
- package/jest/mocks/create-range-mock.js +4 -22
- package/jest/mocks/fetch-mock.d.ts +4 -10
- package/jest/mocks/fetch-mock.js +4 -14
- package/jest/mocks/framer-motion-mock.js +3 -46
- package/jest/mocks/intersection-observer-mock.d.ts +4 -11
- package/jest/mocks/intersection-observer-mock.js +4 -35
- package/jest/mocks/match-media-mock.d.ts +4 -11
- package/jest/mocks/match-media-mock.js +4 -21
- package/jest/mocks/resize-observer-mock.d.ts +4 -13
- package/jest/mocks/resize-observer-mock.js +4 -18
- package/jest/mocks/scroll-into-view-mock.d.ts +4 -10
- package/jest/mocks/scroll-into-view-mock.js +4 -10
- package/jest/testing-helpers/filters/filter-field.d.ts +5 -109
- package/jest/testing-helpers/filters/filter-field.js +11 -414
- package/jest/testing-helpers/tables/datatable.js +1 -11
- package/jest/testing-helpers/utils/setup-user-event.js +1 -1
- package/package.json +3 -3
- package/shared/mocks/create-range-mock.d.ts +7 -0
- package/shared/mocks/create-range-mock.js +47 -0
- package/shared/mocks/fetch-mock.d.ts +13 -0
- package/shared/mocks/fetch-mock.js +41 -0
- package/shared/mocks/framer-motion-mock.d.ts +15 -0
- package/shared/mocks/framer-motion-mock.js +70 -0
- package/shared/mocks/intersection-observer-mock.d.ts +7 -0
- package/shared/mocks/intersection-observer-mock.js +57 -0
- package/shared/mocks/match-media-mock.d.ts +7 -0
- package/shared/mocks/match-media-mock.js +46 -0
- package/shared/mocks/resize-observer-mock.d.ts +8 -0
- package/shared/mocks/resize-observer-mock.js +43 -0
- package/shared/mocks/scroll-into-view-mock.d.ts +7 -0
- package/shared/mocks/scroll-into-view-mock.js +35 -0
- package/shared/testing-helpers/filters/filter-field.d.ts +128 -0
- package/shared/testing-helpers/filters/filter-field.js +449 -0
- package/vitest/index.d.ts +32 -0
- package/vitest/index.js +98 -0
- package/vitest/mocks/create-range-mock.d.ts +5 -0
- package/vitest/mocks/create-range-mock.js +28 -0
- package/vitest/mocks/fetch-mock.d.ts +5 -0
- package/vitest/mocks/fetch-mock.js +28 -0
- package/vitest/mocks/framer-motion-mock.d.ts +27 -0
- package/vitest/mocks/framer-motion-mock.js +31 -0
- package/vitest/mocks/intersection-observer-mock.d.ts +5 -0
- package/vitest/mocks/intersection-observer-mock.js +28 -0
- package/vitest/mocks/match-media-mock.d.ts +5 -0
- package/vitest/mocks/match-media-mock.js +28 -0
- package/vitest/mocks/resize-observer-mock.d.ts +5 -0
- package/vitest/mocks/resize-observer-mock.js +28 -0
- package/vitest/mocks/scroll-into-view-mock.d.ts +5 -0
- package/vitest/mocks/scroll-into-view-mock.js +28 -0
- package/vitest/preset/index.d.ts +2 -0
- package/vitest/preset/index.js +23 -0
- package/vitest/preset/vitest-preset.d.ts +24 -0
- package/vitest/preset/vitest-preset.js +27 -0
- package/vitest/setup/index.d.ts +2 -0
- package/vitest/setup/index.js +40 -0
- package/vitest/testing-helpers/filters/filter-field.d.ts +6 -0
- package/vitest/testing-helpers/filters/filter-field.js +36 -0
- package/vitest/testing-helpers/utils/isFakeTimersEnabled.d.ts +6 -0
- package/vitest/testing-helpers/utils/isFakeTimersEnabled.js +26 -0
- package/vitest/testing-helpers/utils/setup-user-event.d.ts +7 -0
- package/vitest/testing-helpers/utils/setup-user-event.js +44 -0
|
@@ -1,421 +1,18 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
} from "
|
|
8
|
-
import { PointerEventsCheckLevel } from "@testing-library/user-event";
|
|
2
|
+
createFilterFieldHelpers,
|
|
3
|
+
FilterFieldHelper,
|
|
4
|
+
FilterFieldPinnedAndRecentSuggestionBase,
|
|
5
|
+
FilterFieldPinnedSuggestion,
|
|
6
|
+
FilterFieldRecentSuggestion
|
|
7
|
+
} from "../../../shared/testing-helpers/filters/filter-field.js";
|
|
9
8
|
import { isFakeTimersEnabled } from "../utils/isFakeTimersEnabled.js";
|
|
10
9
|
import { setupUserEvent } from "../utils/setup-user-event.js";
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
return this.suggestionRoot.querySelector(
|
|
18
|
-
"button[aria-checked]"
|
|
19
|
-
);
|
|
20
|
-
}
|
|
21
|
-
/**
|
|
22
|
-
* Receives the value of the suggestion that would be applied.
|
|
23
|
-
*/
|
|
24
|
-
get value() {
|
|
25
|
-
return this.suggestionRoot.childNodes.item(1).textContent;
|
|
26
|
-
}
|
|
27
|
-
/**
|
|
28
|
-
* Returns if the current suggestion is currently pinned or not.
|
|
29
|
-
*/
|
|
30
|
-
get pinned() {
|
|
31
|
-
if (this.pinButtonElement) {
|
|
32
|
-
return this.pinButtonElement.getAttribute("aria-checked") === "true";
|
|
33
|
-
}
|
|
34
|
-
return false;
|
|
35
|
-
}
|
|
36
|
-
/**
|
|
37
|
-
* Applies the suggestion to the filter field.
|
|
38
|
-
*/
|
|
39
|
-
async apply() {
|
|
40
|
-
await setupUserEvent({
|
|
41
|
-
pointerEventsCheck: PointerEventsCheckLevel.Never
|
|
42
|
-
}).click(this.suggestionRoot);
|
|
43
|
-
}
|
|
44
|
-
get pinEnabled() {
|
|
45
|
-
if (this.pinButtonElement) {
|
|
46
|
-
return this.pinButtonElement.getAttribute("aria-disabled") === "false";
|
|
47
|
-
}
|
|
48
|
-
return false;
|
|
49
|
-
}
|
|
50
|
-
/**
|
|
51
|
-
* Allows the suggestion to be pinned if not pinned already.
|
|
52
|
-
*/
|
|
53
|
-
async pin() {
|
|
54
|
-
if (this.pinned === true) {
|
|
55
|
-
return;
|
|
56
|
-
}
|
|
57
|
-
if (this.pinButtonElement) {
|
|
58
|
-
await setupUserEvent({
|
|
59
|
-
pointerEventsCheck: PointerEventsCheckLevel.Never
|
|
60
|
-
}).click(this.pinButtonElement);
|
|
61
|
-
if (isFakeTimersEnabled()) {
|
|
62
|
-
await act(async () => {
|
|
63
|
-
jest.runOnlyPendingTimers();
|
|
64
|
-
});
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
/**
|
|
69
|
-
* Allows the suggestion to be unpinned if not unpinned already.
|
|
70
|
-
*/
|
|
71
|
-
async unpin() {
|
|
72
|
-
if (this.pinned === false) {
|
|
73
|
-
return;
|
|
74
|
-
}
|
|
75
|
-
if (this.pinButtonElement) {
|
|
76
|
-
await setupUserEvent({
|
|
77
|
-
pointerEventsCheck: PointerEventsCheckLevel.Never
|
|
78
|
-
}).click(this.pinButtonElement);
|
|
79
|
-
if (isFakeTimersEnabled()) {
|
|
80
|
-
await act(async () => {
|
|
81
|
-
jest.runOnlyPendingTimers();
|
|
82
|
-
});
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
class FilterFieldPinnedSuggestion extends FilterFieldPinnedAndRecentSuggestionBase {
|
|
88
|
-
/**
|
|
89
|
-
* Receives the value of the suggestion that would be applied.
|
|
90
|
-
*/
|
|
91
|
-
get value() {
|
|
92
|
-
return this.suggestionRoot.childNodes.item(0).textContent;
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
class FilterFieldRecentSuggestion extends FilterFieldPinnedAndRecentSuggestionBase {
|
|
96
|
-
/**
|
|
97
|
-
* Receives the value of the suggestion that would be applied.
|
|
98
|
-
*/
|
|
99
|
-
get value() {
|
|
100
|
-
return this.suggestionRoot.querySelector('[role="option"]')?.childNodes.item(0).textContent ?? null;
|
|
101
|
-
}
|
|
102
|
-
/**
|
|
103
|
-
* Receives time offset indicating when the filter statement has last been used.
|
|
104
|
-
*/
|
|
105
|
-
get lastUsed() {
|
|
106
|
-
return this.suggestionRoot.querySelector('[role="option"]')?.childNodes.item(1).textContent ?? null;
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
class FilterFieldHelper {
|
|
110
|
-
constructor(dataTestId) {
|
|
111
|
-
this.dataTestId = dataTestId;
|
|
112
|
-
}
|
|
113
|
-
dataTestId;
|
|
114
|
-
get filterFieldWrapper() {
|
|
115
|
-
return screen.getByTestId(this.dataTestId);
|
|
116
|
-
}
|
|
117
|
-
get editorView() {
|
|
118
|
-
return this.filterFieldWrapper.querySelector(
|
|
119
|
-
".cm-content"
|
|
120
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
121
|
-
)?.cmTile.view;
|
|
122
|
-
}
|
|
123
|
-
get inputElement() {
|
|
124
|
-
return within(this.filterFieldWrapper).getAllByRole(
|
|
125
|
-
"textbox",
|
|
126
|
-
{ hidden: true }
|
|
127
|
-
)[1];
|
|
128
|
-
}
|
|
129
|
-
get filterFieldElement() {
|
|
130
|
-
return within(this.filterFieldWrapper).getByRole(
|
|
131
|
-
"textbox",
|
|
132
|
-
{
|
|
133
|
-
name: "Filter field",
|
|
134
|
-
hidden: true
|
|
135
|
-
}
|
|
136
|
-
);
|
|
137
|
-
}
|
|
138
|
-
get placeholderElement() {
|
|
139
|
-
return this.filterFieldWrapper.querySelector(".cm-placeholder");
|
|
140
|
-
}
|
|
141
|
-
get suggestionsContainer() {
|
|
142
|
-
return screen.queryByTestId(`filterfield-suggestions-${this.dataTestId}`);
|
|
143
|
-
}
|
|
144
|
-
/** Returns the value of the native text input. */
|
|
145
|
-
get value() {
|
|
146
|
-
return this.inputElement.value;
|
|
147
|
-
}
|
|
148
|
-
/** Returns the value of the filter field. */
|
|
149
|
-
get filterFieldValue() {
|
|
150
|
-
return this.filterFieldElement.textContent || "";
|
|
151
|
-
}
|
|
152
|
-
/**
|
|
153
|
-
* The placeholder text shown in the filter field.
|
|
154
|
-
* Undefined if the placeholder is currently not rendered.
|
|
155
|
-
*/
|
|
156
|
-
get placeholder() {
|
|
157
|
-
return this.placeholderElement?.textContent || void 0;
|
|
158
|
-
}
|
|
159
|
-
/** Focus the filter field if not already focused. */
|
|
160
|
-
async focus() {
|
|
161
|
-
if (!this.filterFieldWrapper.contains(document.activeElement)) {
|
|
162
|
-
await act(async () => {
|
|
163
|
-
this.inputElement.focus();
|
|
164
|
-
});
|
|
165
|
-
expect(
|
|
166
|
-
this.filterFieldWrapper.contains(document.activeElement)
|
|
167
|
-
).toBeTruthy();
|
|
168
|
-
if (isFakeTimersEnabled()) {
|
|
169
|
-
await act(async () => {
|
|
170
|
-
jest.runOnlyPendingTimers();
|
|
171
|
-
});
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
/** Blur the filter field by tabbing out of it. */
|
|
176
|
-
async blur() {
|
|
177
|
-
await act(async () => {
|
|
178
|
-
this.filterFieldElement.blur();
|
|
179
|
-
});
|
|
180
|
-
if (isFakeTimersEnabled()) {
|
|
181
|
-
await act(async () => {
|
|
182
|
-
jest.runOnlyPendingTimers();
|
|
183
|
-
});
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
/** Fill a provided value into the filter field. */
|
|
187
|
-
async type(text, cursorIndex) {
|
|
188
|
-
const regEx = new RegExp(text, "g");
|
|
189
|
-
const matches = this.value.match(regEx);
|
|
190
|
-
await this.moveCursorTo(cursorIndex ?? -1);
|
|
191
|
-
await setupUserEvent({
|
|
192
|
-
pointerEventsCheck: PointerEventsCheckLevel.Never
|
|
193
|
-
}).keyboard(text);
|
|
194
|
-
await waitFor(() => {
|
|
195
|
-
expect(this.value.match(regEx) ?? []).toHaveLength(
|
|
196
|
-
(matches?.length ?? 0) + 1
|
|
197
|
-
);
|
|
198
|
-
});
|
|
199
|
-
if (isFakeTimersEnabled()) {
|
|
200
|
-
await act(async () => {
|
|
201
|
-
jest.runOnlyPendingTimers();
|
|
202
|
-
});
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
/** Clears the filter field value. */
|
|
206
|
-
async clear() {
|
|
207
|
-
if (this.value !== "") {
|
|
208
|
-
await setupUserEvent({
|
|
209
|
-
pointerEventsCheck: PointerEventsCheckLevel.Never
|
|
210
|
-
}).click(
|
|
211
|
-
await within(this.filterFieldWrapper).findByRole("button", {
|
|
212
|
-
name: "Clear filter",
|
|
213
|
-
hidden: true
|
|
214
|
-
})
|
|
215
|
-
);
|
|
216
|
-
if (isFakeTimersEnabled()) {
|
|
217
|
-
await act(async () => {
|
|
218
|
-
jest.runOnlyPendingTimers();
|
|
219
|
-
});
|
|
220
|
-
}
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
/**
|
|
224
|
-
* Moves the cursor to the given index.
|
|
225
|
-
* -1 moves to the end of the input.
|
|
226
|
-
*/
|
|
227
|
-
async moveCursorTo(index) {
|
|
228
|
-
const user = setupUserEvent({
|
|
229
|
-
pointerEventsCheck: PointerEventsCheckLevel.Never
|
|
230
|
-
});
|
|
231
|
-
if (index === -1) {
|
|
232
|
-
await act(async () => {
|
|
233
|
-
await this.focus();
|
|
234
|
-
});
|
|
235
|
-
await user.keyboard("{PageDown}");
|
|
236
|
-
return;
|
|
237
|
-
}
|
|
238
|
-
if (!this.editorView) {
|
|
239
|
-
console.warn("FilterField editor could not be found. Panic now.");
|
|
240
|
-
return;
|
|
241
|
-
}
|
|
242
|
-
await act(async () => {
|
|
243
|
-
await this.focus();
|
|
244
|
-
this.editorView.dispatch({
|
|
245
|
-
selection: {
|
|
246
|
-
anchor: Math.min(index, this.editorView.state.doc.length)
|
|
247
|
-
}
|
|
248
|
-
});
|
|
249
|
-
});
|
|
250
|
-
if (isFakeTimersEnabled()) {
|
|
251
|
-
await act(async () => {
|
|
252
|
-
jest.runOnlyPendingTimers();
|
|
253
|
-
});
|
|
254
|
-
}
|
|
255
|
-
}
|
|
256
|
-
/** Open the suggestions overlay with the keyboard shortcut. */
|
|
257
|
-
async openSuggestions() {
|
|
258
|
-
await act(async () => {
|
|
259
|
-
await this.focus();
|
|
260
|
-
});
|
|
261
|
-
await setupUserEvent({
|
|
262
|
-
pointerEventsCheck: PointerEventsCheckLevel.Never
|
|
263
|
-
}).keyboard("{Control>}{Space}{/Control}");
|
|
264
|
-
await waitFor(
|
|
265
|
-
async () => expect(this.suggestionsContainer).toBeInTheDocument()
|
|
266
|
-
);
|
|
267
|
-
if (isFakeTimersEnabled()) {
|
|
268
|
-
await act(async () => {
|
|
269
|
-
jest.runOnlyPendingTimers();
|
|
270
|
-
});
|
|
271
|
-
}
|
|
272
|
-
}
|
|
273
|
-
/**
|
|
274
|
-
* Close the suggestions overlay using the `Escape` key.
|
|
275
|
-
* If the filter field is focused, the focus will stay in the filter field.
|
|
276
|
-
*/
|
|
277
|
-
async closeSuggestions() {
|
|
278
|
-
await act(async () => {
|
|
279
|
-
await this.focus();
|
|
280
|
-
});
|
|
281
|
-
await setupUserEvent({
|
|
282
|
-
pointerEventsCheck: PointerEventsCheckLevel.Never
|
|
283
|
-
}).keyboard("{Escape}");
|
|
284
|
-
if (this.suggestionsContainer !== null) {
|
|
285
|
-
await waitForElementToBeRemoved(() => this.suggestionsContainer);
|
|
286
|
-
}
|
|
287
|
-
}
|
|
288
|
-
/** Apply the suggestion with the given text. */
|
|
289
|
-
async applySuggestion(queryString) {
|
|
290
|
-
await act(async () => {
|
|
291
|
-
await this.focus();
|
|
292
|
-
});
|
|
293
|
-
await waitFor(() => {
|
|
294
|
-
expect(this.suggestionsContainer).toBeInTheDocument();
|
|
295
|
-
});
|
|
296
|
-
if (!this.suggestionsContainer) {
|
|
297
|
-
console.warn(
|
|
298
|
-
"The filter field suggestions overlay seems to be closed. Make sure to open it before applying a suggestion."
|
|
299
|
-
);
|
|
300
|
-
return;
|
|
301
|
-
}
|
|
302
|
-
const showMore = within(this.suggestionsContainer).queryByRole("option", {
|
|
303
|
-
name: "Show more"
|
|
304
|
-
});
|
|
305
|
-
if (showMore) {
|
|
306
|
-
await setupUserEvent({
|
|
307
|
-
pointerEventsCheck: PointerEventsCheckLevel.Never
|
|
308
|
-
}).click(showMore);
|
|
309
|
-
}
|
|
310
|
-
let suggestions = within(this.suggestionsContainer).queryAllByTestId(
|
|
311
|
-
queryString,
|
|
312
|
-
{ exact: true }
|
|
313
|
-
);
|
|
314
|
-
if (suggestions.length > 0) {
|
|
315
|
-
suggestions = suggestions.reduce((acc, container) => {
|
|
316
|
-
const suggestion = within(container).queryByRole("option");
|
|
317
|
-
if (suggestion) {
|
|
318
|
-
acc.push(suggestion);
|
|
319
|
-
}
|
|
320
|
-
return acc;
|
|
321
|
-
}, []);
|
|
322
|
-
}
|
|
323
|
-
if (suggestions.length === 0) {
|
|
324
|
-
suggestions.push(
|
|
325
|
-
...within(this.suggestionsContainer).queryAllByText(
|
|
326
|
-
(_, element) => element?.getAttribute("role") === "option" && // The replace is needed to ignore non-breaking spaces
|
|
327
|
-
element.firstElementChild?.textContent?.replace(/\s+/g, " ") === queryString.replace(/\s+/g, " ")
|
|
328
|
-
)
|
|
329
|
-
);
|
|
330
|
-
}
|
|
331
|
-
if (suggestions.length === 0) {
|
|
332
|
-
console.warn(
|
|
333
|
-
`Could not query a suggestion with the given queryString '${queryString}' (trying queryByTestId and queryByText).`
|
|
334
|
-
);
|
|
335
|
-
return;
|
|
336
|
-
}
|
|
337
|
-
if (suggestions.length > 1) {
|
|
338
|
-
console.warn(
|
|
339
|
-
"Found multiple suggestions with the given testId. Will apply the first one"
|
|
340
|
-
);
|
|
341
|
-
}
|
|
342
|
-
await setupUserEvent({
|
|
343
|
-
pointerEventsCheck: PointerEventsCheckLevel.Never
|
|
344
|
-
}).click(suggestions[0]);
|
|
345
|
-
}
|
|
346
|
-
/** Get recent suggestions */
|
|
347
|
-
async getRecentSuggestions() {
|
|
348
|
-
if (this.suggestionsContainer) {
|
|
349
|
-
const recentGroup = within(this.suggestionsContainer).getByRole("group", {
|
|
350
|
-
name: "Recently used filters"
|
|
351
|
-
});
|
|
352
|
-
const suggestions = within(recentGroup).getAllByRole(
|
|
353
|
-
"group",
|
|
354
|
-
{
|
|
355
|
-
hidden: true,
|
|
356
|
-
name(_accessibleName, element) {
|
|
357
|
-
return element.getAttribute(
|
|
358
|
-
"data-strato-filter-field-virtual-focus-type"
|
|
359
|
-
) === "recent-suggestion";
|
|
360
|
-
}
|
|
361
|
-
}
|
|
362
|
-
);
|
|
363
|
-
return suggestions.map((el) => new FilterFieldRecentSuggestion(el));
|
|
364
|
-
}
|
|
365
|
-
return [];
|
|
366
|
-
}
|
|
367
|
-
/** Get pinned suggestions */
|
|
368
|
-
async getPinnedSuggestions() {
|
|
369
|
-
if (this.suggestionsContainer) {
|
|
370
|
-
try {
|
|
371
|
-
await waitFor(
|
|
372
|
-
() => screen.getByRole("group", {
|
|
373
|
-
name: "Pinned filters",
|
|
374
|
-
hidden: true,
|
|
375
|
-
suggest: false
|
|
376
|
-
})
|
|
377
|
-
);
|
|
378
|
-
const pinnedGroup = within(this.suggestionsContainer).getByRole(
|
|
379
|
-
"group",
|
|
380
|
-
{
|
|
381
|
-
name: "Pinned filters",
|
|
382
|
-
hidden: true
|
|
383
|
-
}
|
|
384
|
-
);
|
|
385
|
-
const suggestions = within(pinnedGroup).getAllByRole(
|
|
386
|
-
"group",
|
|
387
|
-
{
|
|
388
|
-
hidden: true,
|
|
389
|
-
name(_accessibleName, element) {
|
|
390
|
-
return element.getAttribute(
|
|
391
|
-
"data-strato-filter-field-virtual-focus-type"
|
|
392
|
-
) === "pinned-suggestion";
|
|
393
|
-
}
|
|
394
|
-
}
|
|
395
|
-
);
|
|
396
|
-
return suggestions.map((el) => new FilterFieldPinnedSuggestion(el));
|
|
397
|
-
} catch {
|
|
398
|
-
return [];
|
|
399
|
-
}
|
|
400
|
-
}
|
|
401
|
-
return [];
|
|
402
|
-
}
|
|
403
|
-
/** Paste text at the current cursor position */
|
|
404
|
-
async paste(text, cursorIndex) {
|
|
405
|
-
await this.moveCursorTo(cursorIndex ?? -1);
|
|
406
|
-
await setupUserEvent({
|
|
407
|
-
pointerEventsCheck: PointerEventsCheckLevel.Never
|
|
408
|
-
}).paste(text);
|
|
409
|
-
if (isFakeTimersEnabled()) {
|
|
410
|
-
await act(async () => {
|
|
411
|
-
jest.runOnlyPendingTimers();
|
|
412
|
-
});
|
|
413
|
-
}
|
|
414
|
-
}
|
|
415
|
-
}
|
|
416
|
-
function getFilterFieldHelper(dataTestId) {
|
|
417
|
-
return new FilterFieldHelper(dataTestId);
|
|
418
|
-
}
|
|
10
|
+
const helpers = createFilterFieldHelpers({
|
|
11
|
+
isFakeTimersEnabled,
|
|
12
|
+
runOnlyPendingTimers: () => jest.runOnlyPendingTimers(),
|
|
13
|
+
setupUserEvent
|
|
14
|
+
});
|
|
15
|
+
const getFilterFieldHelper = helpers.getFilterFieldHelper;
|
|
419
16
|
export {
|
|
420
17
|
FilterFieldHelper,
|
|
421
18
|
FilterFieldPinnedAndRecentSuggestionBase,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../src/jest/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 { PointerEventsCheckLevel } from '@testing-library/user-event';\n\nimport { isFakeTimersEnabled } from '../utils/isFakeTimersEnabled.js';\nimport { setupUserEvent } from '../utils/setup-user-event.js';\n\n/**\n * Base class for pinned or recent filter field suggestion.\n * @public */\nexport class FilterFieldPinnedAndRecentSuggestionBase {\n constructor(protected readonly suggestionRoot: HTMLDivElement) {}\n\n private 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 setupUserEvent({\n pointerEventsCheck: PointerEventsCheckLevel.Never,\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 setupUserEvent({\n pointerEventsCheck: PointerEventsCheckLevel.Never,\n }).click(this.pinButtonElement);\n\n if (isFakeTimersEnabled()) {\n await act(async () => {\n jest.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 setupUserEvent({\n pointerEventsCheck: PointerEventsCheckLevel.Never,\n }).click(this.pinButtonElement);\n\n if (isFakeTimersEnabled()) {\n await act(async () => {\n jest.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 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 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 get filterFieldWrapper(): HTMLElement {\n return screen.getByTestId(this.dataTestId);\n }\n\n private get editorView() {\n return (\n this.filterFieldWrapper.querySelector(\n '.cm-content',\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n ) as any\n )?.cmTile.view;\n }\n\n private get inputElement(): HTMLInputElement {\n return within(this.filterFieldWrapper).getAllByRole<HTMLInputElement>(\n 'textbox',\n { hidden: true },\n )[1];\n }\n\n private 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 private get placeholderElement(): HTMLSpanElement | null {\n return this.filterFieldWrapper.querySelector('.cm-placeholder');\n }\n\n private get suggestionsContainer(): HTMLDivElement | null {\n return screen.queryByTestId(`filterfield-suggestions-${this.dataTestId}`);\n }\n\n constructor(private dataTestId: string) {}\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 (isFakeTimersEnabled()) {\n await act(async () => {\n jest.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 (isFakeTimersEnabled()) {\n await act(async () => {\n jest.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 setupUserEvent({\n pointerEventsCheck: PointerEventsCheckLevel.Never,\n }).keyboard(text);\n await waitFor(() => {\n expect(this.value.match(regEx) ?? []).toHaveLength(\n (matches?.length ?? 0) + 1,\n );\n });\n\n if (isFakeTimersEnabled()) {\n await act(async () => {\n jest.runOnlyPendingTimers();\n });\n }\n }\n\n /** Clears the filter field value. */\n async clear() {\n if (this.value !== '') {\n await setupUserEvent({\n pointerEventsCheck: PointerEventsCheckLevel.Never,\n }).click(\n await within(this.filterFieldWrapper).findByRole('button', {\n name: 'Clear filter',\n hidden: true,\n }),\n );\n\n if (isFakeTimersEnabled()) {\n await act(async () => {\n jest.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 = 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 if (!this.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 this.editorView.dispatch({\n selection: {\n anchor: Math.min(index, this.editorView.state.doc.length),\n },\n });\n });\n\n if (isFakeTimersEnabled()) {\n await act(async () => {\n jest.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 setupUserEvent({\n pointerEventsCheck: PointerEventsCheckLevel.Never,\n }).keyboard('{Control>}{Space}{/Control}');\n\n await waitFor(async () =>\n expect(this.suggestionsContainer).toBeInTheDocument(),\n );\n if (isFakeTimersEnabled()) {\n await act(async () => {\n jest.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 setupUserEvent({\n pointerEventsCheck: PointerEventsCheckLevel.Never,\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 setupUserEvent({\n pointerEventsCheck: PointerEventsCheckLevel.Never,\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 setupUserEvent({\n pointerEventsCheck: PointerEventsCheckLevel.Never,\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((el) => new FilterFieldRecentSuggestion(el));\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((el) => new FilterFieldPinnedSuggestion(el));\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 setupUserEvent({\n pointerEventsCheck: PointerEventsCheckLevel.Never,\n }).paste(text);\n\n if (isFakeTimersEnabled()) {\n await act(async () => {\n jest.runOnlyPendingTimers();\n });\n }\n }\n}\n\n/**\n * Helper function that returns the filter field with its testing utilities.\n * @public\n */\nexport function getFilterFieldHelper(\n /** The accessible test id used to locate the filter field wrapper. */\n dataTestId: string,\n): FilterFieldHelper {\n return new FilterFieldHelper(dataTestId);\n}\n"],
|
|
5
|
-
"mappings": "AAAA;AAAA,EACE;AAAA,
|
|
4
|
+
"sourcesContent": ["import {\n createFilterFieldHelpers,\n _FilterFieldDeps,\n FilterFieldHelper,\n FilterFieldPinnedAndRecentSuggestionBase,\n FilterFieldPinnedSuggestion,\n FilterFieldRecentSuggestion,\n} from '../../../shared/testing-helpers/filters/filter-field.js';\nimport { isFakeTimersEnabled } from '../utils/isFakeTimersEnabled.js';\nimport { setupUserEvent } from '../utils/setup-user-event.js';\n\nconst helpers = createFilterFieldHelpers({\n isFakeTimersEnabled,\n runOnlyPendingTimers: () => jest.runOnlyPendingTimers(),\n setupUserEvent,\n});\n\n/** @public */\nexport const getFilterFieldHelper = helpers.getFilterFieldHelper;\n\nexport type { _FilterFieldDeps };\n\nexport {\n FilterFieldHelper,\n FilterFieldPinnedAndRecentSuggestionBase,\n FilterFieldPinnedSuggestion,\n FilterFieldRecentSuggestion,\n};\n"],
|
|
5
|
+
"mappings": "AAAA;AAAA,EACE;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,2BAA2B;AACpC,SAAS,sBAAsB;AAE/B,MAAM,UAAU,yBAAyB;AAAA,EACvC;AAAA,EACA,sBAAsB,MAAM,KAAK,qBAAqB;AAAA,EACtD;AACF,CAAC;AAGM,MAAM,uBAAuB,QAAQ;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -181,17 +181,7 @@ class DataTableRowSelectionHelper {
|
|
|
181
181
|
}
|
|
182
182
|
async getSelectAllState() {
|
|
183
183
|
const checkbox = await this.getCheckboxSelectAll();
|
|
184
|
-
|
|
185
|
-
if (value === "true") {
|
|
186
|
-
return true;
|
|
187
|
-
} else if (value === "false") {
|
|
188
|
-
return false;
|
|
189
|
-
} else if (value === "mixed") {
|
|
190
|
-
return "indeterminate";
|
|
191
|
-
}
|
|
192
|
-
throw new Error(
|
|
193
|
-
"Could not determine the state of the select all checkbox."
|
|
194
|
-
);
|
|
184
|
+
return checkbox.indeterminate === true ? "indeterminate" : checkbox.checked;
|
|
195
185
|
}
|
|
196
186
|
async toggleSelectAll() {
|
|
197
187
|
const checkbox = await this.getCheckboxSelectAll();
|