@noma.to/qwik-testing-library 1.5.1 → 1.6.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/README.md +118 -6
- package/lib/_virtual/_rolldown/runtime.qwik.cjs +28 -0
- package/lib/_virtual/_rolldown/runtime.qwik.mjs +27 -0
- package/lib/index.qwik.cjs +14 -10
- package/lib/index.qwik.mjs +4 -5
- package/lib/lib/qwik-testing-library.qwik.cjs +77 -93
- package/lib/lib/qwik-testing-library.qwik.mjs +69 -67
- package/lib/setup.qwik.cjs +3 -2
- package/lib/setup.qwik.mjs +4 -4
- package/lib-types/lib/qwik-testing-library.d.ts +4 -3
- package/lib-types/lib/types.d.ts +7 -2
- package/package.json +12 -12
package/README.md
CHANGED
|
@@ -92,6 +92,7 @@ src="https://raw.githubusercontent.com/ianlet/qwik-testing-library/main/high-vol
|
|
|
92
92
|
- [Setup](#setup)
|
|
93
93
|
- [Examples](#examples)
|
|
94
94
|
- [Qwikstart](#qwikstart)
|
|
95
|
+
- [Testing Hooks (experimental)](#testing-hooks-experimental)
|
|
95
96
|
- [Mocking Component Callbacks (experimental)](#mocking-component-callbacks-experimental)
|
|
96
97
|
- [Qwik City - `server$` calls](#qwik-city---server-calls)
|
|
97
98
|
- [Gotchas](#gotchas)
|
|
@@ -264,6 +265,109 @@ describe("<Counter />", () => {
|
|
|
264
265
|
})
|
|
265
266
|
```
|
|
266
267
|
|
|
268
|
+
### Testing Hooks (experimental)
|
|
269
|
+
|
|
270
|
+
> [!WARNING]
|
|
271
|
+
> This feature is under a testing phase and thus experimental.
|
|
272
|
+
> Its API may change in the future, so use it at your own risk.
|
|
273
|
+
|
|
274
|
+
`renderHook` lets you test custom hooks in isolation, without building a wrapper component by hand.
|
|
275
|
+
This is especially useful for library authors who need to battle-test the hooks they provide to their users.
|
|
276
|
+
|
|
277
|
+
```tsx
|
|
278
|
+
// use-counter.tsx
|
|
279
|
+
|
|
280
|
+
import { $, useSignal } from "@builder.io/qwik";
|
|
281
|
+
|
|
282
|
+
export function useCounter(initial = 0) {
|
|
283
|
+
const count = useSignal(initial);
|
|
284
|
+
const increment$ = $(() => count.value++);
|
|
285
|
+
|
|
286
|
+
return { count, increment$ };
|
|
287
|
+
}
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
```tsx
|
|
291
|
+
// use-counter.spec.tsx
|
|
292
|
+
|
|
293
|
+
import { renderHook } from "@noma.to/qwik-testing-library";
|
|
294
|
+
import { useCounter } from "./use-counter";
|
|
295
|
+
|
|
296
|
+
describe("useCounter", () => {
|
|
297
|
+
it("should start at 0", async () => {
|
|
298
|
+
const { result } = await renderHook(useCounter);
|
|
299
|
+
|
|
300
|
+
expect(result.count.value).toBe(0);
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
it("should increment", async () => {
|
|
304
|
+
const { result } = await renderHook(useCounter);
|
|
305
|
+
|
|
306
|
+
await result.increment$();
|
|
307
|
+
|
|
308
|
+
expect(result.count.value).toBe(1);
|
|
309
|
+
});
|
|
310
|
+
});
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
The `result` is the direct return value of your hook callback. Because Qwik signals are stable reactive
|
|
314
|
+
references, you can read and mutate them directly — no `.current` wrapper needed.
|
|
315
|
+
|
|
316
|
+
#### ESLint `qwik/use-method-usage`
|
|
317
|
+
|
|
318
|
+
The Qwik ESLint plugin only allows `use*` calls inside `component$` or `use*`-named functions.
|
|
319
|
+
This is a known limitation — a discussion is in progress with the Qwik team to relax their ESLint rule.
|
|
320
|
+
In the meantime, when you need to pass arguments to your hook, wrap it in a `use*`-named function to stay lint-clean:
|
|
321
|
+
|
|
322
|
+
```tsx
|
|
323
|
+
// passing the hook by reference — lint-clean
|
|
324
|
+
const { result } = await renderHook(useCounter);
|
|
325
|
+
|
|
326
|
+
// passing arguments — extract a use*-named function
|
|
327
|
+
function useCounterFrom10() {
|
|
328
|
+
return useCounter(10);
|
|
329
|
+
}
|
|
330
|
+
const { result } = await renderHook(useCounterFrom10);
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
#### Providing Context
|
|
334
|
+
|
|
335
|
+
If your hook depends on context, use the `wrapper` option to provide it:
|
|
336
|
+
|
|
337
|
+
```tsx
|
|
338
|
+
import { renderHook } from "@noma.to/qwik-testing-library";
|
|
339
|
+
import { component$, createContextId, useContextProvider, useStore, Slot } from "@builder.io/qwik";
|
|
340
|
+
import { useTheme } from "./use-theme";
|
|
341
|
+
|
|
342
|
+
const ThemeContext = createContextId<{ mode: string }>("theme");
|
|
343
|
+
|
|
344
|
+
const ThemeProvider = component$(() => {
|
|
345
|
+
useContextProvider(ThemeContext, useStore({ mode: "dark" }));
|
|
346
|
+
return <Slot />;
|
|
347
|
+
});
|
|
348
|
+
|
|
349
|
+
it("should read theme from context", async () => {
|
|
350
|
+
const { result } = await renderHook(useTheme, {
|
|
351
|
+
wrapper: ThemeProvider,
|
|
352
|
+
});
|
|
353
|
+
|
|
354
|
+
expect(result.mode).toBe("dark");
|
|
355
|
+
});
|
|
356
|
+
```
|
|
357
|
+
|
|
358
|
+
#### Cleanup
|
|
359
|
+
|
|
360
|
+
`renderHook` integrates with automatic cleanup, just like `render`.
|
|
361
|
+
You can also call `unmount()` manually if needed:
|
|
362
|
+
|
|
363
|
+
```tsx
|
|
364
|
+
const { result, unmount } = await renderHook(useCounter);
|
|
365
|
+
|
|
366
|
+
// ... assertions ...
|
|
367
|
+
|
|
368
|
+
unmount();
|
|
369
|
+
```
|
|
370
|
+
|
|
267
371
|
### Mocking Component Callbacks (experimental)
|
|
268
372
|
|
|
269
373
|
> [!WARNING]
|
|
@@ -290,7 +394,7 @@ Here's an example on how to use the `mock$` function:
|
|
|
290
394
|
// import qwik-testing methods
|
|
291
395
|
import {render, screen, waitFor} from "@noma.to/qwik-testing-library";
|
|
292
396
|
// import qwik-mock methods
|
|
293
|
-
import {mock
|
|
397
|
+
import {clearAllMocks, mock$} from "@noma.to/qwik-mock";
|
|
294
398
|
// import the userEvent methods to interact with the DOM
|
|
295
399
|
import {userEvent} from "@testing-library/user-event";
|
|
296
400
|
|
|
@@ -300,9 +404,7 @@ import {Counter} from "./counter";
|
|
|
300
404
|
// describe the test suite
|
|
301
405
|
describe("<Counter />", () => {
|
|
302
406
|
// initialize a mock
|
|
303
|
-
|
|
304
|
-
const onChangeMock = mock$(() => {
|
|
305
|
-
});
|
|
407
|
+
const onChangeMock = mock$();
|
|
306
408
|
|
|
307
409
|
// setup beforeEach block to run before each test
|
|
308
410
|
beforeEach(() => {
|
|
@@ -325,15 +427,25 @@ describe("<Counter />", () => {
|
|
|
325
427
|
await user.click(decrementBtn);
|
|
326
428
|
|
|
327
429
|
// assert that the onChange$ callback was called with the right value
|
|
328
|
-
// note: QRLs are async in Qwik, so we need to resolve them to verify interactions
|
|
329
430
|
await waitFor(() =>
|
|
330
|
-
expect(onChangeMock
|
|
431
|
+
expect(onChangeMock).toHaveBeenCalledWith(-1),
|
|
331
432
|
);
|
|
332
433
|
});
|
|
333
434
|
});
|
|
334
435
|
})
|
|
335
436
|
```
|
|
336
437
|
|
|
438
|
+
You can also provide a default implementation to `mock$`:
|
|
439
|
+
|
|
440
|
+
```tsx
|
|
441
|
+
const onSubmitMock = mock$(() => "success");
|
|
442
|
+
|
|
443
|
+
await render(<SubmitForm onSubmit$={onSubmitMock} />);
|
|
444
|
+
await user.click(screen.getByRole("button", { name: "Submit" }));
|
|
445
|
+
|
|
446
|
+
expect(await screen.findByText("success")).toBeInTheDocument();
|
|
447
|
+
```
|
|
448
|
+
|
|
337
449
|
### Qwik City - `server$` calls
|
|
338
450
|
|
|
339
451
|
If one of your Qwik components uses `server$` calls, your tests might fail with a rather cryptic message (e.g. `QWIK
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
//#region \0rolldown/runtime.js
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __exportAll = (all, no_symbols) => {
|
|
7
|
+
let target = {};
|
|
8
|
+
for (var name in all) __defProp(target, name, {
|
|
9
|
+
get: all[name],
|
|
10
|
+
enumerable: true
|
|
11
|
+
});
|
|
12
|
+
if (!no_symbols) __defProp(target, Symbol.toStringTag, { value: "Module" });
|
|
13
|
+
return target;
|
|
14
|
+
};
|
|
15
|
+
var __copyProps = (to, from, except, desc) => {
|
|
16
|
+
if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
|
|
17
|
+
key = keys[i];
|
|
18
|
+
if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
|
|
19
|
+
get: ((k) => from[k]).bind(null, key),
|
|
20
|
+
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
return to;
|
|
24
|
+
};
|
|
25
|
+
var __reExport = (target, mod, secondTarget) => (__copyProps(target, mod, "default"), secondTarget && __copyProps(secondTarget, mod, "default"));
|
|
26
|
+
//#endregion
|
|
27
|
+
exports.__exportAll = __exportAll;
|
|
28
|
+
exports.__reExport = __reExport;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
//#region \0rolldown/runtime.js
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __exportAll = (all, no_symbols) => {
|
|
7
|
+
let target = {};
|
|
8
|
+
for (var name in all) __defProp(target, name, {
|
|
9
|
+
get: all[name],
|
|
10
|
+
enumerable: true
|
|
11
|
+
});
|
|
12
|
+
if (!no_symbols) __defProp(target, Symbol.toStringTag, { value: "Module" });
|
|
13
|
+
return target;
|
|
14
|
+
};
|
|
15
|
+
var __copyProps = (to, from, except, desc) => {
|
|
16
|
+
if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
|
|
17
|
+
key = keys[i];
|
|
18
|
+
if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
|
|
19
|
+
get: ((k) => from[k]).bind(null, key),
|
|
20
|
+
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
return to;
|
|
24
|
+
};
|
|
25
|
+
var __reExport = (target, mod, secondTarget) => (__copyProps(target, mod, "default"), secondTarget && __copyProps(secondTarget, mod, "default"));
|
|
26
|
+
//#endregion
|
|
27
|
+
export { __exportAll, __reExport };
|
package/lib/index.qwik.cjs
CHANGED
|
@@ -1,12 +1,16 @@
|
|
|
1
|
-
"use strict";
|
|
2
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
-
|
|
4
|
-
const
|
|
5
|
-
|
|
6
|
-
exports.
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
2
|
+
require("./_virtual/_rolldown/runtime.qwik.cjs");
|
|
3
|
+
const require_qwik_testing_library = require("./lib/qwik-testing-library.qwik.cjs");
|
|
4
|
+
//#endregion
|
|
5
|
+
exports.cleanup = require_qwik_testing_library.cleanup;
|
|
6
|
+
exports.render = require_qwik_testing_library.render;
|
|
7
|
+
exports.renderHook = require_qwik_testing_library.renderHook;
|
|
8
|
+
var _testing_library_dom = require("@testing-library/dom");
|
|
9
|
+
Object.keys(_testing_library_dom).forEach(function(k) {
|
|
10
|
+
if (k !== "default" && !Object.prototype.hasOwnProperty.call(exports, k)) Object.defineProperty(exports, k, {
|
|
11
|
+
enumerable: true,
|
|
12
|
+
get: function() {
|
|
13
|
+
return _testing_library_dom[k];
|
|
14
|
+
}
|
|
15
|
+
});
|
|
12
16
|
});
|
package/lib/index.qwik.mjs
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import
|
|
1
|
+
import "./_virtual/_rolldown/runtime.qwik.mjs";
|
|
2
|
+
import { cleanup, render, renderHook } from "./lib/qwik-testing-library.qwik.mjs";
|
|
2
3
|
export * from "@testing-library/dom";
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
render
|
|
6
|
-
};
|
|
4
|
+
//#endregion
|
|
5
|
+
export { cleanup, render, renderHook };
|
|
@@ -1,106 +1,90 @@
|
|
|
1
|
-
"
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
-
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
-
var __copyProps = (to, from, except, desc) => {
|
|
9
|
-
if (from && typeof from === "object" || typeof from === "function") {
|
|
10
|
-
for (let key of __getOwnPropNames(from))
|
|
11
|
-
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
12
|
-
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
13
|
-
}
|
|
14
|
-
return to;
|
|
15
|
-
};
|
|
16
|
-
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
17
|
-
// If the importer is in node compatibility mode or this is not an ESM
|
|
18
|
-
// file that has been converted to a CommonJS file using a Babel-
|
|
19
|
-
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
20
|
-
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
21
|
-
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
22
|
-
mod
|
|
23
|
-
));
|
|
24
|
-
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
25
|
-
const jsxRuntime = require("@builder.io/qwik/jsx-runtime");
|
|
26
|
-
const dom = require("@testing-library/dom");
|
|
27
|
-
const server = require("@builder.io/qwik/server");
|
|
1
|
+
require("../_virtual/_rolldown/runtime.qwik.cjs");
|
|
2
|
+
let _testing_library_dom = require("@testing-library/dom");
|
|
3
|
+
let _builder_io_qwik_server = require("@builder.io/qwik/server");
|
|
4
|
+
let _builder_io_qwik_jsx_runtime = require("@builder.io/qwik/jsx-runtime");
|
|
5
|
+
//#region src/lib/qwik-testing-library.tsx
|
|
28
6
|
if (typeof HTMLTemplateElement !== "undefined") {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
return this.content.childNodes;
|
|
38
|
-
}
|
|
39
|
-
});
|
|
40
|
-
}
|
|
7
|
+
const testTemplate = document.createElement("template");
|
|
8
|
+
const testNode = document.createComment("test");
|
|
9
|
+
testTemplate.insertBefore(testNode, null);
|
|
10
|
+
const needsPatch = testTemplate.childNodes.length === 0;
|
|
11
|
+
testNode.remove();
|
|
12
|
+
if (needsPatch) Object.defineProperty(HTMLTemplateElement.prototype, "childNodes", { get() {
|
|
13
|
+
return this.content.childNodes;
|
|
14
|
+
} });
|
|
41
15
|
}
|
|
42
16
|
if (typeof process === "undefined" || !process.env?.QTL_SKIP_AUTO_CLEANUP) {
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
});
|
|
47
|
-
}
|
|
17
|
+
if (typeof afterEach === "function") afterEach(() => {
|
|
18
|
+
cleanup();
|
|
19
|
+
});
|
|
48
20
|
}
|
|
49
|
-
|
|
21
|
+
var mountedContainers = /* @__PURE__ */ new Set();
|
|
50
22
|
async function render(ui, options = {}) {
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
unmount: cleanup2,
|
|
85
|
-
...dom.getQueriesForElement(container, queries)
|
|
86
|
-
};
|
|
23
|
+
const qwik = await import("@builder.io/qwik");
|
|
24
|
+
let { container, baseElement = container } = options;
|
|
25
|
+
const { wrapper: Wrapper } = options;
|
|
26
|
+
const { queries, serverData } = options;
|
|
27
|
+
if (!baseElement) baseElement = document.body;
|
|
28
|
+
if (!container) container = baseElement.insertBefore(document.createElement("host"), baseElement.firstChild);
|
|
29
|
+
const wrappedUi = !Wrapper ? ui : /* @__PURE__ */ (0, _builder_io_qwik_jsx_runtime.jsx)(Wrapper, { children: ui });
|
|
30
|
+
const doc = baseElement.ownerDocument;
|
|
31
|
+
const win = doc.defaultView;
|
|
32
|
+
new Function("document", "window", (0, _builder_io_qwik_server.getQwikLoaderScript)())(doc, win);
|
|
33
|
+
const { cleanup } = await qwik.render(container, wrappedUi, { serverData });
|
|
34
|
+
mountedContainers.add({
|
|
35
|
+
container,
|
|
36
|
+
componentCleanup: cleanup
|
|
37
|
+
});
|
|
38
|
+
return {
|
|
39
|
+
container,
|
|
40
|
+
baseElement,
|
|
41
|
+
asFragment: () => {
|
|
42
|
+
if (typeof document.createRange === "function") return document.createRange().createContextualFragment(container.innerHTML);
|
|
43
|
+
else {
|
|
44
|
+
const template = document.createElement("template");
|
|
45
|
+
template.innerHTML = container.innerHTML;
|
|
46
|
+
return template.content;
|
|
47
|
+
}
|
|
48
|
+
},
|
|
49
|
+
debug: (el = baseElement, maxLength, options) => Array.isArray(el) ? el.forEach((e) => console.log((0, _testing_library_dom.prettyDOM)(e, maxLength, options))) : console.log((0, _testing_library_dom.prettyDOM)(el, maxLength, {
|
|
50
|
+
...options,
|
|
51
|
+
filterNode: () => true
|
|
52
|
+
})),
|
|
53
|
+
unmount: cleanup,
|
|
54
|
+
...(0, _testing_library_dom.getQueriesForElement)(container, queries)
|
|
55
|
+
};
|
|
87
56
|
}
|
|
88
57
|
function cleanupAtContainer(ref) {
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
}
|
|
94
|
-
mountedContainers.delete(ref);
|
|
58
|
+
const { container, componentCleanup } = ref;
|
|
59
|
+
componentCleanup();
|
|
60
|
+
if (container?.parentNode === document.body) document.body.removeChild(container);
|
|
61
|
+
mountedContainers.delete(ref);
|
|
95
62
|
}
|
|
96
63
|
function cleanup() {
|
|
97
|
-
|
|
64
|
+
mountedContainers.forEach(cleanupAtContainer);
|
|
98
65
|
}
|
|
66
|
+
async function renderHook(callback, options = {}) {
|
|
67
|
+
const { component$, noSerialize } = await import("@builder.io/qwik");
|
|
68
|
+
const callbackRef = noSerialize(callback);
|
|
69
|
+
const resultRef = noSerialize({ current: void 0 });
|
|
70
|
+
const { unmount } = await render(/* @__PURE__ */ (0, _builder_io_qwik_jsx_runtime.jsx)(component$(() => {
|
|
71
|
+
resultRef.current = callbackRef();
|
|
72
|
+
return /* @__PURE__ */ (0, _builder_io_qwik_jsx_runtime.jsx)(_builder_io_qwik_jsx_runtime.Fragment, {});
|
|
73
|
+
}), {}), { wrapper: options.wrapper });
|
|
74
|
+
return {
|
|
75
|
+
result: resultRef.current,
|
|
76
|
+
unmount
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
//#endregion
|
|
99
80
|
exports.cleanup = cleanup;
|
|
100
81
|
exports.render = render;
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
82
|
+
exports.renderHook = renderHook;
|
|
83
|
+
Object.keys(_testing_library_dom).forEach(function(k) {
|
|
84
|
+
if (k !== "default" && !Object.prototype.hasOwnProperty.call(exports, k)) Object.defineProperty(exports, k, {
|
|
85
|
+
enumerable: true,
|
|
86
|
+
get: function() {
|
|
87
|
+
return _testing_library_dom[k];
|
|
88
|
+
}
|
|
89
|
+
});
|
|
106
90
|
});
|
|
@@ -1,79 +1,81 @@
|
|
|
1
|
-
import
|
|
1
|
+
import "../_virtual/_rolldown/runtime.qwik.mjs";
|
|
2
2
|
import { getQueriesForElement, prettyDOM } from "@testing-library/dom";
|
|
3
|
-
export * from "@testing-library/dom";
|
|
4
3
|
import { getQwikLoaderScript } from "@builder.io/qwik/server";
|
|
4
|
+
import { Fragment, jsx } from "@builder.io/qwik/jsx-runtime";
|
|
5
|
+
export * from "@testing-library/dom";
|
|
6
|
+
//#region src/lib/qwik-testing-library.tsx
|
|
5
7
|
if (typeof HTMLTemplateElement !== "undefined") {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
return this.content.childNodes;
|
|
15
|
-
}
|
|
16
|
-
});
|
|
17
|
-
}
|
|
8
|
+
const testTemplate = document.createElement("template");
|
|
9
|
+
const testNode = document.createComment("test");
|
|
10
|
+
testTemplate.insertBefore(testNode, null);
|
|
11
|
+
const needsPatch = testTemplate.childNodes.length === 0;
|
|
12
|
+
testNode.remove();
|
|
13
|
+
if (needsPatch) Object.defineProperty(HTMLTemplateElement.prototype, "childNodes", { get() {
|
|
14
|
+
return this.content.childNodes;
|
|
15
|
+
} });
|
|
18
16
|
}
|
|
19
17
|
if (typeof process === "undefined" || !process.env?.QTL_SKIP_AUTO_CLEANUP) {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
});
|
|
24
|
-
}
|
|
18
|
+
if (typeof afterEach === "function") afterEach(() => {
|
|
19
|
+
cleanup();
|
|
20
|
+
});
|
|
25
21
|
}
|
|
26
|
-
|
|
22
|
+
var mountedContainers = /* @__PURE__ */ new Set();
|
|
27
23
|
async function render(ui, options = {}) {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
unmount: cleanup2,
|
|
62
|
-
...getQueriesForElement(container, queries)
|
|
63
|
-
};
|
|
24
|
+
const qwik = await import("@builder.io/qwik");
|
|
25
|
+
let { container, baseElement = container } = options;
|
|
26
|
+
const { wrapper: Wrapper } = options;
|
|
27
|
+
const { queries, serverData } = options;
|
|
28
|
+
if (!baseElement) baseElement = document.body;
|
|
29
|
+
if (!container) container = baseElement.insertBefore(document.createElement("host"), baseElement.firstChild);
|
|
30
|
+
const wrappedUi = !Wrapper ? ui : /* @__PURE__ */ jsx(Wrapper, { children: ui });
|
|
31
|
+
const doc = baseElement.ownerDocument;
|
|
32
|
+
const win = doc.defaultView;
|
|
33
|
+
new Function("document", "window", getQwikLoaderScript())(doc, win);
|
|
34
|
+
const { cleanup } = await qwik.render(container, wrappedUi, { serverData });
|
|
35
|
+
mountedContainers.add({
|
|
36
|
+
container,
|
|
37
|
+
componentCleanup: cleanup
|
|
38
|
+
});
|
|
39
|
+
return {
|
|
40
|
+
container,
|
|
41
|
+
baseElement,
|
|
42
|
+
asFragment: () => {
|
|
43
|
+
if (typeof document.createRange === "function") return document.createRange().createContextualFragment(container.innerHTML);
|
|
44
|
+
else {
|
|
45
|
+
const template = document.createElement("template");
|
|
46
|
+
template.innerHTML = container.innerHTML;
|
|
47
|
+
return template.content;
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
debug: (el = baseElement, maxLength, options) => Array.isArray(el) ? el.forEach((e) => console.log(prettyDOM(e, maxLength, options))) : console.log(prettyDOM(el, maxLength, {
|
|
51
|
+
...options,
|
|
52
|
+
filterNode: () => true
|
|
53
|
+
})),
|
|
54
|
+
unmount: cleanup,
|
|
55
|
+
...getQueriesForElement(container, queries)
|
|
56
|
+
};
|
|
64
57
|
}
|
|
65
58
|
function cleanupAtContainer(ref) {
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
}
|
|
71
|
-
mountedContainers.delete(ref);
|
|
59
|
+
const { container, componentCleanup } = ref;
|
|
60
|
+
componentCleanup();
|
|
61
|
+
if (container?.parentNode === document.body) document.body.removeChild(container);
|
|
62
|
+
mountedContainers.delete(ref);
|
|
72
63
|
}
|
|
73
64
|
function cleanup() {
|
|
74
|
-
|
|
65
|
+
mountedContainers.forEach(cleanupAtContainer);
|
|
66
|
+
}
|
|
67
|
+
async function renderHook(callback, options = {}) {
|
|
68
|
+
const { component$, noSerialize } = await import("@builder.io/qwik");
|
|
69
|
+
const callbackRef = noSerialize(callback);
|
|
70
|
+
const resultRef = noSerialize({ current: void 0 });
|
|
71
|
+
const { unmount } = await render(/* @__PURE__ */ jsx(component$(() => {
|
|
72
|
+
resultRef.current = callbackRef();
|
|
73
|
+
return /* @__PURE__ */ jsx(Fragment, {});
|
|
74
|
+
}), {}), { wrapper: options.wrapper });
|
|
75
|
+
return {
|
|
76
|
+
result: resultRef.current,
|
|
77
|
+
unmount
|
|
78
|
+
};
|
|
75
79
|
}
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
render
|
|
79
|
-
};
|
|
80
|
+
//#endregion
|
|
81
|
+
export { cleanup, render, renderHook };
|
package/lib/setup.qwik.cjs
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
"use strict";
|
|
2
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
//#region src/setup.ts
|
|
3
3
|
globalThis.qTest = false;
|
|
4
4
|
globalThis.qRuntimeQrl = true;
|
|
5
5
|
globalThis.qDev = true;
|
|
6
6
|
globalThis.qInspector = false;
|
|
7
|
-
|
|
7
|
+
var __qwikSetupComplete = true;
|
|
8
|
+
//#endregion
|
|
8
9
|
exports.__qwikSetupComplete = __qwikSetupComplete;
|
package/lib/setup.qwik.mjs
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
+
//#region src/setup.ts
|
|
1
2
|
globalThis.qTest = false;
|
|
2
3
|
globalThis.qRuntimeQrl = true;
|
|
3
4
|
globalThis.qDev = true;
|
|
4
5
|
globalThis.qInspector = false;
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
};
|
|
6
|
+
var __qwikSetupComplete = true;
|
|
7
|
+
//#endregion
|
|
8
|
+
export { __qwikSetupComplete };
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { JSXOutput } from "@builder.io/qwik";
|
|
2
|
-
import type {
|
|
3
|
-
declare function render(ui: JSXOutput, options?:
|
|
2
|
+
import type { RenderOptions, RenderHookOptions, RenderHookResult, Result } from "./types";
|
|
3
|
+
declare function render(ui: JSXOutput, options?: RenderOptions): Promise<Result>;
|
|
4
4
|
declare function cleanup(): void;
|
|
5
|
+
declare function renderHook<Result>(callback: () => Result, options?: RenderHookOptions): Promise<RenderHookResult<Result>>;
|
|
5
6
|
export * from "@testing-library/dom";
|
|
6
|
-
export { cleanup, render };
|
|
7
|
+
export { cleanup, render, renderHook };
|
package/lib-types/lib/types.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { BoundFunctions, prettyFormat, Queries } from "@testing-library/dom";
|
|
2
2
|
import { queries } from "@testing-library/dom";
|
|
3
|
-
import type { Component, RenderOptions } from "@builder.io/qwik";
|
|
4
|
-
export interface
|
|
3
|
+
import type { Component, RenderOptions as QwikRenderOptions } from "@builder.io/qwik";
|
|
4
|
+
export interface RenderOptions extends QwikRenderOptions {
|
|
5
5
|
container?: HTMLElement;
|
|
6
6
|
baseElement?: HTMLElement;
|
|
7
7
|
queries?: Queries & typeof queries;
|
|
@@ -19,3 +19,8 @@ export type ComponentRef = {
|
|
|
19
19
|
container: HTMLElement;
|
|
20
20
|
componentCleanup: () => void;
|
|
21
21
|
};
|
|
22
|
+
export type RenderHookOptions = Pick<RenderOptions, 'wrapper'>;
|
|
23
|
+
export interface RenderHookResult<Result> {
|
|
24
|
+
result: Result;
|
|
25
|
+
unmount: () => void;
|
|
26
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@noma.to/qwik-testing-library",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.6.0",
|
|
4
4
|
"description": "Simple and complete Qwik testing utilities that encourage good testing practices.",
|
|
5
5
|
"repository": "https://github.com/ianlet/qwik-testing-library",
|
|
6
6
|
"homepage": "https://github.com/ianlet/qwik-testing-library",
|
|
@@ -39,18 +39,18 @@
|
|
|
39
39
|
"validate": "pnpm build"
|
|
40
40
|
},
|
|
41
41
|
"devDependencies": {
|
|
42
|
-
"@
|
|
43
|
-
"@types/node": "25.
|
|
44
|
-
"
|
|
45
|
-
"
|
|
46
|
-
"
|
|
47
|
-
"
|
|
48
|
-
"
|
|
49
|
-
"typescript": "
|
|
42
|
+
"@eslint/js": "10",
|
|
43
|
+
"@types/node": "25.6.0",
|
|
44
|
+
"eslint": "10",
|
|
45
|
+
"eslint-plugin-qwik": "1.19.2",
|
|
46
|
+
"globals": "^17.4.0",
|
|
47
|
+
"prettier": "3.8.2",
|
|
48
|
+
"typescript": "6.0.2",
|
|
49
|
+
"typescript-eslint": "^8.58.1",
|
|
50
50
|
"undici": "*",
|
|
51
|
-
"vite": "
|
|
52
|
-
"vite-tsconfig-paths": "^6.
|
|
53
|
-
"vitest": "^4.
|
|
51
|
+
"vite": "8.0.8",
|
|
52
|
+
"vite-tsconfig-paths": "^6.1.1",
|
|
53
|
+
"vitest": "^4.1.4"
|
|
54
54
|
},
|
|
55
55
|
"peerDependencies": {
|
|
56
56
|
"@builder.io/qwik": ">= 1.12.0 < 2",
|