@testspectra/react 1.1.8-rc.25 → 1.1.8-rc.28

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.js CHANGED
@@ -1,9 +1,9 @@
1
1
  // src/dom_driver_bridge.ts
2
+ import { userEvent } from "@testing-library/user-event";
2
3
  var cachedUser = null;
3
- async function getUser() {
4
+ function getUser() {
4
5
  if (!cachedUser) {
5
- const { userEvent } = await import("@testing-library/user-event");
6
- cachedUser = userEvent.setup();
6
+ cachedUser = userEvent.setup({ delay: null });
7
7
  }
8
8
  return cachedUser;
9
9
  }
@@ -34,6 +34,8 @@ var DomDriverBridge = class {
34
34
  /** Polls until `selector` resolves to an element, then returns it — shared by every
35
35
  * interaction method below (user-event needs a real element handle, not a selector string). */
36
36
  async waitForEl(selector, index) {
37
+ const immediate = resolveEl(selector, index);
38
+ if (immediate) return immediate;
37
39
  let found = null;
38
40
  const ok = await globalThis.__TS_RUNTIME__.pollCondition(async () => {
39
41
  found = resolveEl(selector, index);
@@ -80,20 +82,20 @@ var DomDriverBridge = class {
80
82
  // ─── Element Actions ─────────────────────────────────────────────────────
81
83
  async click(selector, index = null) {
82
84
  const el = await this.waitForEl(selector, index);
83
- await (await getUser()).click(el);
85
+ await getUser().click(el);
84
86
  }
85
87
  async doubleClick(selector, index = null) {
86
88
  const el = await this.waitForEl(selector, index);
87
- await (await getUser()).dblClick(el);
89
+ await getUser().dblClick(el);
88
90
  }
89
91
  async rightClick(selector, index = null) {
90
92
  const el = await this.waitForEl(selector, index);
91
- await (await getUser()).pointer({ keys: "[MouseRight]", target: el });
93
+ await getUser().pointer({ keys: "[MouseRight]", target: el });
92
94
  }
93
95
  async setValue(selector, value, index = null) {
94
96
  const el = await this.waitForEl(selector, index);
95
97
  if (!isFormEl(el)) throw new Error("Element is not a form field: " + JSON.stringify(selector));
96
- const u = await getUser();
98
+ const u = getUser();
97
99
  await u.clear(el);
98
100
  if (String(value).length > 0) {
99
101
  await u.type(el, String(value));
@@ -101,7 +103,7 @@ var DomDriverBridge = class {
101
103
  }
102
104
  async clearValue(selector, index = null) {
103
105
  const el = await this.waitForEl(selector, index);
104
- await (await getUser()).clear(el);
106
+ await getUser().clear(el);
105
107
  }
106
108
  async select(selector, option, index = null) {
107
109
  const ok = await globalThis.__TS_RUNTIME__.pollCondition(async () => {
@@ -121,7 +123,7 @@ var DomDriverBridge = class {
121
123
  }
122
124
  async hover(selector, index = null) {
123
125
  const el = await this.waitForEl(selector, index);
124
- await (await getUser()).hover(el);
126
+ await getUser().hover(el);
125
127
  }
126
128
  async focus(selector, index = null) {
127
129
  const ok = await globalThis.__TS_RUNTIME__.pollCondition(async () => {
@@ -345,7 +347,7 @@ async function mount(element) {
345
347
  { type: "action", key: "mount", target: componentName, args: [] },
346
348
  async () => {
347
349
  cachedRoot.render(element);
348
- await new Promise((r) => setTimeout(r, 0));
350
+ await new Promise((r) => queueMicrotask(r));
349
351
  return globalThis.__TS_RUNTIME__.createElementProxy(`#${ROOT_ID}`);
350
352
  }
351
353
  );
@@ -363,6 +365,11 @@ import { createRequire } from "node:module";
363
365
  import os from "node:os";
364
366
  import path from "node:path";
365
367
  import { pathToFileURL } from "node:url";
368
+ function resolveEsmUrl(specifier, packageDir) {
369
+ const cjsPath = createRequire(path.join(packageDir, "package.json")).resolve(specifier);
370
+ const esmPath = cjsPath.replace(/\.cjs$/, ".js");
371
+ return pathToFileURL(existsSync(esmPath) ? esmPath : cjsPath).href;
372
+ }
366
373
  function buildAutoImportManifest(root) {
367
374
  function scanEntityDir(subdir, filePattern) {
368
375
  const dir = path.join(root, subdir);
@@ -395,15 +402,17 @@ function buildAutoImportManifest(root) {
395
402
  };
396
403
  }
397
404
  function generateVitestConfig(opts) {
398
- const req = createRequire(path.join(opts.packageDir, "package.json"));
399
- const reactPluginUrl = pathToFileURL(req.resolve("@vitejs/plugin-react")).href;
400
- const vitestConfigUrl = pathToFileURL(req.resolve("vitest/config")).href;
405
+ const reactPluginUrl = resolveEsmUrl("@vitejs/plugin-react", opts.packageDir);
406
+ const vitestConfigUrl = resolveEsmUrl("vitest/config", opts.packageDir);
401
407
  const manifest = buildAutoImportManifest(opts.testRoot);
402
408
  const setupFile = path.join(opts.packageDir, "dist/setup.js").replace(/\\/g, "/");
409
+ const reporterFile = path.join(opts.packageDir, "dist/vitest_reporter.js").replace(/\\/g, "/");
410
+ const reporterUrl = pathToFileURL(reporterFile).href;
403
411
  const normalizedPackageDir = opts.packageDir.replace(/\\/g, "/");
404
412
  const normalizedTestRoot = opts.testRoot.replace(/\\/g, "/");
405
413
  const configContent = `import react from '${reactPluginUrl}';
406
414
  import { defineConfig } from '${vitestConfigUrl}';
415
+ ${opts.semanticReporter ? `import { SpectraVitestReporter } from '${reporterUrl}';` : ""}
407
416
 
408
417
  export default defineConfig({
409
418
  root: ${JSON.stringify(normalizedPackageDir)},
@@ -433,7 +442,10 @@ export default defineConfig({
433
442
  test: {
434
443
  globals: true,
435
444
  setupFiles: [${JSON.stringify(setupFile)}],
436
- reporters: ${opts.semanticReporter ? "[]" : "'default'"},
445
+ reporters: ${opts.semanticReporter ? `[
446
+ new SpectraVitestReporter({ testRoot: ${JSON.stringify(normalizedTestRoot)} }),
447
+ ${opts.outputFile ? `['json', { outputFile: ${JSON.stringify(opts.outputFile)} }],` : ""}
448
+ ]` : opts.outputFile ? `['default', ['json', { outputFile: ${JSON.stringify(opts.outputFile)} }]]` : "'default'"},
437
449
  dir: ${JSON.stringify(normalizedTestRoot)},
438
450
  include: ['specs/**/web.test.tsx', 'specs/**/*.test.tsx'],
439
451
  browser: {
@@ -463,6 +475,12 @@ export default defineConfig({
463
475
 
464
476
  // src/cli.ts
465
477
  async function runComponentTests(options = {}) {
478
+ const runner = options.runner ?? "vitest";
479
+ if (runner === "spectra") {
480
+ console.log("TestSpectra native component testing is orchestrated by the Rust Core Engine.");
481
+ console.log("Use `spectra test` to run tests with full parallel CDP orchestration.");
482
+ return true;
483
+ }
466
484
  const rawCwd = options.cwd ?? process.cwd();
467
485
  const cwd = path2.resolve(rawCwd);
468
486
  const packageDir = fileURLToPath(new URL("..", import.meta.url));
@@ -478,12 +496,10 @@ async function runComponentTests(options = {}) {
478
496
  stepDelayMs: options.stepDelayMs ?? 0,
479
497
  semanticReporter,
480
498
  fileParallelism: options.concurrency !== 1,
481
- browserExecutable: options.browserExecutablePath
499
+ browserExecutable: options.browserExecutablePath,
500
+ outputFile: options.outputFile
482
501
  });
483
502
  const args = ["run", "--config", configPath];
484
- if (options.outputFile) {
485
- args.push("--reporter=default", "--reporter=json", `--outputFile.json=${options.outputFile}`);
486
- }
487
503
  if (options.spec) {
488
504
  let specPattern = options.spec.replace(/\\/g, "/");
489
505
  if (path2.isAbsolute(options.spec)) {
@@ -520,8 +536,18 @@ async function runComponentTests(options = {}) {
520
536
  });
521
537
  }
522
538
  function getFlagValue(argv, ...names) {
523
- const index = argv.findIndex((a) => names.includes(a));
524
- return index !== -1 ? argv[index + 1] : void 0;
539
+ for (let i = 0; i < argv.length; i++) {
540
+ const arg = argv[i];
541
+ for (const name of names) {
542
+ if (arg === name) {
543
+ return argv[i + 1];
544
+ }
545
+ if (arg.startsWith(`${name}=`)) {
546
+ return arg.slice(name.length + 1);
547
+ }
548
+ }
549
+ }
550
+ return void 0;
525
551
  }
526
552
  async function runCli(argv = process.argv.slice(2)) {
527
553
  const [command] = argv;
@@ -532,7 +558,9 @@ async function runCli(argv = process.argv.slice(2)) {
532
558
  let headless = true;
533
559
  if (argv.includes("--headed") || argv.includes("--no-headless")) headless = false;
534
560
  else if (argv.includes("--headless")) headless = true;
535
- const rawDelay = getFlagValue(argv, "--step-delay", "--delay");
561
+ const rawRunner = getFlagValue(argv, "--runner", "-r");
562
+ const runner = rawRunner === "vitest" ? "vitest" : "spectra";
563
+ const rawDelay = getFlagValue(argv, "--step-delay", "--delay", "-d");
536
564
  let stepDelayMs;
537
565
  if (rawDelay !== void 0) {
538
566
  const parsed = parseInt(String(rawDelay), 10);
@@ -541,8 +569,11 @@ async function runCli(argv = process.argv.slice(2)) {
541
569
  let spec = getFlagValue(argv, "--spec", "-s");
542
570
  if (!spec) {
543
571
  const flagsWithValues = /* @__PURE__ */ new Set([
572
+ "--runner",
573
+ "-r",
544
574
  "--step-delay",
545
575
  "--delay",
576
+ "-d",
546
577
  "--spec",
547
578
  "-s",
548
579
  "--output",
@@ -569,12 +600,337 @@ async function runCli(argv = process.argv.slice(2)) {
569
600
  if (!isNaN(parsed) && parsed >= 1) concurrency = parsed;
570
601
  }
571
602
  const browserExecutablePath = getFlagValue(argv, "--browser-executable");
572
- const ok = await runComponentTests({ headless, stepDelayMs, spec, outputFile, concurrency, browserExecutablePath });
603
+ const ok = await runComponentTests({
604
+ runner,
605
+ headless,
606
+ stepDelayMs,
607
+ spec,
608
+ outputFile,
609
+ concurrency,
610
+ browserExecutablePath
611
+ });
573
612
  process.exit(ok ? 0 : 1);
574
613
  }
614
+
615
+ // src/cdp_runner/bundle_server.ts
616
+ import fs from "node:fs";
617
+ import http from "node:http";
618
+ import path3 from "node:path";
619
+ import { fileURLToPath as fileURLToPath2 } from "node:url";
620
+ import * as esbuild from "esbuild";
621
+ var bundleCache = /* @__PURE__ */ new Map();
622
+ var inFlightBuilds = /* @__PURE__ */ new Map();
623
+ async function buildBundle(entrypoint, opts = {}) {
624
+ const result = await esbuild.build({
625
+ entryPoints: [entrypoint],
626
+ bundle: true,
627
+ format: "esm",
628
+ platform: "browser",
629
+ target: "esnext",
630
+ external: opts.external ?? [],
631
+ minify: false,
632
+ write: false,
633
+ // Inline source maps so Chrome DevTools can map errors back to .tsx source lines.
634
+ sourcemap: "inline",
635
+ // React 17+ automatic JSX runtime — test files do not need `import React from 'react'`.
636
+ jsx: "automatic",
637
+ jsxImportSource: "react"
638
+ });
639
+ if (result.errors.length > 0) {
640
+ const msgs = result.errors.map((e) => e.text).join("\n");
641
+ throw new Error(`esbuild failed for ${entrypoint}:
642
+ ${msgs}`);
643
+ }
644
+ return result.outputFiles[0].text;
645
+ }
646
+ async function getCachedBundle(cacheKey, entrypoint, opts) {
647
+ const cached = bundleCache.get(cacheKey);
648
+ if (cached) return cached;
649
+ const existingInFlight = inFlightBuilds.get(cacheKey);
650
+ if (existingInFlight) return existingInFlight;
651
+ const buildPromise = buildBundle(entrypoint, opts).then(
652
+ (js) => {
653
+ bundleCache.set(cacheKey, js);
654
+ inFlightBuilds.delete(cacheKey);
655
+ return js;
656
+ },
657
+ (err) => {
658
+ inFlightBuilds.delete(cacheKey);
659
+ throw err;
660
+ }
661
+ );
662
+ inFlightBuilds.set(cacheKey, buildPromise);
663
+ return buildPromise;
664
+ }
665
+ function buildHarnessHtml(manifest, stepDelayMs) {
666
+ const manifestJson = JSON.stringify(manifest);
667
+ return `<!DOCTYPE html>
668
+ <html>
669
+ <head><meta charset="utf-8"><title>Spectra Component Test</title></head>
670
+ <body>
671
+ <script>
672
+ window.__TESTSPECTRA_AUTO_IMPORT_MANIFEST__ = ${manifestJson};
673
+ window.__TESTSPECTRA_STEP_DELAY_MS__ = ${JSON.stringify(stepDelayMs)};
674
+ window.__TESTSPECTRA_SEMANTIC_REPORTER__ = true;
675
+ </script>
676
+ <script type="module" src="/runner.js"></script>
677
+ <script type="module" src="/setup.js"></script>
678
+ </body>
679
+ </html>`;
680
+ }
681
+ function respond(res, status, contentType, body) {
682
+ res.writeHead(status, { "Content-Type": contentType });
683
+ res.end(body);
684
+ }
685
+ function resolveReactPackageDir() {
686
+ let curr = path3.dirname(fileURLToPath2(import.meta.url));
687
+ while (curr !== path3.dirname(curr)) {
688
+ const pkgPath = path3.join(curr, "package.json");
689
+ if (fs.existsSync(pkgPath)) {
690
+ try {
691
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8"));
692
+ if (pkg.name === "@testspectra/react") {
693
+ return curr;
694
+ }
695
+ } catch {
696
+ }
697
+ }
698
+ curr = path3.dirname(curr);
699
+ }
700
+ return path3.resolve(path3.dirname(fileURLToPath2(import.meta.url)), "../..");
701
+ }
702
+ function makeHandler(testRoot, packageDir, harnessHtml, setupEntrypoint, runnerEntrypoint) {
703
+ return async (req, res) => {
704
+ const url = new URL(req.url ?? "/", `http://127.0.0.1`);
705
+ if (url.pathname === "/") {
706
+ respond(res, 200, "text/html; charset=utf-8", harnessHtml);
707
+ return;
708
+ }
709
+ if (url.pathname.startsWith("/@fs/")) {
710
+ const rawFsPath = url.pathname.slice("/@fs/".length);
711
+ const fsPath = rawFsPath.startsWith("/") ? rawFsPath : `/${rawFsPath}`;
712
+ const resolved = path3.resolve(fsPath);
713
+ if (resolved.endsWith(".json")) {
714
+ try {
715
+ const raw = fs.readFileSync(resolved, "utf-8");
716
+ respond(res, 200, "application/json; charset=utf-8", raw);
717
+ } catch (err) {
718
+ respond(res, 500, "application/json", JSON.stringify({ error: String(err) }));
719
+ }
720
+ return;
721
+ }
722
+ try {
723
+ const js = await getCachedBundle(resolved, resolved);
724
+ respond(res, 200, "application/javascript; charset=utf-8", js);
725
+ } catch (err) {
726
+ respond(
727
+ res,
728
+ 200,
729
+ "application/javascript",
730
+ `throw new Error(${JSON.stringify(`Module bundle error for ${fsPath}: ${String(err)}`)});`
731
+ );
732
+ }
733
+ return;
734
+ }
735
+ if (url.pathname === "/setup.js") {
736
+ try {
737
+ const js = await getCachedBundle("setup", setupEntrypoint);
738
+ respond(res, 200, "application/javascript; charset=utf-8", js);
739
+ } catch (err) {
740
+ respond(res, 500, "application/javascript", `// Bundle error: ${String(err)}`);
741
+ }
742
+ return;
743
+ }
744
+ if (url.pathname === "/runner.js") {
745
+ try {
746
+ const js = await getCachedBundle("runner", runnerEntrypoint);
747
+ respond(res, 200, "application/javascript; charset=utf-8", js);
748
+ } catch (err) {
749
+ respond(res, 500, "application/javascript", `// Bundle error: ${String(err)}`);
750
+ }
751
+ return;
752
+ }
753
+ if (url.pathname === "/test") {
754
+ const encodedFile = url.searchParams.get("file");
755
+ if (!encodedFile) {
756
+ respond(res, 400, "text/plain", "Missing ?file= parameter");
757
+ return;
758
+ }
759
+ const testFilePath = decodeURIComponent(encodedFile);
760
+ const resolved = path3.resolve(testFilePath);
761
+ const rootResolved = path3.resolve(testRoot);
762
+ if (!resolved.startsWith(rootResolved + path3.sep) && resolved !== rootResolved) {
763
+ respond(res, 403, "text/plain", "Forbidden: path outside test root");
764
+ return;
765
+ }
766
+ if (resolved.endsWith(".json")) {
767
+ try {
768
+ const raw = fs.readFileSync(resolved, "utf-8");
769
+ respond(res, 200, "application/json; charset=utf-8", raw);
770
+ } catch (err) {
771
+ respond(res, 500, "application/json", JSON.stringify({ error: String(err) }));
772
+ }
773
+ return;
774
+ }
775
+ try {
776
+ const js = await getCachedBundle(resolved, resolved);
777
+ respond(res, 200, "application/javascript; charset=utf-8", js);
778
+ } catch (err) {
779
+ const msg = String(err);
780
+ respond(res, 200, "application/javascript", `throw new Error(${JSON.stringify(`Test bundle error: ${msg}`)});`);
781
+ }
782
+ return;
783
+ }
784
+ respond(res, 404, "text/plain", "Not Found");
785
+ };
786
+ }
787
+ async function startBundleServer(opts = {}) {
788
+ const testRoot = path3.resolve(opts.testRoot ?? process.cwd());
789
+ const packageDir = opts.packageDir ?? resolveReactPackageDir();
790
+ const manifest = opts.manifest ?? { pageObjects: [], steps: [], fixtures: [] };
791
+ const stepDelayMs = opts.stepDelayMs ?? 0;
792
+ const setupEntrypoint = path3.join(packageDir, "src", "setup.ts");
793
+ const runnerEntrypoint = path3.join(packageDir, "src", "cdp_runner", "in_page_runner.ts");
794
+ const distSetup = path3.join(packageDir, "dist", "setup.js");
795
+ if (fs.existsSync(distSetup)) {
796
+ try {
797
+ bundleCache.set("setup", fs.readFileSync(distSetup, "utf-8"));
798
+ } catch {
799
+ }
800
+ }
801
+ const distRunner = path3.join(packageDir, "dist", "runner.js");
802
+ if (fs.existsSync(distRunner)) {
803
+ try {
804
+ bundleCache.set("runner", fs.readFileSync(distRunner, "utf-8"));
805
+ } catch {
806
+ }
807
+ }
808
+ if (!bundleCache.has("setup")) {
809
+ getCachedBundle("setup", setupEntrypoint).catch(() => {
810
+ });
811
+ }
812
+ if (!bundleCache.has("runner")) {
813
+ getCachedBundle("runner", runnerEntrypoint).catch(() => {
814
+ });
815
+ }
816
+ const harnessHtml = buildHarnessHtml(manifest, stepDelayMs);
817
+ const handler = makeHandler(testRoot, packageDir, harnessHtml, setupEntrypoint, runnerEntrypoint);
818
+ const server = http.createServer(handler);
819
+ await new Promise((resolve, reject) => {
820
+ server.listen(0, "127.0.0.1", () => resolve());
821
+ server.once("error", reject);
822
+ });
823
+ const addr = server.address();
824
+ const baseUrl = `http://127.0.0.1:${addr.port}`;
825
+ return {
826
+ url: baseUrl,
827
+ stop: () => server.close()
828
+ };
829
+ }
830
+
831
+ // src/sanitizer.ts
832
+ var VITE_FS_URL_PATTERN = /https?:\/\/(?:localhost|127\.0\.0\.1|\[::1\]):\d+\/@fs\/([A-Za-z]:\/)/g;
833
+ var VITE_FS_UNIX_URL_PATTERN = /https?:\/\/(?:localhost|127\.0\.0\.1|\[::1\]):\d+\/@fs\//g;
834
+ var VITE_ROOT_URL_PATTERN = /https?:\/\/(?:localhost|127\.0\.0\.1|\[::1\]):\d+\//g;
835
+ var URL_QUERY_PATTERN = /(\.[a-zA-Z0-9]+)\?[^:\s'")]*/g;
836
+ var INTERNAL_FRAME_PATTERNS = [
837
+ /node_modules[\\/]vitest[\\/]/i,
838
+ /node_modules[\\/]@vitest[\\/]/i,
839
+ /node_modules[\\/]vite[\\/]/i,
840
+ /node_modules[\\/]@vitejs[\\/]/i,
841
+ /node_modules[\\/]playwright[\\/]/i,
842
+ /node_modules[\\/]@playwright[\\/]/i,
843
+ /node:internal[\\/]/i,
844
+ /dist[\\/]setup\.js/i,
845
+ /dist[\\/]vitest_reporter\.js/i,
846
+ /tools[\\/]react[\\/]/i,
847
+ /[\\/]vitest\.config\./i,
848
+ /@vitest/i,
849
+ /__vitest__/i
850
+ ];
851
+ function sanitizeViteUrl(text) {
852
+ if (!text) return "";
853
+ let result = text.replace(VITE_FS_URL_PATTERN, "$1");
854
+ result = result.replace(VITE_FS_UNIX_URL_PATTERN, "/");
855
+ result = result.replace(VITE_ROOT_URL_PATTERN, "./");
856
+ result = result.replace(URL_QUERY_PATTERN, "$1");
857
+ return result;
858
+ }
859
+ function sanitizeErrorMessage(msg) {
860
+ if (!msg) return "";
861
+ let sanitized = sanitizeViteUrl(msg);
862
+ sanitized = sanitized.replace(/Failed to fetch dynamically imported module:\s*/gi, "Failed to load module: ");
863
+ sanitized = sanitized.replace(/\[vite\]\s*(?:Internal server error:\s*)?/gi, "[TestSpectra] ");
864
+ sanitized = sanitized.replace(/@vitest\/browser/gi, "TestSpectra Browser");
865
+ sanitized = sanitized.replace(/@vitest\/runner/gi, "TestSpectra Runner");
866
+ sanitized = sanitized.replace(/\bvitest\.config\.[a-z0-9]+/gi, "spectra.config.ts");
867
+ sanitized = sanitized.replace(/\bVitest\b/g, "TestSpectra");
868
+ sanitized = sanitized.replace(/\bvitest\b/g, "testspectra");
869
+ return sanitized;
870
+ }
871
+ function sanitizeStackTrace(stack) {
872
+ if (!stack) return "";
873
+ const lines = stack.split("\n");
874
+ const filtered = [];
875
+ for (let i = 0; i < lines.length; i++) {
876
+ const line = lines[i];
877
+ if (i === 0) {
878
+ filtered.push(sanitizeErrorMessage(line));
879
+ continue;
880
+ }
881
+ const isInternal = INTERNAL_FRAME_PATTERNS.some((pattern) => pattern.test(line));
882
+ if (isInternal) {
883
+ continue;
884
+ }
885
+ filtered.push(sanitizeErrorMessage(line));
886
+ }
887
+ return filtered.join("\n").trim();
888
+ }
889
+ function sanitizeError(err) {
890
+ if (!err) return "";
891
+ if (typeof err === "string") {
892
+ return sanitizeStackTrace(err);
893
+ }
894
+ if (typeof err === "object") {
895
+ const errorObj = err;
896
+ const stack = errorObj.stack;
897
+ const message = errorObj.message || String(err);
898
+ if (stack && typeof stack === "string") {
899
+ const sanitizedStack = sanitizeStackTrace(stack);
900
+ if (sanitizedStack) return sanitizedStack;
901
+ }
902
+ return sanitizeErrorMessage(message);
903
+ }
904
+ return sanitizeErrorMessage(String(err));
905
+ }
906
+ function isVitestNoise(line) {
907
+ const trimmed = line.trim();
908
+ if (!trimmed) return true;
909
+ if (/^RUN\s+v\d+/i.test(trimmed)) return true;
910
+ if (/^DEV\s+v\d+/i.test(trimmed)) return true;
911
+ if (/^Test Files\s+\d+/i.test(trimmed)) return true;
912
+ if (/^Tests\s+\d+/i.test(trimmed)) return true;
913
+ if (/^Start at\s+\d+/i.test(trimmed)) return true;
914
+ if (/^Duration\s+[\d.]+/i.test(trimmed)) return true;
915
+ if (/^⎯{3,}/.test(trimmed)) return true;
916
+ if (/^Re-optimizing dependencies/i.test(trimmed)) return true;
917
+ if (/^JSON report written to/i.test(trimmed)) return true;
918
+ if (/^\(?transform \d+ms/i.test(trimmed)) return true;
919
+ if (/^[·✓❯⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏]\s+/.test(trimmed)) return true;
920
+ if (/vite-cjs-node-api-deprecated/i.test(trimmed)) return true;
921
+ if (/CJS build of Vite's Node API is deprecated/i.test(trimmed)) return true;
922
+ if (/^\[?vite\]?\s*(deprecation|warning)/i.test(trimmed)) return true;
923
+ return false;
924
+ }
575
925
  export {
576
926
  DomDriverBridge,
927
+ isVitestNoise,
577
928
  mount,
578
929
  runCli,
579
- runComponentTests
930
+ runComponentTests,
931
+ sanitizeError,
932
+ sanitizeErrorMessage,
933
+ sanitizeStackTrace,
934
+ sanitizeViteUrl,
935
+ startBundleServer
580
936
  };
package/dist/runner.js ADDED
@@ -0,0 +1 @@
1
+ var T=[],a=[],d=[];globalThis.it=function(n,e){T.push({title:n,fn:e})};globalThis.beforeEach=function(n){a.push(n)};globalThis.afterEach=function(n){d.push(n)};function y(n){let e=t=>({toBe(o){let r=Object.is(n,o);if(t?r:!r)throw new Error(`Expected ${JSON.stringify(n)} ${t?"not to be":"to be"} ${JSON.stringify(o)}`)},toEqual(o){let r=JSON.stringify(n)===JSON.stringify(o);if(t?r:!r)throw new Error(`Expected ${JSON.stringify(n)} ${t?"not to equal":"to equal"} ${JSON.stringify(o)}`)},toBeTruthy(){let o=!!n;if(t?o:!o)throw new Error(`Expected ${JSON.stringify(n)} ${t?"not to be truthy":"to be truthy"}`)},toBeFalsy(){let o=!n;if(t?o:!o)throw new Error(`Expected ${JSON.stringify(n)} ${t?"not to be falsy":"to be falsy"}`)},toBeNull(){let o=n===null;if(t?o:!o)throw new Error(`Expected ${JSON.stringify(n)} ${t?"not to be null":"to be null"}`)},toBeUndefined(){let o=n===void 0;if(t?o:!o)throw new Error(`Expected ${JSON.stringify(n)} ${t?"not to be undefined":"to be undefined"}`)},toBeDefined(){let o=n!==void 0;if(t?o:!o)throw new Error(`Expected ${JSON.stringify(n)} ${t?"not to be defined":"to be defined"}`)},toContain(o){let r=Array.isArray(n)?n.includes(o):typeof n=="string"?n.includes(String(o)):!1;if(t?r:!r)throw new Error(`Expected ${JSON.stringify(n)} ${t?"not to contain":"to contain"} ${JSON.stringify(o)}`)}});return{...e(!1),not:e(!0)}}globalThis.expect=y;function w(){let n=globalThis.Spectra;if(typeof n?.unmount=="function")n.unmount();else{let e=document.getElementById("spectra-root");e&&(e.innerHTML="")}}function s(n,e){console.log(`[${n}] ${e}`)}globalThis.__SPECTRA_RUNNER__={async run(n,e){s("TESTSPECTRA_SUITE_START",JSON.stringify({file:n}));let t=globalThis.__bootstrapAutoImports;if(typeof t=="function")try{await t(e)}catch(i){s("TESTSPECTRA_SUITE_ERROR",JSON.stringify({file:n,error:`Failed to bootstrap auto imports: ${String(i)}`}));return}let o=encodeURIComponent(n);try{await import(`/test?file=${o}`)}catch(i){s("TESTSPECTRA_SUITE_ERROR",JSON.stringify({file:n,error:String(i)}));return}let r=0,E=0;for(let{title:i,fn:u}of T){s("TESTSPECTRA_TEST_START",i),globalThis.__CURRENT_TEST_TITLE__=i;let S={task:{name:i}},g=performance.now();try{for(let c of a)await c(S);await u();for(let c of d)await c(S);let f=Math.round(performance.now()-g);s("TESTSPECTRA_TEST_PASS",`${i} (${f}ms)`),r++}catch(f){let c=Math.round(performance.now()-g),p=f instanceof Error?f.message:String(f);s("TESTSPECTRA_TEST_ERROR",p),s("TESTSPECTRA_TEST_FAIL",`${i} (${c}ms)`),E++}finally{w()}}T.length=0,a.length=0,d.length=0,s("TESTSPECTRA_SUITE_END",JSON.stringify({file:n,passed:r,failed:E}))}};