@noma.to/qwik-testing-library 1.5.2 → 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 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]
@@ -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 };
@@ -1,12 +1,16 @@
1
- "use strict";
2
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
- const qwikTestingLibrary = require("./lib/qwik-testing-library.qwik.cjs");
4
- const dom = require("@testing-library/dom");
5
- exports.cleanup = qwikTestingLibrary.cleanup;
6
- exports.render = qwikTestingLibrary.render;
7
- Object.keys(dom).forEach((k) => {
8
- if (k !== "default" && !Object.prototype.hasOwnProperty.call(exports, k)) Object.defineProperty(exports, k, {
9
- enumerable: true,
10
- get: () => dom[k]
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
  });
@@ -1,6 +1,5 @@
1
- import { cleanup, render } from "./lib/qwik-testing-library.qwik.mjs";
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
- export {
4
- cleanup,
5
- render
6
- };
4
+ //#endregion
5
+ export { cleanup, render, renderHook };
@@ -1,106 +1,90 @@
1
- "use strict";
2
- var __create = Object.create;
3
- var __defProp = Object.defineProperty;
4
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
- var __getOwnPropNames = Object.getOwnPropertyNames;
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
- const testTemplate = document.createElement("template");
30
- const testNode = document.createComment("test");
31
- testTemplate.insertBefore(testNode, null);
32
- const needsPatch = testTemplate.childNodes.length === 0;
33
- testNode.remove();
34
- if (needsPatch) {
35
- Object.defineProperty(HTMLTemplateElement.prototype, "childNodes", {
36
- get() {
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
- if (typeof afterEach === "function") {
44
- afterEach(() => {
45
- cleanup();
46
- });
47
- }
17
+ if (typeof afterEach === "function") afterEach(() => {
18
+ cleanup();
19
+ });
48
20
  }
49
- const mountedContainers = /* @__PURE__ */ new Set();
21
+ var mountedContainers = /* @__PURE__ */ new Set();
50
22
  async function render(ui, options = {}) {
51
- const qwik = await import("@builder.io/qwik");
52
- let { container, baseElement = container, wrapper: Wrapper } = options;
53
- const { queries, serverData } = options;
54
- if (!baseElement) {
55
- baseElement = document.body;
56
- }
57
- if (!container) {
58
- container = baseElement.insertBefore(
59
- document.createElement("host"),
60
- baseElement.firstChild
61
- );
62
- }
63
- const wrappedUi = !Wrapper ? ui : /* @__PURE__ */ jsxRuntime.jsx(Wrapper, { children: ui });
64
- const doc = baseElement.ownerDocument;
65
- const win = doc.defaultView;
66
- new Function("document", "window", server.getQwikLoaderScript())(doc, win);
67
- const { cleanup: cleanup2 } = await qwik.render(container, wrappedUi, { serverData });
68
- mountedContainers.add({ container, componentCleanup: cleanup2 });
69
- return {
70
- container,
71
- baseElement,
72
- asFragment: () => {
73
- if (typeof document.createRange === "function") {
74
- return document.createRange().createContextualFragment(container.innerHTML);
75
- } else {
76
- const template = document.createElement("template");
77
- template.innerHTML = container.innerHTML;
78
- return template.content;
79
- }
80
- },
81
- debug: (el = baseElement, maxLength, options2) => Array.isArray(el) ? el.forEach((e) => console.log(dom.prettyDOM(e, maxLength, options2))) : console.log(
82
- dom.prettyDOM(el, maxLength, { ...options2, filterNode: () => true })
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
- const { container, componentCleanup } = ref;
90
- componentCleanup();
91
- if (container?.parentNode === document.body) {
92
- document.body.removeChild(container);
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
- mountedContainers.forEach(cleanupAtContainer);
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
- Object.keys(dom).forEach((k) => {
102
- if (k !== "default" && !Object.prototype.hasOwnProperty.call(exports, k)) Object.defineProperty(exports, k, {
103
- enumerable: true,
104
- get: () => dom[k]
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 { jsx } from "@builder.io/qwik/jsx-runtime";
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
- const testTemplate = document.createElement("template");
7
- const testNode = document.createComment("test");
8
- testTemplate.insertBefore(testNode, null);
9
- const needsPatch = testTemplate.childNodes.length === 0;
10
- testNode.remove();
11
- if (needsPatch) {
12
- Object.defineProperty(HTMLTemplateElement.prototype, "childNodes", {
13
- get() {
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
- if (typeof afterEach === "function") {
21
- afterEach(() => {
22
- cleanup();
23
- });
24
- }
18
+ if (typeof afterEach === "function") afterEach(() => {
19
+ cleanup();
20
+ });
25
21
  }
26
- const mountedContainers = /* @__PURE__ */ new Set();
22
+ var mountedContainers = /* @__PURE__ */ new Set();
27
23
  async function render(ui, options = {}) {
28
- const qwik = await import("@builder.io/qwik");
29
- let { container, baseElement = container, wrapper: Wrapper } = options;
30
- const { queries, serverData } = options;
31
- if (!baseElement) {
32
- baseElement = document.body;
33
- }
34
- if (!container) {
35
- container = baseElement.insertBefore(
36
- document.createElement("host"),
37
- baseElement.firstChild
38
- );
39
- }
40
- const wrappedUi = !Wrapper ? ui : /* @__PURE__ */ jsx(Wrapper, { children: ui });
41
- const doc = baseElement.ownerDocument;
42
- const win = doc.defaultView;
43
- new Function("document", "window", getQwikLoaderScript())(doc, win);
44
- const { cleanup: cleanup2 } = await qwik.render(container, wrappedUi, { serverData });
45
- mountedContainers.add({ container, componentCleanup: cleanup2 });
46
- return {
47
- container,
48
- baseElement,
49
- asFragment: () => {
50
- if (typeof document.createRange === "function") {
51
- return document.createRange().createContextualFragment(container.innerHTML);
52
- } else {
53
- const template = document.createElement("template");
54
- template.innerHTML = container.innerHTML;
55
- return template.content;
56
- }
57
- },
58
- debug: (el = baseElement, maxLength, options2) => Array.isArray(el) ? el.forEach((e) => console.log(prettyDOM(e, maxLength, options2))) : console.log(
59
- prettyDOM(el, maxLength, { ...options2, filterNode: () => true })
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
- const { container, componentCleanup } = ref;
67
- componentCleanup();
68
- if (container?.parentNode === document.body) {
69
- document.body.removeChild(container);
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
- mountedContainers.forEach(cleanupAtContainer);
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
- export {
77
- cleanup,
78
- render
79
- };
80
+ //#endregion
81
+ export { cleanup, render, renderHook };
@@ -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
- const __qwikSetupComplete = true;
7
+ var __qwikSetupComplete = true;
8
+ //#endregion
8
9
  exports.__qwikSetupComplete = __qwikSetupComplete;
@@ -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
- const __qwikSetupComplete = true;
6
- export {
7
- __qwikSetupComplete
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 { Options, Result } from "./types";
3
- declare function render(ui: JSXOutput, options?: Options): Promise<Result>;
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 };
@@ -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 Options extends RenderOptions {
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.5.2",
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
- "@types/eslint": "8.56.10",
43
- "@types/node": "25.3.3",
44
- "@typescript-eslint/eslint-plugin": "8.56.1",
45
- "@typescript-eslint/parser": "8.56.1",
46
- "eslint": "8.57.1",
47
- "eslint-plugin-qwik": "1.19.0",
48
- "prettier": "3.8.1",
49
- "typescript": "5.9.3",
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": "7.3.1",
51
+ "vite": "8.0.8",
52
52
  "vite-tsconfig-paths": "^6.1.1",
53
- "vitest": "^4.0.18"
53
+ "vitest": "^4.1.4"
54
54
  },
55
55
  "peerDependencies": {
56
56
  "@builder.io/qwik": ">= 1.12.0 < 2",