@vickbk/tests 0.1.0 → 0.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.
- package/CHANGELOG.md +39 -21
- package/LICENSE +21 -21
- package/README.md +226 -192
- package/dist/console-Cf1taTRK.mjs +39 -0
- package/dist/console-Cf1taTRK.mjs.map +1 -0
- package/dist/dom-DeHbD1ni.mjs +459 -0
- package/dist/dom-DeHbD1ni.mjs.map +1 -0
- package/dist/index-CKsosUpb.d.mts +249 -0
- package/dist/index-CKsosUpb.d.mts.map +1 -0
- package/dist/index-DtGBqDev.d.mts +25 -0
- package/dist/index-DtGBqDev.d.mts.map +1 -0
- package/dist/pattern-to-regex-BrMJtzu8.cjs.map +1 -1
- package/dist/pattern-to-regex-CO5Y0oDU.mjs.map +1 -1
- package/dist/playwright.cjs.map +1 -1
- package/dist/playwright.mjs.map +1 -1
- package/dist/promises.cjs +45 -0
- package/dist/promises.cjs.map +1 -0
- package/dist/promises.d.cts +34 -0
- package/dist/promises.d.cts.map +1 -0
- package/dist/promises.d.mts +34 -0
- package/dist/promises.d.mts.map +1 -0
- package/dist/promises.mjs +43 -0
- package/dist/promises.mjs.map +1 -0
- package/dist/react.cjs.map +1 -1
- package/dist/react.mjs.map +1 -1
- package/dist/vitest/console.d.mts +2 -0
- package/dist/vitest/console.mjs +1 -0
- package/dist/vitest/dom.d.mts +2 -0
- package/dist/vitest/dom.mjs +1 -0
- package/dist/vitest.d.mts +14 -270
- package/dist/vitest.d.mts.map +1 -1
- package/dist/vitest.mjs +13 -490
- package/dist/vitest.mjs.map +1 -1
- package/package.json +23 -1
|
@@ -0,0 +1,459 @@
|
|
|
1
|
+
import { expect } from "vitest";
|
|
2
|
+
import { fireEvent, screen, within } from "@testing-library/react";
|
|
3
|
+
import userEvent from "@testing-library/user-event";
|
|
4
|
+
//#region src/features/vitest/modules/dom/modules/toggle-event/utils/togglePolyfill.ts
|
|
5
|
+
/**
|
|
6
|
+
* Creates the browser ToggleEvent instance used by the fireEvent.toggle polyfill.
|
|
7
|
+
*
|
|
8
|
+
* @param type - Event type to dispatch.
|
|
9
|
+
* @param initDict - Optional toggle event initialization state.
|
|
10
|
+
* @returns A ToggleEvent matching the requested state.
|
|
11
|
+
* @example
|
|
12
|
+
* ```ts
|
|
13
|
+
* const event = createToggleEvent("toggle", {
|
|
14
|
+
* oldState: "closed",
|
|
15
|
+
* newState: "open",
|
|
16
|
+
* });
|
|
17
|
+
* ```
|
|
18
|
+
*/
|
|
19
|
+
function createToggleEvent(type, initDict = {
|
|
20
|
+
oldState: "closed",
|
|
21
|
+
newState: "open"
|
|
22
|
+
}) {
|
|
23
|
+
if (typeof globalThis.ToggleEvent === "undefined") {
|
|
24
|
+
class ToggleEventPolyfill extends Event {
|
|
25
|
+
oldState;
|
|
26
|
+
newState;
|
|
27
|
+
source = null;
|
|
28
|
+
constructor(eventType, eventInitDict = {}) {
|
|
29
|
+
super(eventType, eventInitDict);
|
|
30
|
+
this.oldState = eventInitDict.oldState ?? "closed";
|
|
31
|
+
this.newState = eventInitDict.newState ?? "open";
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
globalThis.ToggleEvent = ToggleEventPolyfill;
|
|
35
|
+
}
|
|
36
|
+
return new globalThis.ToggleEvent(type, {
|
|
37
|
+
bubbles: false,
|
|
38
|
+
cancelable: false,
|
|
39
|
+
...initDict
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Polyfills `fireEvent.toggle` for tests by dispatching a native ToggleEvent.
|
|
44
|
+
*
|
|
45
|
+
* @param element - Element or window to dispatch the toggle event on.
|
|
46
|
+
* @param initDict - Optional state values for the toggle transition.
|
|
47
|
+
* @returns Whether the event dispatch completed successfully.
|
|
48
|
+
* @example
|
|
49
|
+
* ```ts
|
|
50
|
+
* fireEvent.toggle(document.body, { newState: "open" });
|
|
51
|
+
* ```
|
|
52
|
+
*/
|
|
53
|
+
function togglePolyfill(element, initDict = {
|
|
54
|
+
oldState: "closed",
|
|
55
|
+
newState: "open"
|
|
56
|
+
}) {
|
|
57
|
+
const event = createToggleEvent("toggle", initDict);
|
|
58
|
+
return fireEvent(element, event);
|
|
59
|
+
}
|
|
60
|
+
//#endregion
|
|
61
|
+
//#region src/features/vitest/modules/dom/modules/toggle-event/utils/fireEvent.ts
|
|
62
|
+
const callableFireEvent = (...args) => fireEvent(...args);
|
|
63
|
+
/**
|
|
64
|
+
* Testing Library's fireEvent API with an additional `toggle` event helper.
|
|
65
|
+
*
|
|
66
|
+
* The exported object copies the standard `fireEvent` properties and adds
|
|
67
|
+
* `toggle`, which dispatches the toggle event through `togglePolyfill`.
|
|
68
|
+
* It does not install the extension onto Testing Library's original export.
|
|
69
|
+
*
|
|
70
|
+
* Standard Testing Library event helpers remain available, including
|
|
71
|
+
* `click`, `change`, `input`, `submit`, `focus`, and keyboard/mouse events.
|
|
72
|
+
*
|
|
73
|
+
* @remarks
|
|
74
|
+
* The `toggle` helper defaults to a transition from `closed` to `open`.
|
|
75
|
+
* Call-specific configuration overrides those defaults. This helper does
|
|
76
|
+
* not maintain state between calls.
|
|
77
|
+
*
|
|
78
|
+
* @example
|
|
79
|
+
* ```ts
|
|
80
|
+
* import { fireEvent } from "@vickbk/tests/vitest";
|
|
81
|
+
*
|
|
82
|
+
* const disclosure = document.querySelector("details")!;
|
|
83
|
+
*
|
|
84
|
+
* fireEvent.toggle(disclosure);
|
|
85
|
+
* fireEvent.toggle(disclosure, { newState: "closed" });
|
|
86
|
+
* ```
|
|
87
|
+
*
|
|
88
|
+
* @example
|
|
89
|
+
* ```ts
|
|
90
|
+
* import { fireEvent } from "@vickbk/tests/vitest";
|
|
91
|
+
*
|
|
92
|
+
* const button = document.querySelector("button")!;
|
|
93
|
+
*
|
|
94
|
+
* fireEvent.click(button);
|
|
95
|
+
* ```
|
|
96
|
+
*/
|
|
97
|
+
const fireEvent$1 = Object.assign(callableFireEvent, fireEvent, {
|
|
98
|
+
/**
|
|
99
|
+
* Dispatches a toggle event using the toggle polyfill.
|
|
100
|
+
*
|
|
101
|
+
* @param element - The document, element, node, or window receiving the event.
|
|
102
|
+
* @param initDict - Optional old/new state overrides for this dispatch.
|
|
103
|
+
* @returns The result returned by `togglePolyfill`.
|
|
104
|
+
*
|
|
105
|
+
* @example
|
|
106
|
+
* ```ts
|
|
107
|
+
* fireEvent.toggle(document.body, {
|
|
108
|
+
* oldState: "closed",
|
|
109
|
+
* newState: "open",
|
|
110
|
+
* });
|
|
111
|
+
* ```
|
|
112
|
+
*/
|
|
113
|
+
toggle: (element, initDict) => togglePolyfill(element, {
|
|
114
|
+
oldState: "closed",
|
|
115
|
+
newState: "open",
|
|
116
|
+
...initDict
|
|
117
|
+
}) });
|
|
118
|
+
//#endregion
|
|
119
|
+
//#region src/shared/regex/utils/escape-regex.ts
|
|
120
|
+
function escapeRegExp(str) {
|
|
121
|
+
if (typeof RegExp.escape === "function") return RegExp.escape(str);
|
|
122
|
+
return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
123
|
+
}
|
|
124
|
+
//#endregion
|
|
125
|
+
//#region src/shared/regex/utils/pattern-to-regex.ts
|
|
126
|
+
function patternToRegex(target, flags = "i") {
|
|
127
|
+
return typeof target === "string" ? new RegExp(escapeRegExp(target), flags) : target;
|
|
128
|
+
}
|
|
129
|
+
//#endregion
|
|
130
|
+
//#region src/features/vitest/modules/dom/utils/getSearchArea.ts
|
|
131
|
+
/**
|
|
132
|
+
* Returns the Testing Library query API for a container or the whole document.
|
|
133
|
+
*
|
|
134
|
+
* @param container Optional element to scope queries to.
|
|
135
|
+
* @returns The scoped query API, or the global `screen` query API.
|
|
136
|
+
* @example
|
|
137
|
+
* ```ts
|
|
138
|
+
* const queries = getSearchArea(formElement);
|
|
139
|
+
* queries.getByRole("button", { name: "Submit" });
|
|
140
|
+
* ```
|
|
141
|
+
*/
|
|
142
|
+
function getSearchArea(container) {
|
|
143
|
+
return container ? within(container) : screen;
|
|
144
|
+
}
|
|
145
|
+
//#endregion
|
|
146
|
+
//#region src/features/vitest/modules/dom/utils/clickLabelInput.ts
|
|
147
|
+
/**
|
|
148
|
+
* Simulates clicking an input found by its label or placeholder.
|
|
149
|
+
*
|
|
150
|
+
* @param label Label or placeholder text/regular expression identifying the input.
|
|
151
|
+
* @param container Optional element that limits the input search.
|
|
152
|
+
* @returns A promise that resolves after the click interaction completes.
|
|
153
|
+
* @example
|
|
154
|
+
* ```ts
|
|
155
|
+
* await clickLabelInput("Email address");
|
|
156
|
+
* ```
|
|
157
|
+
*/
|
|
158
|
+
async function clickLabelInput(label, container) {
|
|
159
|
+
const user = userEvent.setup();
|
|
160
|
+
const regex = patternToRegex(label);
|
|
161
|
+
const searchArea = getSearchArea(container);
|
|
162
|
+
const element = typeof label === "string" && label.length === 0 ? searchArea.queryByPlaceholderText(regex) || searchArea.getByLabelText(regex) : searchArea.queryByLabelText(regex) || searchArea.getByPlaceholderText(regex);
|
|
163
|
+
await user.click(element);
|
|
164
|
+
}
|
|
165
|
+
//#endregion
|
|
166
|
+
//#region src/features/vitest/modules/dom/utils/getCheckboxRadio.ts
|
|
167
|
+
/**
|
|
168
|
+
* Finds a checkbox or radio control by its accessible name.
|
|
169
|
+
*
|
|
170
|
+
* @param target Accessible name to match, as text or a regular expression.
|
|
171
|
+
* @param container Optional element that limits the search.
|
|
172
|
+
* @returns The matching checkbox or radio input.
|
|
173
|
+
* @throws If neither a checkbox nor a radio control matches the target.
|
|
174
|
+
* @example
|
|
175
|
+
* ```ts
|
|
176
|
+
* const consent = getCheckboxRadio("Accept terms");
|
|
177
|
+
* expect(consent).toBeChecked();
|
|
178
|
+
* ```
|
|
179
|
+
*/
|
|
180
|
+
function getCheckboxRadio(target, container) {
|
|
181
|
+
const regex = patternToRegex(target);
|
|
182
|
+
const searchArea = getSearchArea(container);
|
|
183
|
+
const element = searchArea.queryByRole("checkbox", { name: regex }) || searchArea.queryByRole("radio", { name: regex });
|
|
184
|
+
if (element) return element;
|
|
185
|
+
throw new Error(`Unable to find a checkbox or radio control named "${String(target)}".`);
|
|
186
|
+
}
|
|
187
|
+
//#endregion
|
|
188
|
+
//#region src/features/vitest/modules/dom/utils/isChecked.ts
|
|
189
|
+
/**
|
|
190
|
+
* Asserts that the radio button or checkbox matching the accessible name is checked.
|
|
191
|
+
*
|
|
192
|
+
* @param selector Accessible label text or regular expression for the control.
|
|
193
|
+
* @param container Optional element that limits the control search.
|
|
194
|
+
* @returns Nothing; throws an assertion error if the control is absent or unchecked.
|
|
195
|
+
* @example
|
|
196
|
+
* ```ts
|
|
197
|
+
* isChecked("Accept terms");
|
|
198
|
+
* ```
|
|
199
|
+
*/
|
|
200
|
+
function isChecked(selector, container) {
|
|
201
|
+
expect(getCheckboxRadio(selector, container)).toBeChecked();
|
|
202
|
+
}
|
|
203
|
+
//#endregion
|
|
204
|
+
//#region src/features/vitest/modules/dom/utils/isNotChecked.ts
|
|
205
|
+
/**
|
|
206
|
+
* Asserts that the radio button or checkbox matching the accessible name is unchecked.
|
|
207
|
+
*
|
|
208
|
+
* @param selector Accessible label text or regular expression for the control.
|
|
209
|
+
* @param container Optional element that limits the control search.
|
|
210
|
+
* @returns Nothing; throws an assertion error if the control is absent or checked.
|
|
211
|
+
* @example
|
|
212
|
+
* ```ts
|
|
213
|
+
* isNotChecked("Subscribe to updates");
|
|
214
|
+
* ```
|
|
215
|
+
*/
|
|
216
|
+
function isNotChecked(selector, container) {
|
|
217
|
+
expect(getCheckboxRadio(selector, container)).not.toBeChecked();
|
|
218
|
+
}
|
|
219
|
+
//#endregion
|
|
220
|
+
//#region src/features/vitest/modules/dom/utils/shouldHaveAttributes.ts
|
|
221
|
+
/**
|
|
222
|
+
* Asserts that an HTMLElement possesses the expected set of DOM attributes and values.
|
|
223
|
+
*
|
|
224
|
+
* Handles string/numeric values, boolean attributes (true = present, false = absent),
|
|
225
|
+
* and null/undefined values (absent).
|
|
226
|
+
*
|
|
227
|
+
* @param element The DOM element whose attributes will be checked.
|
|
228
|
+
* @param attributes Attribute names mapped to their expected values.
|
|
229
|
+
* @returns Nothing; throws an assertion error when any attribute does not match.
|
|
230
|
+
* @example
|
|
231
|
+
* ```ts
|
|
232
|
+
* shouldHaveAttributes(input, { required: true, "aria-label": "Email" });
|
|
233
|
+
* ```
|
|
234
|
+
*/
|
|
235
|
+
function shouldHaveAttributes(element, attributes) {
|
|
236
|
+
Object.entries(attributes).forEach(([key, value]) => {
|
|
237
|
+
if (value === null || value === void 0 || value === false) expect(element).not.toHaveAttribute(key);
|
|
238
|
+
else if (value === true) expect(element).toHaveAttribute(key);
|
|
239
|
+
else expect(element).toHaveAttribute(key, String(value));
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
//#endregion
|
|
243
|
+
//#region src/features/vitest/modules/dom/utils/shouldHaveTestId.ts
|
|
244
|
+
/**
|
|
245
|
+
* Asserts that elements with all supplied test IDs are present in the document.
|
|
246
|
+
*
|
|
247
|
+
* @param ids One or more `data-testid` values to locate.
|
|
248
|
+
* @returns The elements found, in the same order as the supplied IDs.
|
|
249
|
+
* @example
|
|
250
|
+
* ```ts
|
|
251
|
+
* const [submitButton] = shouldHaveTestId("submit-button");
|
|
252
|
+
* ```
|
|
253
|
+
*/
|
|
254
|
+
function shouldHaveTestId(...ids) {
|
|
255
|
+
const testElements = ids.map((id) => screen.getByTestId(id));
|
|
256
|
+
testElements.forEach((element) => expect(element).toBeInTheDocument());
|
|
257
|
+
return testElements;
|
|
258
|
+
}
|
|
259
|
+
//#endregion
|
|
260
|
+
//#region src/features/vitest/modules/dom/utils/shouldNotHaveTestId.ts
|
|
261
|
+
/**
|
|
262
|
+
* Asserts that elements with all supplied test IDs are absent from the document.
|
|
263
|
+
*
|
|
264
|
+
* @param ids One or more `data-testid` values to verify are absent.
|
|
265
|
+
* @returns The query results (`null` for each absent element), in ID order.
|
|
266
|
+
* @example
|
|
267
|
+
* ```ts
|
|
268
|
+
* shouldNotHaveTestId("loading-spinner");
|
|
269
|
+
* ```
|
|
270
|
+
*/
|
|
271
|
+
function shouldNotHaveTestId(...ids) {
|
|
272
|
+
const testElements = ids.map((id) => screen.queryByTestId(id));
|
|
273
|
+
testElements.forEach((element) => expect(element).not.toBeInTheDocument());
|
|
274
|
+
return testElements;
|
|
275
|
+
}
|
|
276
|
+
//#endregion
|
|
277
|
+
//#region src/features/vitest/modules/dom/utils/shouldGetByText.ts
|
|
278
|
+
/**
|
|
279
|
+
* Creates assertion/element pairs for text matchers, supporting a zero-based occurrence index.
|
|
280
|
+
*
|
|
281
|
+
* @param texts Text strings, regular expressions, or `[matcher, occurrenceIndex]` pairs.
|
|
282
|
+
* @returns Assertion and matching-element pairs, with `null` for missing text.
|
|
283
|
+
* @example
|
|
284
|
+
* ```ts
|
|
285
|
+
* const [{ expected, element }] = shouldGetByText(["Item", 1]);
|
|
286
|
+
* ```
|
|
287
|
+
*/
|
|
288
|
+
function shouldGetByText(...texts) {
|
|
289
|
+
return texts.map((text) => {
|
|
290
|
+
if (Array.isArray(text)) {
|
|
291
|
+
const [matcher, nth] = text;
|
|
292
|
+
const element = screen.queryAllByText(patternToRegex(matcher))[nth] ?? null;
|
|
293
|
+
return {
|
|
294
|
+
expected: expect(element),
|
|
295
|
+
element
|
|
296
|
+
};
|
|
297
|
+
}
|
|
298
|
+
const element = screen.queryByText(patternToRegex(text));
|
|
299
|
+
return {
|
|
300
|
+
expected: expect(element),
|
|
301
|
+
element
|
|
302
|
+
};
|
|
303
|
+
});
|
|
304
|
+
}
|
|
305
|
+
//#endregion
|
|
306
|
+
//#region src/features/vitest/modules/dom/utils/shouldNotSee.ts
|
|
307
|
+
/**
|
|
308
|
+
* Asserts that none of the supplied text matchers appear in the document.
|
|
309
|
+
*
|
|
310
|
+
* @param texts Text strings or regular expressions that must be absent.
|
|
311
|
+
* @returns Nothing; throws an assertion error if any text is found.
|
|
312
|
+
* @example
|
|
313
|
+
* ```ts
|
|
314
|
+
* shouldNotSee("Error", /failed/i);
|
|
315
|
+
* ```
|
|
316
|
+
*/
|
|
317
|
+
const shouldNotSee = (...texts) => {
|
|
318
|
+
shouldGetByText(...texts).forEach(({ expected }) => expected.not.toBeInTheDocument());
|
|
319
|
+
};
|
|
320
|
+
//#endregion
|
|
321
|
+
//#region src/features/vitest/modules/dom/utils/shouldSee.ts
|
|
322
|
+
/**
|
|
323
|
+
* Asserts that each requested text matcher is present in the document.
|
|
324
|
+
*
|
|
325
|
+
* @param texts Text strings, regular expressions, or `[matcher, occurrenceIndex]` pairs.
|
|
326
|
+
* @returns The matching elements in the same order as the supplied matchers.
|
|
327
|
+
* @example
|
|
328
|
+
* ```ts
|
|
329
|
+
* const [heading] = shouldSee("Welcome");
|
|
330
|
+
* ```
|
|
331
|
+
*/
|
|
332
|
+
const shouldSee = (...texts) => {
|
|
333
|
+
return shouldGetByText(...texts).map(({ expected, element }) => {
|
|
334
|
+
expected.toBeInTheDocument();
|
|
335
|
+
return element;
|
|
336
|
+
});
|
|
337
|
+
};
|
|
338
|
+
//#endregion
|
|
339
|
+
//#region src/features/vitest/modules/dom/utils/togglePopover.ts
|
|
340
|
+
/**
|
|
341
|
+
* Dispatches a toggle event on the first element matching a selector.
|
|
342
|
+
*
|
|
343
|
+
* @param options Selector, optional search container, and target open/closed state.
|
|
344
|
+
* @returns The matched element, or `undefined` when no matching element exists.
|
|
345
|
+
* @example
|
|
346
|
+
* ```ts
|
|
347
|
+
* const popover = togglePopover({ selector: "#help", newState: "open" });
|
|
348
|
+
* ```
|
|
349
|
+
*/
|
|
350
|
+
function togglePopover({ container, selector, newState = "open" }) {
|
|
351
|
+
const element = (container ?? document).querySelector(selector);
|
|
352
|
+
if (!element) return;
|
|
353
|
+
fireEvent$1.toggle(element, { newState });
|
|
354
|
+
return element;
|
|
355
|
+
}
|
|
356
|
+
//#endregion
|
|
357
|
+
//#region src/features/vitest/modules/dom/utils/userClicks.ts
|
|
358
|
+
/**
|
|
359
|
+
* Simulates a user click on a button with the requested name, or matching text.
|
|
360
|
+
*
|
|
361
|
+
* @param target Button accessible name or text string/regular expression.
|
|
362
|
+
* @param container Optional element that limits the target search.
|
|
363
|
+
* @returns A promise that resolves after the click interaction completes.
|
|
364
|
+
* @example
|
|
365
|
+
* ```ts
|
|
366
|
+
* await userClicks("Save");
|
|
367
|
+
* ```
|
|
368
|
+
*/
|
|
369
|
+
const userClicks = async (target, container) => {
|
|
370
|
+
const user = userEvent.setup();
|
|
371
|
+
const regex = patternToRegex(target);
|
|
372
|
+
const searchArea = container ? within(container) : screen;
|
|
373
|
+
const element = searchArea.queryByRole("button", { name: regex }) || searchArea.getByText(regex);
|
|
374
|
+
await user.click(element);
|
|
375
|
+
};
|
|
376
|
+
//#endregion
|
|
377
|
+
//#region src/features/vitest/modules/dom/utils/getTextBox.ts
|
|
378
|
+
/**
|
|
379
|
+
* Finds a text-entry control by its accessible name or placeholder.
|
|
380
|
+
*
|
|
381
|
+
* Supported controls are text-like inputs, textareas, and elements exposed
|
|
382
|
+
* with the textbox role. A matching non-text element is rejected.
|
|
383
|
+
*
|
|
384
|
+
* @param target Accessible name or placeholder to match, as text or a regular expression.
|
|
385
|
+
* @param container Optional element that limits the search.
|
|
386
|
+
* @returns The matching textbox element.
|
|
387
|
+
* @throws If no textbox matches, or a placeholder match is not a text-entry control.
|
|
388
|
+
* @example
|
|
389
|
+
* ```ts
|
|
390
|
+
* const email = getTextBox("Email address");
|
|
391
|
+
* const notes = getTextBox("Notes", dialog);
|
|
392
|
+
* ```
|
|
393
|
+
*/
|
|
394
|
+
function getTextBox(target, container) {
|
|
395
|
+
const regex = patternToRegex(target);
|
|
396
|
+
const searchArea = getSearchArea(container);
|
|
397
|
+
const textBox = searchArea.queryByRole("textbox", { name: regex });
|
|
398
|
+
if (textBox) return textBox;
|
|
399
|
+
const placeholderMatch = searchArea.queryByPlaceholderText(regex);
|
|
400
|
+
if (placeholderMatch) {
|
|
401
|
+
const isTextInput = placeholderMatch instanceof HTMLInputElement && [
|
|
402
|
+
"text",
|
|
403
|
+
"search",
|
|
404
|
+
"email",
|
|
405
|
+
"url",
|
|
406
|
+
"tel",
|
|
407
|
+
"password"
|
|
408
|
+
].includes(placeholderMatch.type);
|
|
409
|
+
const isTextArea = placeholderMatch instanceof HTMLTextAreaElement;
|
|
410
|
+
const isContentEditable = placeholderMatch.getAttribute("contenteditable") === "true" && placeholderMatch.getAttribute("role") === "textbox";
|
|
411
|
+
if (isTextInput || isTextArea || isContentEditable) return placeholderMatch;
|
|
412
|
+
throw new Error(`Element matching placeholder "${String(target)}" is not a text-entry control.`);
|
|
413
|
+
}
|
|
414
|
+
return searchArea.getByRole("textbox", { name: regex });
|
|
415
|
+
}
|
|
416
|
+
//#endregion
|
|
417
|
+
//#region src/features/vitest/modules/dom/utils/userTypes.ts
|
|
418
|
+
/**
|
|
419
|
+
* Replaces the value of a textbox found by its label, placeholder, or textbox name.
|
|
420
|
+
*
|
|
421
|
+
* @param target Accessible label, placeholder, or textbox name as text or a regex.
|
|
422
|
+
* @param text Text to type after clearing the existing value.
|
|
423
|
+
* @param container Optional element that limits the textbox search.
|
|
424
|
+
* @param user User-event instance used for the interaction; defaults to a new instance.
|
|
425
|
+
* @returns A promise resolving to the textbox that was updated.
|
|
426
|
+
* @example
|
|
427
|
+
* ```ts
|
|
428
|
+
* const emailInput = await userTypes("Email", "person@example.com");
|
|
429
|
+
* ```
|
|
430
|
+
*/
|
|
431
|
+
const userTypes = async (target, text, container, user = userEvent.setup()) => {
|
|
432
|
+
const element = getTextBox(target, container);
|
|
433
|
+
await user.clear(element);
|
|
434
|
+
if (text) await user.type(element, text);
|
|
435
|
+
return element;
|
|
436
|
+
};
|
|
437
|
+
//#endregion
|
|
438
|
+
//#region src/features/vitest/modules/dom/utils/userTypesMultiple.ts
|
|
439
|
+
/**
|
|
440
|
+
* Replaces the values of multiple textboxes, located by their respective selectors.
|
|
441
|
+
*
|
|
442
|
+
* @param inputs Object mapping each textbox label/placeholder to the text to enter.
|
|
443
|
+
* @param container Optional element that limits all textbox searches.
|
|
444
|
+
* @returns A promise resolving to an object mapping each selector to its updated textbox.
|
|
445
|
+
* @example
|
|
446
|
+
* ```ts
|
|
447
|
+
* const fields = await userTypesMultiple({ Email: "person@example.com", Name: "Pat" });
|
|
448
|
+
* ```
|
|
449
|
+
*/
|
|
450
|
+
async function userTypesMultiple(inputs, container) {
|
|
451
|
+
const entries = Object.entries(inputs);
|
|
452
|
+
const outputs = {};
|
|
453
|
+
for (const [selector, value] of entries) outputs[selector] = await userTypes(selector, value, container);
|
|
454
|
+
return outputs;
|
|
455
|
+
}
|
|
456
|
+
//#endregion
|
|
457
|
+
export { shouldSee as a, shouldHaveTestId as c, isChecked as d, clickLabelInput as f, togglePopover as i, shouldHaveAttributes as l, userTypes as n, shouldNotSee as o, fireEvent$1 as p, userClicks as r, shouldNotHaveTestId as s, userTypesMultiple as t, isNotChecked as u };
|
|
458
|
+
|
|
459
|
+
//# sourceMappingURL=dom-DeHbD1ni.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dom-DeHbD1ni.mjs","names":["rtlFireEvent","fireEvent"],"sources":["../src/features/vitest/modules/dom/modules/toggle-event/utils/togglePolyfill.ts","../src/features/vitest/modules/dom/modules/toggle-event/utils/fireEvent.ts","../src/shared/regex/utils/escape-regex.ts","../src/shared/regex/utils/pattern-to-regex.ts","../src/features/vitest/modules/dom/utils/getSearchArea.ts","../src/features/vitest/modules/dom/utils/clickLabelInput.ts","../src/features/vitest/modules/dom/utils/getCheckboxRadio.ts","../src/features/vitest/modules/dom/utils/isChecked.ts","../src/features/vitest/modules/dom/utils/isNotChecked.ts","../src/features/vitest/modules/dom/utils/shouldHaveAttributes.ts","../src/features/vitest/modules/dom/utils/shouldHaveTestId.ts","../src/features/vitest/modules/dom/utils/shouldNotHaveTestId.ts","../src/features/vitest/modules/dom/utils/shouldGetByText.ts","../src/features/vitest/modules/dom/utils/shouldNotSee.ts","../src/features/vitest/modules/dom/utils/shouldSee.ts","../src/features/vitest/modules/dom/utils/togglePopover.ts","../src/features/vitest/modules/dom/utils/userClicks.ts","../src/features/vitest/modules/dom/utils/getTextBox.ts","../src/features/vitest/modules/dom/utils/userTypes.ts","../src/features/vitest/modules/dom/utils/userTypesMultiple.ts"],"sourcesContent":["import { fireEvent } from \"@testing-library/react\";\nimport type { ToggleConfig } from \"./types\";\n\n/**\n * Creates the browser ToggleEvent instance used by the fireEvent.toggle polyfill.\n *\n * @param type - Event type to dispatch.\n * @param initDict - Optional toggle event initialization state.\n * @returns A ToggleEvent matching the requested state.\n * @example\n * ```ts\n * const event = createToggleEvent(\"toggle\", {\n * oldState: \"closed\",\n * newState: \"open\",\n * });\n * ```\n */\nexport function createToggleEvent(\n type: string,\n initDict: Partial<ToggleConfig> = {\n oldState: \"closed\",\n newState: \"open\",\n },\n): ToggleEvent {\n if (typeof globalThis.ToggleEvent === \"undefined\") {\n class ToggleEventPolyfill extends Event {\n oldState: string;\n newState: string;\n source: Element | null = null;\n\n constructor(eventType: string, eventInitDict: ToggleEventInit = {}) {\n super(eventType, eventInitDict);\n this.oldState = (eventInitDict as Partial<ToggleConfig>).oldState ?? \"closed\";\n this.newState = (eventInitDict as Partial<ToggleConfig>).newState ?? \"open\";\n }\n }\n\n globalThis.ToggleEvent = ToggleEventPolyfill as typeof ToggleEvent;\n }\n\n return new globalThis.ToggleEvent(type, {\n bubbles: false,\n cancelable: false,\n ...initDict,\n });\n}\n\n/**\n * Polyfills `fireEvent.toggle` for tests by dispatching a native ToggleEvent.\n *\n * @param element - Element or window to dispatch the toggle event on.\n * @param initDict - Optional state values for the toggle transition.\n * @returns Whether the event dispatch completed successfully.\n * @example\n * ```ts\n * fireEvent.toggle(document.body, { newState: \"open\" });\n * ```\n */\nexport function togglePolyfill(\n element: Document | Element | Node | Window,\n initDict: Partial<ToggleConfig> = {\n oldState: \"closed\",\n newState: \"open\",\n },\n): boolean {\n const event = createToggleEvent(\"toggle\", initDict);\n return fireEvent(element, event);\n}\n","import { fireEvent as rtlFireEvent } from \"@testing-library/react\";\nimport { togglePolyfill } from \"./togglePolyfill\";\nimport type { ToggleConfig, ToggleFireEventExtensions } from \"./types\";\n\nconst callableFireEvent = (...args: Parameters<typeof rtlFireEvent>) =>\n rtlFireEvent(...args);\n/**\n * Testing Library's fireEvent API with an additional `toggle` event helper.\n *\n * The exported object copies the standard `fireEvent` properties and adds\n * `toggle`, which dispatches the toggle event through `togglePolyfill`.\n * It does not install the extension onto Testing Library's original export.\n *\n * Standard Testing Library event helpers remain available, including\n * `click`, `change`, `input`, `submit`, `focus`, and keyboard/mouse events.\n *\n * @remarks\n * The `toggle` helper defaults to a transition from `closed` to `open`.\n * Call-specific configuration overrides those defaults. This helper does\n * not maintain state between calls.\n *\n * @example\n * ```ts\n * import { fireEvent } from \"@vickbk/tests/vitest\";\n *\n * const disclosure = document.querySelector(\"details\")!;\n *\n * fireEvent.toggle(disclosure);\n * fireEvent.toggle(disclosure, { newState: \"closed\" });\n * ```\n *\n * @example\n * ```ts\n * import { fireEvent } from \"@vickbk/tests/vitest\";\n *\n * const button = document.querySelector(\"button\")!;\n *\n * fireEvent.click(button);\n * ```\n */\nexport const fireEvent = Object.assign(callableFireEvent, rtlFireEvent, {\n /**\n * Dispatches a toggle event using the toggle polyfill.\n *\n * @param element - The document, element, node, or window receiving the event.\n * @param initDict - Optional old/new state overrides for this dispatch.\n * @returns The result returned by `togglePolyfill`.\n *\n * @example\n * ```ts\n * fireEvent.toggle(document.body, {\n * oldState: \"closed\",\n * newState: \"open\",\n * });\n * ```\n */\n toggle: (\n element: Document | Element | Node | Window,\n initDict?: Partial<ToggleConfig>,\n ) =>\n togglePolyfill(element, {\n oldState: \"closed\",\n newState: \"open\",\n ...initDict,\n }),\n}) as typeof rtlFireEvent & ToggleFireEventExtensions;\n","export function escapeRegExp(str: string): string {\n if (typeof RegExp.escape === \"function\") {\n return RegExp.escape(str);\n }\n return str.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n}\n","import { TEXT_PATTERN } from \"../types\";\nimport { escapeRegExp } from \"./escape-regex\";\n\nexport function patternToRegex(\n target: TEXT_PATTERN,\n flags: string = \"i\",\n): RegExp {\n return typeof target === \"string\"\n ? new RegExp(escapeRegExp(target), flags)\n : target;\n}\n","import { screen, within } from \"@testing-library/react\";\n\n/**\n * Returns the Testing Library query API for a container or the whole document.\n *\n * @param container Optional element to scope queries to.\n * @returns The scoped query API, or the global `screen` query API.\n * @example\n * ```ts\n * const queries = getSearchArea(formElement);\n * queries.getByRole(\"button\", { name: \"Submit\" });\n * ```\n */\nexport function getSearchArea(container?: HTMLElement) {\n return container ? within(container) : screen;\n}\n","import type { TEXT_PATTERN } from \"@/shared/regex\";\nimport { patternToRegex } from \"@/shared/regex\";\nimport userEvent from \"@testing-library/user-event\";\nimport { getSearchArea } from \"./getSearchArea\";\n\n/**\n * Simulates clicking an input found by its label or placeholder.\n *\n * @param label Label or placeholder text/regular expression identifying the input.\n * @param container Optional element that limits the input search.\n * @returns A promise that resolves after the click interaction completes.\n * @example\n * ```ts\n * await clickLabelInput(\"Email address\");\n * ```\n */\nexport async function clickLabelInput(\n label: TEXT_PATTERN,\n container?: HTMLElement,\n) {\n const user = userEvent.setup();\n const regex = patternToRegex(label);\n const searchArea = getSearchArea(container);\n const element =\n typeof label === \"string\" && label.length === 0\n ? searchArea.queryByPlaceholderText(regex) ||\n searchArea.getByLabelText(regex)\n : searchArea.queryByLabelText(regex) ||\n searchArea.getByPlaceholderText(regex);\n await user.click(element);\n}\n","import type { TEXT_PATTERN } from \"@/shared/regex\";\nimport { patternToRegex } from \"@/shared/regex\";\nimport { getSearchArea } from \"./getSearchArea\";\n\n/**\n * Finds a checkbox or radio control by its accessible name.\n *\n * @param target Accessible name to match, as text or a regular expression.\n * @param container Optional element that limits the search.\n * @returns The matching checkbox or radio input.\n * @throws If neither a checkbox nor a radio control matches the target.\n * @example\n * ```ts\n * const consent = getCheckboxRadio(\"Accept terms\");\n * expect(consent).toBeChecked();\n * ```\n */\nexport function getCheckboxRadio(\n target: TEXT_PATTERN,\n container?: HTMLElement,\n) {\n const regex = patternToRegex(target);\n const searchArea = getSearchArea(container);\n const element =\n searchArea.queryByRole(\"checkbox\", { name: regex }) ||\n searchArea.queryByRole(\"radio\", { name: regex });\n\n if (element) return element;\n\n throw new Error(\n `Unable to find a checkbox or radio control named \"${String(target)}\".`,\n );\n}\n","import type { TEXT_PATTERN } from \"@/shared/regex\";\nimport { expect } from \"vitest\";\nimport { getCheckboxRadio } from \"./getCheckboxRadio\";\n\n/**\n * Asserts that the radio button or checkbox matching the accessible name is checked.\n *\n * @param selector Accessible label text or regular expression for the control.\n * @param container Optional element that limits the control search.\n * @returns Nothing; throws an assertion error if the control is absent or unchecked.\n * @example\n * ```ts\n * isChecked(\"Accept terms\");\n * ```\n */\nexport function isChecked(selector: TEXT_PATTERN, container?: HTMLElement) {\n expect(getCheckboxRadio(selector, container)).toBeChecked();\n}\n","import type { TEXT_PATTERN } from \"@/shared/regex\";\nimport { expect } from \"vitest\";\nimport { getCheckboxRadio } from \"./getCheckboxRadio\";\n\n/**\n * Asserts that the radio button or checkbox matching the accessible name is unchecked.\n *\n * @param selector Accessible label text or regular expression for the control.\n * @param container Optional element that limits the control search.\n * @returns Nothing; throws an assertion error if the control is absent or checked.\n * @example\n * ```ts\n * isNotChecked(\"Subscribe to updates\");\n * ```\n */\nexport function isNotChecked(selector: TEXT_PATTERN, container?: HTMLElement) {\n expect(getCheckboxRadio(selector, container)).not.toBeChecked();\n}\n","import { expect } from \"vitest\";\n\n/**\n * Asserts that an HTMLElement possesses the expected set of DOM attributes and values.\n *\n * Handles string/numeric values, boolean attributes (true = present, false = absent),\n * and null/undefined values (absent).\n *\n * @param element The DOM element whose attributes will be checked.\n * @param attributes Attribute names mapped to their expected values.\n * @returns Nothing; throws an assertion error when any attribute does not match.\n * @example\n * ```ts\n * shouldHaveAttributes(input, { required: true, \"aria-label\": \"Email\" });\n * ```\n */\nexport function shouldHaveAttributes(\n element: HTMLElement,\n attributes: Record<string, unknown>,\n): void {\n Object.entries(attributes).forEach(([key, value]) => {\n if (value === null || value === undefined || value === false) {\n expect(element).not.toHaveAttribute(key);\n } else if (value === true) {\n expect(element).toHaveAttribute(key);\n } else {\n expect(element).toHaveAttribute(key, String(value));\n }\n });\n}\n","import { screen } from \"@testing-library/react\";\nimport { expect } from \"vitest\";\n\n/**\n * Asserts that elements with all supplied test IDs are present in the document.\n *\n * @param ids One or more `data-testid` values to locate.\n * @returns The elements found, in the same order as the supplied IDs.\n * @example\n * ```ts\n * const [submitButton] = shouldHaveTestId(\"submit-button\");\n * ```\n */\nexport function shouldHaveTestId(...ids: string[]) {\n const testElements = ids.map((id) => screen.getByTestId(id));\n\n testElements.forEach((element) => expect(element).toBeInTheDocument());\n return testElements;\n}\n","import { screen } from \"@testing-library/react\";\nimport { expect } from \"vitest\";\n\n/**\n * Asserts that elements with all supplied test IDs are absent from the document.\n *\n * @param ids One or more `data-testid` values to verify are absent.\n * @returns The query results (`null` for each absent element), in ID order.\n * @example\n * ```ts\n * shouldNotHaveTestId(\"loading-spinner\");\n * ```\n */\nexport function shouldNotHaveTestId(...ids: string[]) {\n const testElements = ids.map((id) => screen.queryByTestId(id));\n\n testElements.forEach((element) => expect(element).not.toBeInTheDocument());\n return testElements;\n}\n","import type { TEXT_MATCHER } from \"@/shared/regex\";\nimport { patternToRegex } from \"@/shared/regex\";\nimport { screen } from \"@testing-library/react\";\nimport { expect } from \"vitest\";\n\n/**\n * Creates assertion/element pairs for text matchers, supporting a zero-based occurrence index.\n *\n * @param texts Text strings, regular expressions, or `[matcher, occurrenceIndex]` pairs.\n * @returns Assertion and matching-element pairs, with `null` for missing text.\n * @example\n * ```ts\n * const [{ expected, element }] = shouldGetByText([\"Item\", 1]);\n * ```\n */\nexport function shouldGetByText(...texts: TEXT_MATCHER[]) {\n return texts.map((text) => {\n if (Array.isArray(text)) {\n const [matcher, nth] = text;\n const element =\n screen.queryAllByText(patternToRegex(matcher))[nth] ?? null;\n return { expected: expect(element), element };\n }\n const element = screen.queryByText(patternToRegex(text));\n return { expected: expect(element), element };\n });\n}\n","import { shouldGetByText } from \"./shouldGetByText\";\n\n/**\n * Asserts that none of the supplied text matchers appear in the document.\n *\n * @param texts Text strings or regular expressions that must be absent.\n * @returns Nothing; throws an assertion error if any text is found.\n * @example\n * ```ts\n * shouldNotSee(\"Error\", /failed/i);\n * ```\n */\nexport const shouldNotSee = (...texts: (string | RegExp)[]) => {\n shouldGetByText(...texts).forEach(({ expected }) =>\n expected.not.toBeInTheDocument(),\n );\n};\n","import type { TEXT_MATCHER } from \"@/shared/regex\";\nimport { shouldGetByText } from \"./shouldGetByText\";\n\n/**\n * Asserts that each requested text matcher is present in the document.\n *\n * @param texts Text strings, regular expressions, or `[matcher, occurrenceIndex]` pairs.\n * @returns The matching elements in the same order as the supplied matchers.\n * @example\n * ```ts\n * const [heading] = shouldSee(\"Welcome\");\n * ```\n */\nexport const shouldSee = (...texts: TEXT_MATCHER[]) => {\n return shouldGetByText(...texts).map(({ expected, element }) => {\n expected.toBeInTheDocument();\n return element!;\n });\n};\n","import { fireEvent, type ToggleState } from \"../modules/toggle-event\";\n\n/**\n * Dispatches a toggle event on the first element matching a selector.\n *\n * @param options Selector, optional search container, and target open/closed state.\n * @returns The matched element, or `undefined` when no matching element exists.\n * @example\n * ```ts\n * const popover = togglePopover({ selector: \"#help\", newState: \"open\" });\n * ```\n */\nexport function togglePopover({\n container,\n selector,\n newState = \"open\",\n}: {\n container?: HTMLElement;\n selector: string;\n newState?: ToggleState;\n}) {\n const element = (container ?? document).querySelector(selector);\n if (!element) return;\n fireEvent.toggle(element, { newState });\n return element;\n}\n","import { patternToRegex } from \"@/shared/regex\";\nimport { screen, within } from \"@testing-library/react\";\nimport userEvent from \"@testing-library/user-event\";\n\n/**\n * Simulates a user click on a button with the requested name, or matching text.\n *\n * @param target Button accessible name or text string/regular expression.\n * @param container Optional element that limits the target search.\n * @returns A promise that resolves after the click interaction completes.\n * @example\n * ```ts\n * await userClicks(\"Save\");\n * ```\n */\nexport const userClicks = async (\n target: string | RegExp,\n container?: HTMLElement,\n) => {\n const user = userEvent.setup();\n const regex = patternToRegex(target);\n const searchArea = container ? within(container) : screen;\n const element =\n searchArea.queryByRole(\"button\", { name: regex }) ||\n searchArea.getByText(regex);\n\n await user.click(element);\n};\n","import { patternToRegex, TEXT_PATTERN } from \"@/shared/regex\";\nimport { getSearchArea } from \"./getSearchArea\";\n\n/**\n * Finds a text-entry control by its accessible name or placeholder.\n *\n * Supported controls are text-like inputs, textareas, and elements exposed\n * with the textbox role. A matching non-text element is rejected.\n *\n * @param target Accessible name or placeholder to match, as text or a regular expression.\n * @param container Optional element that limits the search.\n * @returns The matching textbox element.\n * @throws If no textbox matches, or a placeholder match is not a text-entry control.\n * @example\n * ```ts\n * const email = getTextBox(\"Email address\");\n * const notes = getTextBox(\"Notes\", dialog);\n * ```\n */\nexport function getTextBox(target: TEXT_PATTERN, container?: HTMLElement) {\n const regex = patternToRegex(target);\n const searchArea = getSearchArea(container);\n const textBox = searchArea.queryByRole(\"textbox\", { name: regex });\n\n if (textBox) return textBox;\n\n const placeholderMatch = searchArea.queryByPlaceholderText(regex);\n if (placeholderMatch) {\n const isTextInput =\n placeholderMatch instanceof HTMLInputElement &&\n [\"text\", \"search\", \"email\", \"url\", \"tel\", \"password\"].includes(\n placeholderMatch.type,\n );\n const isTextArea = placeholderMatch instanceof HTMLTextAreaElement;\n const isContentEditable =\n placeholderMatch.getAttribute(\"contenteditable\") === \"true\" &&\n placeholderMatch.getAttribute(\"role\") === \"textbox\";\n\n if (isTextInput || isTextArea || isContentEditable) return placeholderMatch;\n\n throw new Error(\n `Element matching placeholder \"${String(target)}\" is not a text-entry control.`,\n );\n }\n\n return searchArea.getByRole(\"textbox\", { name: regex });\n}\n","import { TEXT_PATTERN } from \"@/shared/regex\";\nimport userEvent from \"@testing-library/user-event\";\nimport { getTextBox } from \"./getTextBox\";\n\n/**\n * Replaces the value of a textbox found by its label, placeholder, or textbox name.\n *\n * @param target Accessible label, placeholder, or textbox name as text or a regex.\n * @param text Text to type after clearing the existing value.\n * @param container Optional element that limits the textbox search.\n * @param user User-event instance used for the interaction; defaults to a new instance.\n * @returns A promise resolving to the textbox that was updated.\n * @example\n * ```ts\n * const emailInput = await userTypes(\"Email\", \"person@example.com\");\n * ```\n */\nexport const userTypes = async (\n target: TEXT_PATTERN,\n text: string,\n container?: HTMLElement,\n user = userEvent.setup(),\n) => {\n const element = getTextBox(target, container);\n\n await user.clear(element);\n if (text) await user.type(element, text);\n return element;\n};\n","import { userTypes } from \"./userTypes\";\n\n/**\n * Replaces the values of multiple textboxes, located by their respective selectors.\n *\n * @param inputs Object mapping each textbox label/placeholder to the text to enter.\n * @param container Optional element that limits all textbox searches.\n * @returns A promise resolving to an object mapping each selector to its updated textbox.\n * @example\n * ```ts\n * const fields = await userTypesMultiple({ Email: \"person@example.com\", Name: \"Pat\" });\n * ```\n */\nexport async function userTypesMultiple(\n inputs: Record<string, string>,\n container?: HTMLElement,\n) {\n const entries = Object.entries(inputs);\n const outputs: Record<keyof typeof inputs, HTMLElement> = {};\n\n for (const [selector, value] of entries) {\n outputs[selector] = await userTypes(selector, value, container);\n }\n\n return outputs;\n}\n"],"mappings":"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"}
|