@testspectra/react 1.1.8-rc.29 → 1.1.8-rc.30
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/cli.js +19 -3
- package/dist/index.js +242 -36
- package/dist/runner.js +1 -1
- package/dist/setup.js +41 -81
- package/package.json +2 -2
package/dist/cli.js
CHANGED
|
@@ -22,6 +22,9 @@ function buildAutoImportManifest(root) {
|
|
|
22
22
|
const entries = [];
|
|
23
23
|
for (const name of readdirSync(dir, { withFileTypes: true })) {
|
|
24
24
|
if (!name.isDirectory()) continue;
|
|
25
|
+
if (name.name.startsWith(".") || name.name === "node_modules" || name.name === "dist" || name.name === "__screenshots__" || name.name === "coverage") {
|
|
26
|
+
continue;
|
|
27
|
+
}
|
|
25
28
|
for (const platform of ["web", "common"]) {
|
|
26
29
|
for (const ext of ["tsx", "ts"]) {
|
|
27
30
|
const candidate = path.join(dir, name.name, filePattern(platform).replace(/\.tsx?$/, `.${ext}`));
|
|
@@ -38,7 +41,7 @@ function buildAutoImportManifest(root) {
|
|
|
38
41
|
function scanFixtures() {
|
|
39
42
|
const dir = path.join(root, "fixtures");
|
|
40
43
|
if (!existsSync(dir)) return [];
|
|
41
|
-
return readdirSync(dir).filter((f) => f.endsWith(".json")).map((f) => ({ name: f.replace(/\.json$/, ""), path: path.join(dir, f).replace(/\\/g, "/") }));
|
|
44
|
+
return readdirSync(dir).filter((f) => f.endsWith(".json") && !f.startsWith(".")).map((f) => ({ name: f.replace(/\.json$/, ""), path: path.join(dir, f).replace(/\\/g, "/") }));
|
|
42
45
|
}
|
|
43
46
|
return {
|
|
44
47
|
pageObjects: scanEntityDir("page-objects", (p) => `${p}.tsx`),
|
|
@@ -60,7 +63,7 @@ import { defineConfig } from '${vitestConfigUrl}';
|
|
|
60
63
|
${opts.semanticReporter ? `import { SpectraVitestReporter } from '${reporterUrl}';` : ""}
|
|
61
64
|
|
|
62
65
|
export default defineConfig({
|
|
63
|
-
root: ${JSON.stringify(
|
|
66
|
+
root: ${JSON.stringify(normalizedTestRoot)},
|
|
64
67
|
plugins: [react()],
|
|
65
68
|
define: {
|
|
66
69
|
__TESTSPECTRA_AUTO_IMPORT_MANIFEST__: ${JSON.stringify(JSON.stringify(manifest))},
|
|
@@ -92,7 +95,20 @@ export default defineConfig({
|
|
|
92
95
|
${opts.outputFile ? `['json', { outputFile: ${JSON.stringify(opts.outputFile)} }],` : ""}
|
|
93
96
|
]` : opts.outputFile ? `['default', ['json', { outputFile: ${JSON.stringify(opts.outputFile)} }]]` : "'default'"},
|
|
94
97
|
dir: ${JSON.stringify(normalizedTestRoot)},
|
|
95
|
-
include: [
|
|
98
|
+
include: [
|
|
99
|
+
'specs/**/web.test.tsx',
|
|
100
|
+
'specs/**/*.test.tsx',
|
|
101
|
+
'specs/**/web.test.ts',
|
|
102
|
+
'specs/**/*.test.ts',
|
|
103
|
+
],
|
|
104
|
+
exclude: [
|
|
105
|
+
'**/node_modules/**',
|
|
106
|
+
'**/dist/**',
|
|
107
|
+
'**/__screenshots__/**',
|
|
108
|
+
'**/.testspectra/**',
|
|
109
|
+
'**/.git/**',
|
|
110
|
+
'**/coverage/**',
|
|
111
|
+
],
|
|
96
112
|
browser: {
|
|
97
113
|
enabled: true,
|
|
98
114
|
provider: 'playwright',
|
package/dist/index.js
CHANGED
|
@@ -322,12 +322,26 @@ var DomDriverBridge = class {
|
|
|
322
322
|
};
|
|
323
323
|
|
|
324
324
|
// src/mount.ts
|
|
325
|
-
import * as ReactDOMClient from "react-dom/client";
|
|
326
325
|
var ROOT_ID = "spectra-root";
|
|
327
326
|
var cachedRoot = null;
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
327
|
+
var dynamicCreateRoot = null;
|
|
328
|
+
async function getCreateRoot() {
|
|
329
|
+
if (typeof window !== "undefined" && window.__TESTSPECTRA_CREATE_ROOT__) {
|
|
330
|
+
return window.__TESTSPECTRA_CREATE_ROOT__;
|
|
331
|
+
}
|
|
332
|
+
if (dynamicCreateRoot) {
|
|
333
|
+
return dynamicCreateRoot;
|
|
334
|
+
}
|
|
335
|
+
try {
|
|
336
|
+
const mod = await import(
|
|
337
|
+
/* @vite-ignore */
|
|
338
|
+
"react-dom/client"
|
|
339
|
+
);
|
|
340
|
+
dynamicCreateRoot = mod.createRoot || mod.default?.createRoot || mod.default;
|
|
341
|
+
if (dynamicCreateRoot) return dynamicCreateRoot;
|
|
342
|
+
} catch {
|
|
343
|
+
}
|
|
344
|
+
throw new Error("Could not find react-dom/client createRoot. Ensure react-dom is installed in your project.");
|
|
331
345
|
}
|
|
332
346
|
function ensureContainer() {
|
|
333
347
|
let container = document.getElementById(ROOT_ID);
|
|
@@ -341,7 +355,7 @@ function ensureContainer() {
|
|
|
341
355
|
async function mount(element) {
|
|
342
356
|
const container = ensureContainer();
|
|
343
357
|
if (!cachedRoot) {
|
|
344
|
-
const createRootFn = getCreateRoot();
|
|
358
|
+
const createRootFn = await getCreateRoot();
|
|
345
359
|
cachedRoot = createRootFn(container);
|
|
346
360
|
}
|
|
347
361
|
const componentName = (typeof element.type === "string" ? element.type : element.type?.displayName || element.type?.name) || "component";
|
|
@@ -350,7 +364,7 @@ async function mount(element) {
|
|
|
350
364
|
{ type: "action", key: "mount", target: componentName, args: [] },
|
|
351
365
|
async () => {
|
|
352
366
|
cachedRoot.render(element);
|
|
353
|
-
await new Promise((r) =>
|
|
367
|
+
await new Promise((r) => setTimeout(r, 20));
|
|
354
368
|
return globalThis.__TS_RUNTIME__.createElementProxy(`#${ROOT_ID}`);
|
|
355
369
|
}
|
|
356
370
|
);
|
|
@@ -380,6 +394,9 @@ function buildAutoImportManifest(root) {
|
|
|
380
394
|
const entries = [];
|
|
381
395
|
for (const name of readdirSync(dir, { withFileTypes: true })) {
|
|
382
396
|
if (!name.isDirectory()) continue;
|
|
397
|
+
if (name.name.startsWith(".") || name.name === "node_modules" || name.name === "dist" || name.name === "__screenshots__" || name.name === "coverage") {
|
|
398
|
+
continue;
|
|
399
|
+
}
|
|
383
400
|
for (const platform of ["web", "common"]) {
|
|
384
401
|
for (const ext of ["tsx", "ts"]) {
|
|
385
402
|
const candidate = path.join(dir, name.name, filePattern(platform).replace(/\.tsx?$/, `.${ext}`));
|
|
@@ -396,7 +413,7 @@ function buildAutoImportManifest(root) {
|
|
|
396
413
|
function scanFixtures() {
|
|
397
414
|
const dir = path.join(root, "fixtures");
|
|
398
415
|
if (!existsSync(dir)) return [];
|
|
399
|
-
return readdirSync(dir).filter((f) => f.endsWith(".json")).map((f) => ({ name: f.replace(/\.json$/, ""), path: path.join(dir, f).replace(/\\/g, "/") }));
|
|
416
|
+
return readdirSync(dir).filter((f) => f.endsWith(".json") && !f.startsWith(".")).map((f) => ({ name: f.replace(/\.json$/, ""), path: path.join(dir, f).replace(/\\/g, "/") }));
|
|
400
417
|
}
|
|
401
418
|
return {
|
|
402
419
|
pageObjects: scanEntityDir("page-objects", (p) => `${p}.tsx`),
|
|
@@ -418,7 +435,7 @@ import { defineConfig } from '${vitestConfigUrl}';
|
|
|
418
435
|
${opts.semanticReporter ? `import { SpectraVitestReporter } from '${reporterUrl}';` : ""}
|
|
419
436
|
|
|
420
437
|
export default defineConfig({
|
|
421
|
-
root: ${JSON.stringify(
|
|
438
|
+
root: ${JSON.stringify(normalizedTestRoot)},
|
|
422
439
|
plugins: [react()],
|
|
423
440
|
define: {
|
|
424
441
|
__TESTSPECTRA_AUTO_IMPORT_MANIFEST__: ${JSON.stringify(JSON.stringify(manifest))},
|
|
@@ -450,7 +467,20 @@ export default defineConfig({
|
|
|
450
467
|
${opts.outputFile ? `['json', { outputFile: ${JSON.stringify(opts.outputFile)} }],` : ""}
|
|
451
468
|
]` : opts.outputFile ? `['default', ['json', { outputFile: ${JSON.stringify(opts.outputFile)} }]]` : "'default'"},
|
|
452
469
|
dir: ${JSON.stringify(normalizedTestRoot)},
|
|
453
|
-
include: [
|
|
470
|
+
include: [
|
|
471
|
+
'specs/**/web.test.tsx',
|
|
472
|
+
'specs/**/*.test.tsx',
|
|
473
|
+
'specs/**/web.test.ts',
|
|
474
|
+
'specs/**/*.test.ts',
|
|
475
|
+
],
|
|
476
|
+
exclude: [
|
|
477
|
+
'**/node_modules/**',
|
|
478
|
+
'**/dist/**',
|
|
479
|
+
'**/__screenshots__/**',
|
|
480
|
+
'**/.testspectra/**',
|
|
481
|
+
'**/.git/**',
|
|
482
|
+
'**/coverage/**',
|
|
483
|
+
],
|
|
454
484
|
browser: {
|
|
455
485
|
enabled: true,
|
|
456
486
|
provider: 'playwright',
|
|
@@ -621,29 +651,96 @@ import http from "node:http";
|
|
|
621
651
|
import path3 from "node:path";
|
|
622
652
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
623
653
|
import * as esbuild from "esbuild";
|
|
654
|
+
import { createRequire as createRequire2 } from "node:module";
|
|
624
655
|
var bundleCache = /* @__PURE__ */ new Map();
|
|
625
656
|
var inFlightBuilds = /* @__PURE__ */ new Map();
|
|
657
|
+
var cssInjectorPlugin = {
|
|
658
|
+
name: "spectra-css-injector",
|
|
659
|
+
setup(build2) {
|
|
660
|
+
build2.onLoad({ filter: /\.(css|scss|sass|less)$/ }, async (args) => {
|
|
661
|
+
try {
|
|
662
|
+
const raw = await fs.promises.readFile(args.path, "utf8");
|
|
663
|
+
const escaped = JSON.stringify(raw);
|
|
664
|
+
const sourcePath = JSON.stringify(args.path);
|
|
665
|
+
return {
|
|
666
|
+
contents: `
|
|
667
|
+
if (typeof document !== 'undefined') {
|
|
668
|
+
const style = document.createElement('style');
|
|
669
|
+
style.setAttribute('data-spectra-css', ${sourcePath});
|
|
670
|
+
style.textContent = ${escaped};
|
|
671
|
+
document.head.appendChild(style);
|
|
672
|
+
}
|
|
673
|
+
`,
|
|
674
|
+
loader: "js"
|
|
675
|
+
};
|
|
676
|
+
} catch (e) {
|
|
677
|
+
return {
|
|
678
|
+
contents: `/* failed to load css: ${String(e)} */`,
|
|
679
|
+
loader: "js"
|
|
680
|
+
};
|
|
681
|
+
}
|
|
682
|
+
});
|
|
683
|
+
}
|
|
684
|
+
};
|
|
685
|
+
var SHARED_REACT_EXTERNALS = [
|
|
686
|
+
"react",
|
|
687
|
+
"react/jsx-runtime",
|
|
688
|
+
"react/jsx-dev-runtime",
|
|
689
|
+
"react/compiler-runtime",
|
|
690
|
+
"react-dom",
|
|
691
|
+
"react-dom/client"
|
|
692
|
+
];
|
|
626
693
|
async function buildBundle(entrypoint, opts = {}) {
|
|
627
|
-
const
|
|
628
|
-
|
|
694
|
+
const mergedExternals = opts.skipSharedExternals ? opts.external ?? [] : Array.from(/* @__PURE__ */ new Set([...opts.external ?? [], ...SHARED_REACT_EXTERNALS]));
|
|
695
|
+
const buildOptions = {
|
|
629
696
|
bundle: true,
|
|
630
697
|
format: "esm",
|
|
631
698
|
platform: "browser",
|
|
632
699
|
target: "esnext",
|
|
633
|
-
external:
|
|
700
|
+
external: mergedExternals,
|
|
634
701
|
minify: false,
|
|
635
702
|
write: false,
|
|
636
|
-
|
|
703
|
+
plugins: [cssInjectorPlugin],
|
|
704
|
+
loader: {
|
|
705
|
+
".png": "dataurl",
|
|
706
|
+
".jpg": "dataurl",
|
|
707
|
+
".jpeg": "dataurl",
|
|
708
|
+
".gif": "dataurl",
|
|
709
|
+
".webp": "dataurl",
|
|
710
|
+
".svg": "text",
|
|
711
|
+
".json": "json",
|
|
712
|
+
".woff": "dataurl",
|
|
713
|
+
".woff2": "dataurl",
|
|
714
|
+
".ttf": "dataurl",
|
|
715
|
+
".eot": "dataurl"
|
|
716
|
+
},
|
|
717
|
+
logLevel: "error",
|
|
718
|
+
logOverride: {
|
|
719
|
+
"ignored-bare-import": "silent"
|
|
720
|
+
},
|
|
637
721
|
sourcemap: "inline",
|
|
638
|
-
// React 17+ automatic JSX runtime — test files do not need `import React from 'react'`.
|
|
639
722
|
jsx: "automatic",
|
|
640
723
|
jsxImportSource: "react"
|
|
641
|
-
}
|
|
724
|
+
};
|
|
725
|
+
if (opts.virtualContents) {
|
|
726
|
+
buildOptions.stdin = {
|
|
727
|
+
contents: opts.virtualContents,
|
|
728
|
+
resolveDir: opts.resolveDir ?? process.cwd(),
|
|
729
|
+
sourcefile: entrypoint,
|
|
730
|
+
loader: "ts"
|
|
731
|
+
};
|
|
732
|
+
} else {
|
|
733
|
+
buildOptions.entryPoints = [entrypoint];
|
|
734
|
+
}
|
|
735
|
+
const result = await esbuild.build(buildOptions);
|
|
642
736
|
if (result.errors.length > 0) {
|
|
643
737
|
const msgs = result.errors.map((e) => e.text).join("\n");
|
|
644
738
|
throw new Error(`esbuild failed for ${entrypoint}:
|
|
645
739
|
${msgs}`);
|
|
646
740
|
}
|
|
741
|
+
if (!result.outputFiles || result.outputFiles.length === 0) {
|
|
742
|
+
throw new Error(`esbuild produced no output files for ${entrypoint}`);
|
|
743
|
+
}
|
|
647
744
|
return result.outputFiles[0].text;
|
|
648
745
|
}
|
|
649
746
|
async function getCachedBundle(cacheKey, entrypoint, opts) {
|
|
@@ -665,16 +762,134 @@ async function getCachedBundle(cacheKey, entrypoint, opts) {
|
|
|
665
762
|
inFlightBuilds.set(cacheKey, buildPromise);
|
|
666
763
|
return buildPromise;
|
|
667
764
|
}
|
|
765
|
+
function buildCombinedReactBundleCode(testRoot) {
|
|
766
|
+
let req;
|
|
767
|
+
try {
|
|
768
|
+
req = createRequire2(path3.join(testRoot, "package.json"));
|
|
769
|
+
} catch {
|
|
770
|
+
req = createRequire2(import.meta.url);
|
|
771
|
+
}
|
|
772
|
+
const resolveSafe = (specifier) => {
|
|
773
|
+
try {
|
|
774
|
+
return req.resolve(specifier);
|
|
775
|
+
} catch {
|
|
776
|
+
try {
|
|
777
|
+
return createRequire2(import.meta.url).resolve(specifier);
|
|
778
|
+
} catch {
|
|
779
|
+
return null;
|
|
780
|
+
}
|
|
781
|
+
}
|
|
782
|
+
};
|
|
783
|
+
const loadSafe = (specifier) => {
|
|
784
|
+
try {
|
|
785
|
+
return req(specifier);
|
|
786
|
+
} catch {
|
|
787
|
+
try {
|
|
788
|
+
return createRequire2(import.meta.url)(specifier);
|
|
789
|
+
} catch {
|
|
790
|
+
return null;
|
|
791
|
+
}
|
|
792
|
+
}
|
|
793
|
+
};
|
|
794
|
+
const reactPath = resolveSafe("react");
|
|
795
|
+
const jsxPath = resolveSafe("react/jsx-runtime");
|
|
796
|
+
const compilerPath = resolveSafe("react/compiler-runtime");
|
|
797
|
+
const domPath = resolveSafe("react-dom");
|
|
798
|
+
const domClientPath = resolveSafe("react-dom/client");
|
|
799
|
+
const reactPkg = loadSafe("react") ?? {};
|
|
800
|
+
const jsxPkg = loadSafe("react/jsx-runtime") ?? {};
|
|
801
|
+
const compilerPkg = loadSafe("react/compiler-runtime") ?? {};
|
|
802
|
+
const domPkg = loadSafe("react-dom") ?? {};
|
|
803
|
+
const domClientPkg = loadSafe("react-dom/client") ?? {};
|
|
804
|
+
const allKeys = /* @__PURE__ */ new Set();
|
|
805
|
+
const exportLines = [];
|
|
806
|
+
const addExports = (varName, pkg) => {
|
|
807
|
+
const keys = Object.keys(pkg).filter((k) => /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(k));
|
|
808
|
+
for (const k of keys) {
|
|
809
|
+
if (!allKeys.has(k)) {
|
|
810
|
+
allKeys.add(k);
|
|
811
|
+
exportLines.push(`export const ${k} = (${varName} as any).${k};`);
|
|
812
|
+
}
|
|
813
|
+
}
|
|
814
|
+
};
|
|
815
|
+
addExports("_react", reactPkg);
|
|
816
|
+
addExports("_jsx", jsxPkg);
|
|
817
|
+
addExports("_compiler", compilerPkg);
|
|
818
|
+
addExports("_dom", domPkg);
|
|
819
|
+
addExports("_domClient", domClientPkg);
|
|
820
|
+
const importParts = [];
|
|
821
|
+
const initParts = [];
|
|
822
|
+
if (reactPath) {
|
|
823
|
+
importParts.push(`import * as _rawReact from ${JSON.stringify(reactPath)};`);
|
|
824
|
+
initParts.push(`const _react: any = (_rawReact as any).default || _rawReact;`);
|
|
825
|
+
} else {
|
|
826
|
+
initParts.push(`const _react: any = {};`);
|
|
827
|
+
}
|
|
828
|
+
if (jsxPath) {
|
|
829
|
+
importParts.push(`import * as _rawJsx from ${JSON.stringify(jsxPath)};`);
|
|
830
|
+
initParts.push(`const _jsx: any = (_rawJsx as any).default || _rawJsx;`);
|
|
831
|
+
} else {
|
|
832
|
+
initParts.push(`const _jsx: any = {};`);
|
|
833
|
+
}
|
|
834
|
+
if (compilerPath) {
|
|
835
|
+
importParts.push(`import * as _rawCompiler from ${JSON.stringify(compilerPath)};`);
|
|
836
|
+
initParts.push(`const _compiler: any = (_rawCompiler as any).default || _rawCompiler;`);
|
|
837
|
+
} else {
|
|
838
|
+
initParts.push(`const _compiler: any = {};`);
|
|
839
|
+
}
|
|
840
|
+
if (domPath) {
|
|
841
|
+
importParts.push(`import * as _rawDom from ${JSON.stringify(domPath)};`);
|
|
842
|
+
initParts.push(`const _dom: any = (_rawDom as any).default || _rawDom;`);
|
|
843
|
+
} else {
|
|
844
|
+
initParts.push(`const _dom: any = {};`);
|
|
845
|
+
}
|
|
846
|
+
if (domClientPath) {
|
|
847
|
+
importParts.push(`import * as _rawDomClient from ${JSON.stringify(domClientPath)};`);
|
|
848
|
+
initParts.push(`const _domClient: any = (_rawDomClient as any).default || _rawDomClient;`);
|
|
849
|
+
} else {
|
|
850
|
+
initParts.push(`const _domClient: any = {};`);
|
|
851
|
+
}
|
|
852
|
+
return `
|
|
853
|
+
${importParts.join("\n")}
|
|
854
|
+
|
|
855
|
+
${initParts.join("\n")}
|
|
856
|
+
|
|
857
|
+
${exportLines.join("\n")}
|
|
858
|
+
|
|
859
|
+
export default _react;
|
|
860
|
+
`;
|
|
861
|
+
}
|
|
668
862
|
function buildHarnessHtml(manifest, stepDelayMs) {
|
|
669
863
|
const manifestJson = JSON.stringify(manifest);
|
|
670
864
|
return `<!DOCTYPE html>
|
|
671
865
|
<html>
|
|
672
|
-
<head
|
|
866
|
+
<head>
|
|
867
|
+
<meta charset="utf-8">
|
|
868
|
+
<title>Spectra Component Test</title>
|
|
869
|
+
<script type="importmap">
|
|
870
|
+
{
|
|
871
|
+
"imports": {
|
|
872
|
+
"react": "/@modules/react.js",
|
|
873
|
+
"react/jsx-runtime": "/@modules/react.js",
|
|
874
|
+
"react/jsx-dev-runtime": "/@modules/react.js",
|
|
875
|
+
"react/compiler-runtime": "/@modules/react.js",
|
|
876
|
+
"react-dom": "/@modules/react.js",
|
|
877
|
+
"react-dom/client": "/@modules/react.js"
|
|
878
|
+
}
|
|
879
|
+
}
|
|
880
|
+
</script>
|
|
881
|
+
</head>
|
|
673
882
|
<body>
|
|
674
|
-
<script>
|
|
883
|
+
<script type="module">
|
|
675
884
|
window.__TESTSPECTRA_AUTO_IMPORT_MANIFEST__ = ${manifestJson};
|
|
676
885
|
window.__TESTSPECTRA_STEP_DELAY_MS__ = ${JSON.stringify(stepDelayMs)};
|
|
677
886
|
window.__TESTSPECTRA_SEMANTIC_REPORTER__ = true;
|
|
887
|
+
try {
|
|
888
|
+
const rdc = await import('react-dom/client');
|
|
889
|
+
window.__TESTSPECTRA_CREATE_ROOT__ = rdc.createRoot || rdc.default?.createRoot || rdc.default;
|
|
890
|
+
} catch (e) {
|
|
891
|
+
console.warn('[TestSpectra] createRoot pre-load warning:', e);
|
|
892
|
+
}
|
|
678
893
|
</script>
|
|
679
894
|
<script type="module" src="/runner.js"></script>
|
|
680
895
|
<script type="module" src="/setup.js"></script>
|
|
@@ -709,28 +924,20 @@ function makeHandler(testRoot, packageDir, harnessHtml, setupEntrypoint, runnerE
|
|
|
709
924
|
respond(res, 200, "text/html; charset=utf-8", harnessHtml);
|
|
710
925
|
return;
|
|
711
926
|
}
|
|
712
|
-
if (url.pathname.startsWith("/@
|
|
713
|
-
const rawFsPath = url.pathname.slice("/@fs/".length);
|
|
714
|
-
const fsPath = rawFsPath.startsWith("/") ? rawFsPath : `/${rawFsPath}`;
|
|
715
|
-
const resolved = path3.resolve(fsPath);
|
|
716
|
-
if (resolved.endsWith(".json")) {
|
|
717
|
-
try {
|
|
718
|
-
const raw = fs.readFileSync(resolved, "utf-8");
|
|
719
|
-
respond(res, 200, "application/json; charset=utf-8", raw);
|
|
720
|
-
} catch (err) {
|
|
721
|
-
respond(res, 500, "application/json", JSON.stringify({ error: String(err) }));
|
|
722
|
-
}
|
|
723
|
-
return;
|
|
724
|
-
}
|
|
927
|
+
if (url.pathname === "/@modules/react.js" || url.pathname.startsWith("/@modules/react")) {
|
|
725
928
|
try {
|
|
726
|
-
const js = await getCachedBundle(
|
|
929
|
+
const js = await getCachedBundle("module_react_combined", path3.join(testRoot, "__virtual_react_combined.js"), {
|
|
930
|
+
virtualContents: buildCombinedReactBundleCode(testRoot),
|
|
931
|
+
resolveDir: testRoot,
|
|
932
|
+
skipSharedExternals: true
|
|
933
|
+
});
|
|
727
934
|
respond(res, 200, "application/javascript; charset=utf-8", js);
|
|
728
935
|
} catch (err) {
|
|
729
936
|
respond(
|
|
730
937
|
res,
|
|
731
|
-
|
|
938
|
+
500,
|
|
732
939
|
"application/javascript",
|
|
733
|
-
`throw new Error(${JSON.stringify(`
|
|
940
|
+
`throw new Error(${JSON.stringify(`Failed to bundle shared React runtime: ${String(err)}`)});`
|
|
734
941
|
);
|
|
735
942
|
}
|
|
736
943
|
return;
|
|
@@ -761,9 +968,8 @@ function makeHandler(testRoot, packageDir, harnessHtml, setupEntrypoint, runnerE
|
|
|
761
968
|
}
|
|
762
969
|
const testFilePath = decodeURIComponent(encodedFile);
|
|
763
970
|
const resolved = path3.resolve(testFilePath);
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
respond(res, 403, "text/plain", "Forbidden: path outside test root");
|
|
971
|
+
if (!fs.existsSync(resolved)) {
|
|
972
|
+
respond(res, 404, "text/plain", `File not found: ${testFilePath}`);
|
|
767
973
|
return;
|
|
768
974
|
}
|
|
769
975
|
if (resolved.endsWith(".json")) {
|
package/dist/runner.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
var
|
|
1
|
+
var T=[],c=[],f=[];globalThis.it=function(n,o){T.push({title:n,fn:o})};globalThis.beforeEach=function(n){c.push(n)};globalThis.afterEach=function(n){f.push(n)};function R(){let n=globalThis.Spectra;if(typeof n?.unmount=="function")n.unmount();else{let o=document.getElementById("spectra-root");o&&(o.innerHTML="")}}function e(n,o){console.log(`[${n}] ${o}`)}globalThis.__SPECTRA_RUNNER__={async run(n,o){e("TESTSPECTRA_SUITE_START",JSON.stringify({file:n}));let u=globalThis.__bootstrapAutoImports;if(typeof u=="function")try{await u(o)}catch(t){return e("TESTSPECTRA_SUITE_ERROR",JSON.stringify({file:n,error:`Failed to bootstrap auto imports: ${String(t)}`})),{passed:0,failed:1}}let l=encodeURIComponent(n);try{await import(`/test?file=${l}`)}catch(t){return e("TESTSPECTRA_SUITE_ERROR",JSON.stringify({file:n,error:String(t)})),{passed:0,failed:1}}let i=0,a=0;for(let{title:t,fn:d}of T){e("TESTSPECTRA_TEST_START",t),globalThis.__CURRENT_TEST_TITLE__=t;let S={task:{name:t}},E=performance.now();try{for(let s of c)await s(S);await d();for(let s of f)await s(S);let r=Math.round(performance.now()-E);e("TESTSPECTRA_TEST_PASS",`${t} (${r}ms)`),i++}catch(r){let s=Math.round(performance.now()-E),g=r instanceof Error?r.message:String(r);e("TESTSPECTRA_TEST_ERROR",g),e("TESTSPECTRA_TEST_FAIL",`${t} (${s}ms)`),a++}finally{R()}}return T.length=0,c.length=0,f.length=0,e("TESTSPECTRA_SUITE_END",JSON.stringify({file:n,passed:i,failed:a})),{passed:i,failed:a}}};
|