vitest-browser-qwik 0.2.2 → 0.3.2
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 +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/{pure-BRpTMupH.js → pure-BlLa_nxU.js} +7 -7
- package/dist/{pure-1MgDxC6b.d.ts → pure-dqI6lsdL.d.ts} +2 -2
- package/dist/pure.d.ts +1 -1
- package/dist/pure.js +1 -1
- package/dist/ssr-plugin.js +77 -18
- package/package.json +12 -12
package/README.md
CHANGED
|
@@ -43,7 +43,7 @@ export default defineConfig({
|
|
|
43
43
|
|
|
44
44
|
```tsx
|
|
45
45
|
import { defineConfig } from 'vitest/config'
|
|
46
|
-
import { qwikVite } from '@
|
|
46
|
+
import { qwikVite } from '@qwik.dev/core/optimizer'
|
|
47
47
|
|
|
48
48
|
// optional, run the tests in SSR mode
|
|
49
49
|
import { testSSR } from 'vitest-browser-qwik/ssr-plugin'
|
|
@@ -97,7 +97,7 @@ test('renders counter with SSR', async () => {
|
|
|
97
97
|
### Hook Testing Example
|
|
98
98
|
|
|
99
99
|
```tsx
|
|
100
|
-
import { useSignal } from "@
|
|
100
|
+
import { useSignal } from "@qwik.dev/core";
|
|
101
101
|
import { expect, test } from "vitest";
|
|
102
102
|
import { renderHook } from "vitest-browser-qwik";
|
|
103
103
|
import { useCounter } from "./fixtures/useCounter";
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { a as cleanup, c as renderServerHTML, i as SSRRenderOptions, o as render, r as RenderResult, s as renderHook } from "./pure-
|
|
2
|
-
import { JSXOutput } from "@
|
|
1
|
+
import { a as cleanup, c as renderServerHTML, i as SSRRenderOptions, o as render, r as RenderResult, s as renderHook } from "./pure-dqI6lsdL.js";
|
|
2
|
+
import { JSXOutput } from "@qwik.dev/core";
|
|
3
3
|
|
|
4
4
|
//#region src/index.d.ts
|
|
5
5
|
declare function renderSSR(jsxNode: JSXOutput): Promise<RenderResult>;
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { i as renderServerHTML, n as render, r as renderHook, t as cleanup } from "./pure-
|
|
1
|
+
import { i as renderServerHTML, n as render, r as renderHook, t as cleanup } from "./pure-BlLa_nxU.js";
|
|
2
2
|
import { beforeEach } from "vitest";
|
|
3
3
|
import { page } from "vitest/browser";
|
|
4
4
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { utils } from "vitest/browser";
|
|
2
|
-
import { component$, render } from "@
|
|
3
|
-
import { getQwikLoaderScript } from "@
|
|
4
|
-
import { jsx } from "@
|
|
2
|
+
import { component$, render } from "@qwik.dev/core";
|
|
3
|
+
import { getQwikLoaderScript } from "@qwik.dev/core/server";
|
|
4
|
+
import { jsx } from "@qwik.dev/core/jsx-runtime";
|
|
5
5
|
|
|
6
6
|
//#region src/pure.tsx
|
|
7
7
|
const { debug, getElementLocatorSelectors } = utils;
|
|
@@ -40,17 +40,17 @@ function setupContainer(baseElement, container) {
|
|
|
40
40
|
baseElement
|
|
41
41
|
};
|
|
42
42
|
}
|
|
43
|
-
function render$1(ui, { container, baseElement } = {}) {
|
|
43
|
+
async function render$1(ui, { container, baseElement } = {}) {
|
|
44
44
|
csrQwikLoader();
|
|
45
45
|
const setup = setupContainer(baseElement, container);
|
|
46
|
-
render(setup.container, ui);
|
|
46
|
+
await render(setup.container, ui);
|
|
47
47
|
return createRenderResult(setup.container, setup.baseElement);
|
|
48
48
|
}
|
|
49
49
|
function setHTMLWithScripts(container, html) {
|
|
50
50
|
container.innerHTML = html;
|
|
51
51
|
container.querySelectorAll("script").forEach((oldScript) => {
|
|
52
52
|
const newScript = document.createElement("script");
|
|
53
|
-
for (const attr of oldScript.attributes) newScript.setAttribute(attr.name, attr.value);
|
|
53
|
+
for (const attr of Array.from(oldScript.attributes)) newScript.setAttribute(attr.name, attr.value);
|
|
54
54
|
newScript.text = oldScript.textContent ?? "";
|
|
55
55
|
oldScript.parentNode?.replaceChild(newScript, oldScript);
|
|
56
56
|
});
|
|
@@ -66,7 +66,7 @@ async function renderHook(hook) {
|
|
|
66
66
|
const renderPromise = new Promise((resolve) => {
|
|
67
67
|
resolveRender = resolve;
|
|
68
68
|
});
|
|
69
|
-
const screen = render$1(/* @__PURE__ */ jsx(component$(() => {
|
|
69
|
+
const screen = await render$1(/* @__PURE__ */ jsx(component$(() => {
|
|
70
70
|
resultContainer.value = hook();
|
|
71
71
|
resolveRender();
|
|
72
72
|
return /* @__PURE__ */ jsx("div", { "data-testid": "hook-result" });
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Locator, LocatorSelectors, PrettyDOMOptions } from "vitest/browser";
|
|
2
|
-
import { JSXOutput } from "@
|
|
2
|
+
import { JSXOutput } from "@qwik.dev/core";
|
|
3
3
|
|
|
4
4
|
//#region src/pure.d.ts
|
|
5
5
|
interface RenderResult extends LocatorSelectors {
|
|
@@ -20,7 +20,7 @@ interface SSRRenderOptions {
|
|
|
20
20
|
declare function render$1(ui: JSXOutput, {
|
|
21
21
|
container,
|
|
22
22
|
baseElement
|
|
23
|
-
}?: RenderOptions): RenderResult
|
|
23
|
+
}?: RenderOptions): Promise<RenderResult>;
|
|
24
24
|
declare function renderServerHTML(html: string, {
|
|
25
25
|
container,
|
|
26
26
|
baseElement
|
package/dist/pure.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as cleanup, c as renderServerHTML, i as SSRRenderOptions, n as RenderOptions, o as render, r as RenderResult, s as renderHook, t as RenderHookResult } from "./pure-
|
|
1
|
+
import { a as cleanup, c as renderServerHTML, i as SSRRenderOptions, n as RenderOptions, o as render, r as RenderResult, s as renderHook, t as RenderHookResult } from "./pure-dqI6lsdL.js";
|
|
2
2
|
export { RenderHookResult, RenderOptions, RenderResult, SSRRenderOptions, cleanup, render, renderHook, renderServerHTML };
|
package/dist/pure.js
CHANGED
package/dist/ssr-plugin.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { dirname, relative, resolve } from "node:path";
|
|
2
|
-
import { symbolMapper } from "@builder.io/qwik/optimizer";
|
|
3
2
|
import MagicString from "magic-string";
|
|
4
3
|
import { parseSync } from "oxc-parser";
|
|
5
4
|
import { ResolverFactory } from "oxc-resolver";
|
|
@@ -212,40 +211,80 @@ function hasCommandsImport(node) {
|
|
|
212
211
|
if (!isImportDeclaration(node) || node.source?.value !== "vitest/browser" || !node.specifiers) return false;
|
|
213
212
|
return node.specifiers.some((spec) => spec.type === "ImportSpecifier" && spec.imported.type === "Identifier" && spec.imported.name === "commands");
|
|
214
213
|
}
|
|
214
|
+
const getClientModule = async (viteServer, moduleId) => {
|
|
215
|
+
const clientEnv = viteServer.environments.client;
|
|
216
|
+
await clientEnv.fetchModule(moduleId);
|
|
217
|
+
const resolved = await clientEnv.moduleGraph.resolveUrl(moduleId);
|
|
218
|
+
const resolvedId = resolved?.[1];
|
|
219
|
+
if (!resolvedId) throw new Error(`Could not resolve module "${moduleId}" in client environment`);
|
|
220
|
+
const module = clientEnv.moduleGraph.getModuleById(resolvedId);
|
|
221
|
+
console.log("Resolved client module", moduleId, resolved, module);
|
|
222
|
+
if (!module) throw new Error(`Module "${moduleId}" not found in client module graph.`);
|
|
223
|
+
return module;
|
|
224
|
+
};
|
|
215
225
|
async function renderComponentToSSR(ctx, Component, props = {}) {
|
|
216
226
|
const viteServer = ctx.project.vite;
|
|
217
|
-
const { jsx } = await viteServer.ssrLoadModule("@
|
|
227
|
+
const { jsx } = await viteServer.ssrLoadModule("@qwik.dev/core");
|
|
218
228
|
const jsxElement = jsx(Component, props);
|
|
219
|
-
const { renderToStream } = await viteServer.ssrLoadModule("@
|
|
220
|
-
|
|
229
|
+
const { renderToStream } = await viteServer.ssrLoadModule("@qwik.dev/core/server");
|
|
230
|
+
const mapping = {};
|
|
231
|
+
if (!ctx.testPath) throw new Error("ctx.testPath is required for SSR rendering");
|
|
232
|
+
const module = await getClientModule(viteServer, ctx.testPath);
|
|
233
|
+
for (const importedModule of module?.importedModules || []) {
|
|
234
|
+
const meta = importedModule.info?.meta;
|
|
235
|
+
if (meta?.segment) {
|
|
236
|
+
const symbol = meta.segment.hash;
|
|
237
|
+
if (symbol && importedModule.id) mapping[symbol] = `/@fs${importedModule.id}`;
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
const handlersId = (await getClientModule(viteServer, "@qwik.dev/core/handlers.mjs")).id;
|
|
241
|
+
if (!handlersId) throw new Error("Handlers module ID could not be resolved");
|
|
242
|
+
const handlersExports = await viteServer.ssrLoadModule("@qwik.dev/core/handlers.mjs");
|
|
243
|
+
for (const key of Object.keys(handlersExports)) if (key.startsWith("_")) mapping[key] = handlersId;
|
|
244
|
+
const qwikManifest = {
|
|
245
|
+
manifestHash: "dev",
|
|
246
|
+
mapping
|
|
247
|
+
};
|
|
248
|
+
console.log(mapping);
|
|
249
|
+
let html = "<script>var _import=(s)=>{console.log('importing', s);return import(s)}<\/script>";
|
|
221
250
|
await renderToStream(jsxElement, {
|
|
251
|
+
manifest: qwikManifest,
|
|
222
252
|
containerTagName: "div",
|
|
223
253
|
base: "/",
|
|
224
|
-
qwikLoader: { include: "always" },
|
|
225
|
-
symbolMapper: globalThis.qwikSymbolMapper,
|
|
226
254
|
stream: { write(chunk) {
|
|
227
|
-
html += chunk;
|
|
255
|
+
html += chunk.replace(/=import\(/g, "=_import(");
|
|
228
256
|
} }
|
|
229
257
|
});
|
|
258
|
+
console.log("FINAL HTML", html);
|
|
230
259
|
return { html };
|
|
231
260
|
}
|
|
232
261
|
|
|
233
262
|
//#endregion
|
|
234
263
|
//#region src/ssr-plugin.ts
|
|
235
264
|
const isJSorTS = createRegExp(exactly(".").and(anyOf("j", "t")).and("s").and(maybe("x")).at.lineEnd());
|
|
265
|
+
function isBrowserOnlySource(source) {
|
|
266
|
+
if (!source) return false;
|
|
267
|
+
return source === "vitest" || source.startsWith("vitest/") || source === "vitest-browser-qwik" || source.startsWith("vitest-browser-qwik/") || source.includes("@vitest/");
|
|
268
|
+
}
|
|
269
|
+
function referencesStrippedId(node, strippedIds) {
|
|
270
|
+
if (!node || typeof node !== "object") return false;
|
|
271
|
+
if (node.type === "Identifier") return strippedIds.has(node.name);
|
|
272
|
+
if (node.type === "MemberExpression") return referencesStrippedId(node.object, strippedIds);
|
|
273
|
+
if (isCallExpression(node)) return referencesStrippedId(node.callee, strippedIds);
|
|
274
|
+
return false;
|
|
275
|
+
}
|
|
276
|
+
function isVariableDeclaration(node) {
|
|
277
|
+
return node.type === "VariableDeclaration";
|
|
278
|
+
}
|
|
279
|
+
let userDefines = {};
|
|
236
280
|
const renderSSRCommand = async (ctx, componentPath, componentName, props = {}) => {
|
|
237
281
|
const absoluteComponentPath = resolve(process.cwd(), componentPath);
|
|
238
|
-
const
|
|
239
|
-
if (!viteServer.config.define) return;
|
|
240
|
-
for (const [key, value] of Object.entries(viteServer.config.env)) viteServer.config.define[`__vite_ssr_import_meta__.env.${key}`] = JSON.stringify(value);
|
|
241
|
-
const Component = (await viteServer.ssrLoadModule(absoluteComponentPath))[componentName];
|
|
282
|
+
const Component = (await ctx.project.vite.ssrLoadModule(absoluteComponentPath))[componentName];
|
|
242
283
|
if (!Component) throw new Error(`Component "${componentName}" not found in ${absoluteComponentPath}`);
|
|
243
284
|
return await renderComponentToSSR(ctx, Component, props);
|
|
244
285
|
};
|
|
245
286
|
const renderSSRLocalCommand = async (ctx, testFilePath, componentName, allLocalComponents, props = {}) => {
|
|
246
287
|
const viteServer = ctx.project.vite;
|
|
247
|
-
if (!viteServer.config.define) return;
|
|
248
|
-
for (const [key, value] of Object.entries(viteServer.config.env)) viteServer.config.define[`__vite_ssr_import_meta__.env.${key}`] = JSON.stringify(value);
|
|
249
288
|
const { readFileSync, writeFileSync, unlinkSync } = await import("node:fs");
|
|
250
289
|
const { dirname: dirname$1, join } = await import("node:path");
|
|
251
290
|
const tempFileName = `ssr-test-${Date.now()}-${Math.random().toString(36).slice(2, 11)}.tsx`;
|
|
@@ -254,16 +293,28 @@ const renderSSRLocalCommand = async (ctx, testFilePath, componentName, allLocalC
|
|
|
254
293
|
const originalContent = readFileSync(testFilePath, "utf8");
|
|
255
294
|
const ast = parseSync(testFilePath, originalContent);
|
|
256
295
|
const s = new MagicString(originalContent);
|
|
296
|
+
const strippedIds = /* @__PURE__ */ new Set();
|
|
257
297
|
function cleanTestFile(node) {
|
|
258
|
-
if (isImportDeclaration(node)) {
|
|
259
|
-
const
|
|
260
|
-
|
|
298
|
+
if (isImportDeclaration(node) && isBrowserOnlySource(node.source?.value)) {
|
|
299
|
+
for (const spec of node.specifiers || []) if (spec.local?.name) strippedIds.add(spec.local.name);
|
|
300
|
+
s.remove(node.start, node.end);
|
|
301
|
+
return;
|
|
261
302
|
}
|
|
262
303
|
if (isExpressionStatement(node) && node.expression?.type === "CallExpression") {
|
|
263
304
|
const callExpr = node.expression;
|
|
264
305
|
if (callExpr.callee.type === "Identifier") {
|
|
265
306
|
const calleeName = callExpr.callee.name;
|
|
266
|
-
if (calleeName === "test" || calleeName === "describe" || calleeName === "it")
|
|
307
|
+
if (calleeName === "test" || calleeName === "describe" || calleeName === "it") {
|
|
308
|
+
s.remove(node.start, node.end);
|
|
309
|
+
return;
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
if (isVariableDeclaration(node)) {
|
|
314
|
+
if (node.declarations.every((d) => referencesStrippedId(d.init, strippedIds))) {
|
|
315
|
+
for (const d of node.declarations) if (d.id.type === "Identifier") strippedIds.add(d.id.name);
|
|
316
|
+
s.remove(node.start, node.end);
|
|
317
|
+
return;
|
|
267
318
|
}
|
|
268
319
|
}
|
|
269
320
|
traverseChildren(node, cleanTestFile);
|
|
@@ -291,6 +342,12 @@ function testSSR() {
|
|
|
291
342
|
return {
|
|
292
343
|
name: "vitest:ssr-transform",
|
|
293
344
|
enforce: "pre",
|
|
345
|
+
config(config) {
|
|
346
|
+
if (config.define) userDefines = {
|
|
347
|
+
...userDefines,
|
|
348
|
+
...config.define
|
|
349
|
+
};
|
|
350
|
+
},
|
|
294
351
|
transform: {
|
|
295
352
|
filter: {
|
|
296
353
|
id: isJSorTS,
|
|
@@ -377,7 +434,9 @@ function testSSR() {
|
|
|
377
434
|
}
|
|
378
435
|
},
|
|
379
436
|
configResolved(config) {
|
|
380
|
-
|
|
437
|
+
if (!config.define) config.define = {};
|
|
438
|
+
for (const [key, value] of Object.entries(userDefines)) if (config.define) config.define[key] = value;
|
|
439
|
+
for (const [key, value] of Object.entries(config.env)) if (config.define) config.define[`__vite_ssr_import_meta__.env.${key}`] = JSON.stringify(value);
|
|
381
440
|
if (config.test?.browser?.enabled) config.test.browser.commands = {
|
|
382
441
|
...config.test.browser.commands,
|
|
383
442
|
renderSSR: renderSSRCommand,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vitest-browser-qwik",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.2",
|
|
4
4
|
"description": "Render Qwik components using Vitest Browser Mode",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -40,26 +40,26 @@
|
|
|
40
40
|
},
|
|
41
41
|
"dependencies": {
|
|
42
42
|
"@oxc-project/types": "^0.95.0",
|
|
43
|
-
"magic-string": "^0.30.
|
|
43
|
+
"magic-string": "^0.30.17",
|
|
44
44
|
"oxc-parser": "^0.95.0",
|
|
45
|
-
"oxc-resolver": "^11.
|
|
45
|
+
"oxc-resolver": "^11.11.1"
|
|
46
46
|
},
|
|
47
47
|
"peerDependencies": {
|
|
48
|
-
"@
|
|
48
|
+
"@qwik.dev/core": "*",
|
|
49
49
|
"vite": ">=6.3.5",
|
|
50
|
-
"vitest": "^4.0.
|
|
50
|
+
"vitest": "^4.0.18"
|
|
51
51
|
},
|
|
52
52
|
"devDependencies": {
|
|
53
53
|
"@biomejs/biome": "2.0.0",
|
|
54
|
-
"@
|
|
55
|
-
"@
|
|
56
|
-
"@types/node": "^22.
|
|
54
|
+
"@playwright/test": "see flake.nix",
|
|
55
|
+
"@qwik.dev/core": "*",
|
|
56
|
+
"@types/node": "^22.15.17",
|
|
57
57
|
"@vitest/browser-playwright": "^4.0.18",
|
|
58
|
-
"bumpp": "^10.3.2",
|
|
59
|
-
"ignore": "^7.0.5",
|
|
60
58
|
"magic-regexp": "^0.10.0",
|
|
61
|
-
"
|
|
62
|
-
"
|
|
59
|
+
"bumpp": "^10.1.0",
|
|
60
|
+
"ignore": "^7.0.5",
|
|
61
|
+
"tsdown": "^0.15.9",
|
|
62
|
+
"typescript": "^5.8.3",
|
|
63
63
|
"vitest": "^4.0.18"
|
|
64
64
|
},
|
|
65
65
|
"scripts": {
|