@fictjs/testing-library 0.2.2 → 0.3.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/dist/index.cjs +502 -0
- package/dist/index.d.cts +491 -1
- package/dist/index.d.ts +491 -1
- package/dist/index.js +472 -0
- package/package.json +26 -3
package/dist/index.cjs
CHANGED
|
@@ -1 +1,503 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __reExport = (target, mod, secondTarget) => (__copyProps(target, mod, "default"), secondTarget && __copyProps(secondTarget, mod, "default"));
|
|
19
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
20
|
+
|
|
21
|
+
// src/index.ts
|
|
22
|
+
var index_exports = {};
|
|
23
|
+
__export(index_exports, {
|
|
24
|
+
act: () => act,
|
|
25
|
+
cleanup: () => cleanup,
|
|
26
|
+
createTestSuspenseToken: () => createTestSuspenseToken,
|
|
27
|
+
flush: () => flush,
|
|
28
|
+
prettyDOM: () => import_dom.prettyDOM,
|
|
29
|
+
queries: () => import_dom.queries,
|
|
30
|
+
render: () => render,
|
|
31
|
+
renderHook: () => renderHook,
|
|
32
|
+
renderWithErrorBoundary: () => renderWithErrorBoundary,
|
|
33
|
+
renderWithSuspense: () => renderWithSuspense,
|
|
34
|
+
testEffect: () => testEffect,
|
|
35
|
+
waitForCondition: () => waitForCondition
|
|
36
|
+
});
|
|
37
|
+
module.exports = __toCommonJS(index_exports);
|
|
38
|
+
var import_runtime = require("@fictjs/runtime");
|
|
39
|
+
var import_advanced = require("@fictjs/runtime/advanced");
|
|
40
|
+
var import_internal = require("@fictjs/runtime/internal");
|
|
41
|
+
var import_dom = require("@testing-library/dom");
|
|
42
|
+
__reExport(index_exports, require("@testing-library/dom"), module.exports);
|
|
43
|
+
var mountedContainers = /* @__PURE__ */ new Set();
|
|
44
|
+
var mountedHookRoots = /* @__PURE__ */ new Set();
|
|
45
|
+
if (typeof process === "undefined" || !process.env?.FICT_TL_SKIP_AUTO_CLEANUP) {
|
|
46
|
+
const globalAfterEach = globalThis.afterEach;
|
|
47
|
+
if (typeof globalAfterEach === "function") {
|
|
48
|
+
globalAfterEach(() => {
|
|
49
|
+
cleanup();
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
function render(view, options = {}) {
|
|
54
|
+
const {
|
|
55
|
+
container: providedContainer,
|
|
56
|
+
baseElement: providedBaseElement,
|
|
57
|
+
queries: customQueries,
|
|
58
|
+
wrapper
|
|
59
|
+
} = options;
|
|
60
|
+
let container = providedContainer;
|
|
61
|
+
let baseElement = providedBaseElement;
|
|
62
|
+
let ownedContainer = false;
|
|
63
|
+
if (!baseElement) {
|
|
64
|
+
baseElement = container ?? document.body;
|
|
65
|
+
}
|
|
66
|
+
if (!container) {
|
|
67
|
+
container = baseElement.appendChild(document.createElement("div"));
|
|
68
|
+
ownedContainer = true;
|
|
69
|
+
}
|
|
70
|
+
let wrappedView = view;
|
|
71
|
+
if (wrapper) {
|
|
72
|
+
const Wrapper = wrapper;
|
|
73
|
+
wrappedView = () => {
|
|
74
|
+
const children = view();
|
|
75
|
+
return (0, import_runtime.createElement)({ type: Wrapper, props: { children } });
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
let currentTeardown = null;
|
|
79
|
+
const teardown = (0, import_runtime.render)(wrappedView, container);
|
|
80
|
+
currentTeardown = teardown;
|
|
81
|
+
const ref = { container, baseElement, ownedContainer, teardown };
|
|
82
|
+
mountedContainers.add(ref);
|
|
83
|
+
const queryHelpers = (0, import_dom.getQueriesForElement)(container, customQueries);
|
|
84
|
+
const debug = (el = baseElement, maxLength, debugOptions) => {
|
|
85
|
+
if (Array.isArray(el)) {
|
|
86
|
+
el.forEach((e) => console.log((0, import_dom.prettyDOM)(e, maxLength, debugOptions)));
|
|
87
|
+
} else {
|
|
88
|
+
console.log((0, import_dom.prettyDOM)(el, maxLength, debugOptions));
|
|
89
|
+
}
|
|
90
|
+
};
|
|
91
|
+
const unmount = () => {
|
|
92
|
+
if (currentTeardown) {
|
|
93
|
+
currentTeardown();
|
|
94
|
+
currentTeardown = null;
|
|
95
|
+
}
|
|
96
|
+
if (ownedContainer && container?.parentNode) {
|
|
97
|
+
container.parentNode.removeChild(container);
|
|
98
|
+
}
|
|
99
|
+
mountedContainers.delete(ref);
|
|
100
|
+
};
|
|
101
|
+
const rerender = (newView) => {
|
|
102
|
+
if (currentTeardown) {
|
|
103
|
+
currentTeardown();
|
|
104
|
+
}
|
|
105
|
+
let wrappedNewView = newView;
|
|
106
|
+
if (wrapper) {
|
|
107
|
+
const Wrapper = wrapper;
|
|
108
|
+
wrappedNewView = () => {
|
|
109
|
+
const children = newView();
|
|
110
|
+
return (0, import_runtime.createElement)({ type: Wrapper, props: { children } });
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
currentTeardown = (0, import_runtime.render)(wrappedNewView, container);
|
|
114
|
+
ref.teardown = currentTeardown;
|
|
115
|
+
};
|
|
116
|
+
return {
|
|
117
|
+
asFragment: () => container?.innerHTML ?? "",
|
|
118
|
+
container,
|
|
119
|
+
baseElement,
|
|
120
|
+
debug,
|
|
121
|
+
unmount,
|
|
122
|
+
rerender,
|
|
123
|
+
...queryHelpers
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
function cleanupAtContainer(ref) {
|
|
127
|
+
const { container, teardown, ownedContainer } = ref;
|
|
128
|
+
if (typeof teardown === "function") {
|
|
129
|
+
teardown();
|
|
130
|
+
} else if (typeof teardown !== "undefined") {
|
|
131
|
+
console.warn(
|
|
132
|
+
"[@fictjs/testing-library] Expected teardown to be a function. This might indicate a version mismatch between @fictjs/runtime and @fictjs/testing-library."
|
|
133
|
+
);
|
|
134
|
+
}
|
|
135
|
+
if (ownedContainer && container?.parentNode) {
|
|
136
|
+
container.parentNode.removeChild(container);
|
|
137
|
+
}
|
|
138
|
+
mountedContainers.delete(ref);
|
|
139
|
+
}
|
|
140
|
+
function cleanup() {
|
|
141
|
+
mountedContainers.forEach(cleanupAtContainer);
|
|
142
|
+
mountedHookRoots.forEach((dispose) => {
|
|
143
|
+
try {
|
|
144
|
+
dispose();
|
|
145
|
+
} catch (err) {
|
|
146
|
+
console.error("[fict/testing-library] Error during hook cleanup:", err);
|
|
147
|
+
}
|
|
148
|
+
});
|
|
149
|
+
mountedHookRoots.clear();
|
|
150
|
+
}
|
|
151
|
+
function renderHook(hookFn, options = {}) {
|
|
152
|
+
let initialProps;
|
|
153
|
+
let wrapper;
|
|
154
|
+
if (Array.isArray(options)) {
|
|
155
|
+
initialProps = options;
|
|
156
|
+
} else {
|
|
157
|
+
initialProps = options.initialProps;
|
|
158
|
+
wrapper = options.wrapper;
|
|
159
|
+
}
|
|
160
|
+
const resultContainer = { current: void 0 };
|
|
161
|
+
let currentProps = initialProps ?? [];
|
|
162
|
+
let disposeRoot = null;
|
|
163
|
+
let registeredDispose = null;
|
|
164
|
+
const registerDispose = (dispose) => {
|
|
165
|
+
if (registeredDispose) {
|
|
166
|
+
mountedHookRoots.delete(registeredDispose);
|
|
167
|
+
}
|
|
168
|
+
registeredDispose = dispose;
|
|
169
|
+
mountedHookRoots.add(dispose);
|
|
170
|
+
};
|
|
171
|
+
const executeHook = () => {
|
|
172
|
+
const { dispose, value } = (0, import_runtime.createRoot)(() => {
|
|
173
|
+
let hookResult;
|
|
174
|
+
if (wrapper) {
|
|
175
|
+
const Wrapper = wrapper;
|
|
176
|
+
(0, import_runtime.createElement)({
|
|
177
|
+
type: Wrapper,
|
|
178
|
+
props: {
|
|
179
|
+
children: (() => {
|
|
180
|
+
(0, import_internal.__fictPushContext)();
|
|
181
|
+
try {
|
|
182
|
+
hookResult = hookFn(...currentProps);
|
|
183
|
+
} finally {
|
|
184
|
+
(0, import_internal.__fictPopContext)();
|
|
185
|
+
}
|
|
186
|
+
return null;
|
|
187
|
+
})()
|
|
188
|
+
}
|
|
189
|
+
});
|
|
190
|
+
} else {
|
|
191
|
+
(0, import_internal.__fictPushContext)();
|
|
192
|
+
try {
|
|
193
|
+
hookResult = hookFn(...currentProps);
|
|
194
|
+
} finally {
|
|
195
|
+
(0, import_internal.__fictPopContext)();
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
return hookResult;
|
|
199
|
+
});
|
|
200
|
+
disposeRoot = dispose;
|
|
201
|
+
registerDispose(dispose);
|
|
202
|
+
resultContainer.current = value;
|
|
203
|
+
return value;
|
|
204
|
+
};
|
|
205
|
+
executeHook();
|
|
206
|
+
const rerender = (newProps) => {
|
|
207
|
+
if (disposeRoot) {
|
|
208
|
+
disposeRoot();
|
|
209
|
+
}
|
|
210
|
+
if (newProps !== void 0) {
|
|
211
|
+
currentProps = newProps;
|
|
212
|
+
}
|
|
213
|
+
executeHook();
|
|
214
|
+
};
|
|
215
|
+
const cleanupHook = () => {
|
|
216
|
+
if (disposeRoot) {
|
|
217
|
+
disposeRoot();
|
|
218
|
+
disposeRoot = null;
|
|
219
|
+
}
|
|
220
|
+
if (registeredDispose) {
|
|
221
|
+
mountedHookRoots.delete(registeredDispose);
|
|
222
|
+
registeredDispose = null;
|
|
223
|
+
}
|
|
224
|
+
};
|
|
225
|
+
return {
|
|
226
|
+
result: resultContainer,
|
|
227
|
+
rerender,
|
|
228
|
+
cleanup: cleanupHook,
|
|
229
|
+
unmount: cleanupHook
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
function testEffect(fn) {
|
|
233
|
+
return new Promise((resolve, reject) => {
|
|
234
|
+
let settled = false;
|
|
235
|
+
let resolveScheduled = false;
|
|
236
|
+
let disposed = false;
|
|
237
|
+
const disposeRef = { current: null };
|
|
238
|
+
const scheduleDispose = () => {
|
|
239
|
+
if (disposed) return;
|
|
240
|
+
queueMicrotask(() => {
|
|
241
|
+
if (disposed) return;
|
|
242
|
+
disposed = true;
|
|
243
|
+
disposeRef.current?.();
|
|
244
|
+
});
|
|
245
|
+
};
|
|
246
|
+
const reportError = (err) => {
|
|
247
|
+
if (settled) {
|
|
248
|
+
queueMicrotask(() => {
|
|
249
|
+
throw err;
|
|
250
|
+
});
|
|
251
|
+
return;
|
|
252
|
+
}
|
|
253
|
+
settled = true;
|
|
254
|
+
reject(err);
|
|
255
|
+
scheduleDispose();
|
|
256
|
+
};
|
|
257
|
+
const scheduleResolve = (result) => {
|
|
258
|
+
if (settled || resolveScheduled) return;
|
|
259
|
+
resolveScheduled = true;
|
|
260
|
+
queueMicrotask(() => {
|
|
261
|
+
if (settled) return;
|
|
262
|
+
settled = true;
|
|
263
|
+
resolve(result);
|
|
264
|
+
scheduleDispose();
|
|
265
|
+
});
|
|
266
|
+
};
|
|
267
|
+
const root = (0, import_runtime.createRoot)(() => {
|
|
268
|
+
(0, import_advanced.registerErrorHandler)((err) => {
|
|
269
|
+
reportError(err);
|
|
270
|
+
return true;
|
|
271
|
+
});
|
|
272
|
+
try {
|
|
273
|
+
fn((result) => {
|
|
274
|
+
scheduleResolve(result);
|
|
275
|
+
});
|
|
276
|
+
} catch (err) {
|
|
277
|
+
reportError(err);
|
|
278
|
+
}
|
|
279
|
+
});
|
|
280
|
+
disposeRef.current = root.dispose;
|
|
281
|
+
});
|
|
282
|
+
}
|
|
283
|
+
function waitForCondition(condition, options = {}) {
|
|
284
|
+
const { timeout = 1e3, interval = 50 } = options;
|
|
285
|
+
return new Promise((resolve, reject) => {
|
|
286
|
+
const startTime = Date.now();
|
|
287
|
+
const check = () => {
|
|
288
|
+
if (condition()) {
|
|
289
|
+
resolve();
|
|
290
|
+
return;
|
|
291
|
+
}
|
|
292
|
+
if (Date.now() - startTime >= timeout) {
|
|
293
|
+
reject(new Error(`waitForCondition timed out after ${timeout}ms`));
|
|
294
|
+
return;
|
|
295
|
+
}
|
|
296
|
+
setTimeout(check, interval);
|
|
297
|
+
};
|
|
298
|
+
check();
|
|
299
|
+
});
|
|
300
|
+
}
|
|
301
|
+
function flush() {
|
|
302
|
+
return new Promise((resolve) => queueMicrotask(resolve));
|
|
303
|
+
}
|
|
304
|
+
async function act(fn) {
|
|
305
|
+
try {
|
|
306
|
+
const result = await fn();
|
|
307
|
+
await flush();
|
|
308
|
+
await flush();
|
|
309
|
+
return result;
|
|
310
|
+
} catch (err) {
|
|
311
|
+
await flush();
|
|
312
|
+
await flush();
|
|
313
|
+
throw err;
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
function renderWithErrorBoundary(view, options = {}) {
|
|
317
|
+
const {
|
|
318
|
+
fallback = (0, import_runtime.createElement)({
|
|
319
|
+
type: "div",
|
|
320
|
+
props: { "data-testid": "error-fallback", children: "Error occurred" }
|
|
321
|
+
}),
|
|
322
|
+
onError,
|
|
323
|
+
resetKeys,
|
|
324
|
+
...renderOptions
|
|
325
|
+
} = options;
|
|
326
|
+
let hasError = false;
|
|
327
|
+
let resetFn;
|
|
328
|
+
let _currentError = null;
|
|
329
|
+
const wrappedFallback = (err, reset) => {
|
|
330
|
+
hasError = true;
|
|
331
|
+
_currentError = err;
|
|
332
|
+
resetFn = reset;
|
|
333
|
+
if (typeof fallback === "function") {
|
|
334
|
+
return fallback(err, reset);
|
|
335
|
+
}
|
|
336
|
+
return fallback;
|
|
337
|
+
};
|
|
338
|
+
const ErrorBoundaryComponent = import_runtime.ErrorBoundary;
|
|
339
|
+
const wrapView = (nextView) => {
|
|
340
|
+
return () => {
|
|
341
|
+
const ViewComponent = () => nextView();
|
|
342
|
+
return (0, import_runtime.createElement)({
|
|
343
|
+
type: ErrorBoundaryComponent,
|
|
344
|
+
props: {
|
|
345
|
+
fallback: wrappedFallback,
|
|
346
|
+
onError: (err) => {
|
|
347
|
+
hasError = true;
|
|
348
|
+
_currentError = err;
|
|
349
|
+
onError?.(err);
|
|
350
|
+
},
|
|
351
|
+
resetKeys,
|
|
352
|
+
children: {
|
|
353
|
+
type: ViewComponent,
|
|
354
|
+
props: {},
|
|
355
|
+
key: void 0
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
});
|
|
359
|
+
};
|
|
360
|
+
};
|
|
361
|
+
const result = render(wrapView(view), renderOptions);
|
|
362
|
+
return {
|
|
363
|
+
...result,
|
|
364
|
+
triggerError: (error) => {
|
|
365
|
+
hasError = true;
|
|
366
|
+
_currentError = error;
|
|
367
|
+
const Thrower = () => {
|
|
368
|
+
throw error;
|
|
369
|
+
};
|
|
370
|
+
result.rerender(
|
|
371
|
+
wrapView(() => ({
|
|
372
|
+
type: Thrower,
|
|
373
|
+
props: {},
|
|
374
|
+
key: void 0
|
|
375
|
+
}))
|
|
376
|
+
);
|
|
377
|
+
},
|
|
378
|
+
resetErrorBoundary: () => {
|
|
379
|
+
hasError = false;
|
|
380
|
+
_currentError = null;
|
|
381
|
+
if (resetFn) {
|
|
382
|
+
try {
|
|
383
|
+
resetFn();
|
|
384
|
+
} catch (err) {
|
|
385
|
+
onError?.(err);
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
},
|
|
389
|
+
isShowingFallback: () => hasError,
|
|
390
|
+
rerender: (nextView) => {
|
|
391
|
+
hasError = false;
|
|
392
|
+
_currentError = null;
|
|
393
|
+
resetFn = void 0;
|
|
394
|
+
result.rerender(wrapView(nextView));
|
|
395
|
+
}
|
|
396
|
+
};
|
|
397
|
+
}
|
|
398
|
+
function createTestSuspenseToken() {
|
|
399
|
+
const handle = (0, import_runtime.createSuspenseToken)();
|
|
400
|
+
return {
|
|
401
|
+
// Cast to match the TestSuspenseHandle type
|
|
402
|
+
token: handle.token,
|
|
403
|
+
resolve: handle.resolve,
|
|
404
|
+
reject: handle.reject
|
|
405
|
+
};
|
|
406
|
+
}
|
|
407
|
+
function renderWithSuspense(view, options = {}) {
|
|
408
|
+
const {
|
|
409
|
+
fallback = (0, import_runtime.createElement)({
|
|
410
|
+
type: "div",
|
|
411
|
+
props: { "data-testid": "suspense-fallback", children: "Loading..." }
|
|
412
|
+
}),
|
|
413
|
+
onResolve,
|
|
414
|
+
onReject,
|
|
415
|
+
resetKeys,
|
|
416
|
+
...renderOptions
|
|
417
|
+
} = options;
|
|
418
|
+
let isSuspended = false;
|
|
419
|
+
let isResolved = true;
|
|
420
|
+
let resolveWaiters = [];
|
|
421
|
+
const wrappedFallback = (err) => {
|
|
422
|
+
isSuspended = true;
|
|
423
|
+
isResolved = false;
|
|
424
|
+
if (typeof fallback === "function") {
|
|
425
|
+
return fallback(err);
|
|
426
|
+
}
|
|
427
|
+
return fallback;
|
|
428
|
+
};
|
|
429
|
+
const SuspenseComponent = import_runtime.Suspense;
|
|
430
|
+
const wrapView = (nextView) => {
|
|
431
|
+
return () => {
|
|
432
|
+
if (onReject) {
|
|
433
|
+
(0, import_advanced.registerErrorHandler)(() => true);
|
|
434
|
+
}
|
|
435
|
+
const ViewComponent = () => nextView();
|
|
436
|
+
return (0, import_runtime.createElement)({
|
|
437
|
+
type: SuspenseComponent,
|
|
438
|
+
props: {
|
|
439
|
+
fallback: wrappedFallback,
|
|
440
|
+
onResolve: () => {
|
|
441
|
+
isSuspended = false;
|
|
442
|
+
isResolved = true;
|
|
443
|
+
onResolve?.();
|
|
444
|
+
resolveWaiters.forEach((waiter) => waiter());
|
|
445
|
+
resolveWaiters = [];
|
|
446
|
+
},
|
|
447
|
+
onReject: (err) => {
|
|
448
|
+
isSuspended = false;
|
|
449
|
+
onReject?.(err);
|
|
450
|
+
},
|
|
451
|
+
resetKeys,
|
|
452
|
+
children: {
|
|
453
|
+
type: ViewComponent,
|
|
454
|
+
props: {},
|
|
455
|
+
key: void 0
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
});
|
|
459
|
+
};
|
|
460
|
+
};
|
|
461
|
+
const result = render(wrapView(view), renderOptions);
|
|
462
|
+
return {
|
|
463
|
+
...result,
|
|
464
|
+
isShowingFallback: () => {
|
|
465
|
+
const fallbackEl = result.container.querySelector('[data-testid="suspense-fallback"]');
|
|
466
|
+
return !!fallbackEl || isSuspended;
|
|
467
|
+
},
|
|
468
|
+
waitForResolution: (waitOptions) => {
|
|
469
|
+
const { timeout = 5e3 } = waitOptions ?? {};
|
|
470
|
+
if (isResolved && !isSuspended) {
|
|
471
|
+
return Promise.resolve();
|
|
472
|
+
}
|
|
473
|
+
return new Promise((resolve, reject) => {
|
|
474
|
+
const timeoutId = setTimeout(() => {
|
|
475
|
+
reject(new Error(`Suspense did not resolve within ${timeout}ms`));
|
|
476
|
+
}, timeout);
|
|
477
|
+
resolveWaiters.push(() => {
|
|
478
|
+
clearTimeout(timeoutId);
|
|
479
|
+
resolve();
|
|
480
|
+
});
|
|
481
|
+
});
|
|
482
|
+
},
|
|
483
|
+
rerender: (nextView) => {
|
|
484
|
+
result.rerender(wrapView(nextView));
|
|
485
|
+
}
|
|
486
|
+
};
|
|
487
|
+
}
|
|
488
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
489
|
+
0 && (module.exports = {
|
|
490
|
+
act,
|
|
491
|
+
cleanup,
|
|
492
|
+
createTestSuspenseToken,
|
|
493
|
+
flush,
|
|
494
|
+
prettyDOM,
|
|
495
|
+
queries,
|
|
496
|
+
render,
|
|
497
|
+
renderHook,
|
|
498
|
+
renderWithErrorBoundary,
|
|
499
|
+
renderWithSuspense,
|
|
500
|
+
testEffect,
|
|
501
|
+
waitForCondition,
|
|
502
|
+
...require("@testing-library/dom")
|
|
503
|
+
});
|