@testspectra/react 1.1.8-rc.22 → 1.1.8-rc.23
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 +131 -15
- package/dist/index.js +138 -17
- package/dist/setup.js +1415 -0
- package/package.json +4 -5
- package/vitest.config.ts +0 -117
package/dist/cli.js
CHANGED
|
@@ -1,37 +1,149 @@
|
|
|
1
1
|
// src/cli.ts
|
|
2
|
+
import { existsSync as existsSync2 } from "node:fs";
|
|
3
|
+
import path2 from "node:path";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
2
5
|
import spawn from "cross-spawn";
|
|
6
|
+
|
|
7
|
+
// src/config_generator.ts
|
|
8
|
+
import { existsSync, mkdtempSync, readdirSync, rmSync, writeFileSync } from "node:fs";
|
|
9
|
+
import { createRequire } from "node:module";
|
|
10
|
+
import os from "node:os";
|
|
3
11
|
import path from "node:path";
|
|
4
|
-
import {
|
|
12
|
+
import { pathToFileURL } from "node:url";
|
|
13
|
+
function buildAutoImportManifest(root) {
|
|
14
|
+
function scanEntityDir(subdir, filePattern) {
|
|
15
|
+
const dir = path.join(root, subdir);
|
|
16
|
+
if (!existsSync(dir)) return [];
|
|
17
|
+
const entries = [];
|
|
18
|
+
for (const name of readdirSync(dir, { withFileTypes: true })) {
|
|
19
|
+
if (!name.isDirectory()) continue;
|
|
20
|
+
for (const platform of ["web", "common"]) {
|
|
21
|
+
for (const ext of ["tsx", "ts"]) {
|
|
22
|
+
const candidate = path.join(dir, name.name, filePattern(platform).replace(/\.tsx?$/, `.${ext}`));
|
|
23
|
+
if (existsSync(candidate)) {
|
|
24
|
+
entries.push({ name: name.name, path: candidate.replace(/\\/g, "/") });
|
|
25
|
+
break;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
if (entries.at(-1)?.name === name.name) break;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
return entries;
|
|
32
|
+
}
|
|
33
|
+
function scanFixtures() {
|
|
34
|
+
const dir = path.join(root, "fixtures");
|
|
35
|
+
if (!existsSync(dir)) return [];
|
|
36
|
+
return readdirSync(dir).filter((f) => f.endsWith(".json")).map((f) => ({ name: f.replace(/\.json$/, ""), path: path.join(dir, f).replace(/\\/g, "/") }));
|
|
37
|
+
}
|
|
38
|
+
return {
|
|
39
|
+
pageObjects: scanEntityDir("page-objects", (p) => `${p}.tsx`),
|
|
40
|
+
steps: scanEntityDir("support/steps", (p) => `${p}.step.tsx`),
|
|
41
|
+
fixtures: scanFixtures()
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
function generateVitestConfig(opts) {
|
|
45
|
+
const req = createRequire(path.join(opts.packageDir, "package.json"));
|
|
46
|
+
const reactPluginUrl = pathToFileURL(req.resolve("@vitejs/plugin-react")).href;
|
|
47
|
+
const vitestConfigUrl = pathToFileURL(req.resolve("vitest/config")).href;
|
|
48
|
+
const manifest = buildAutoImportManifest(opts.testRoot);
|
|
49
|
+
const setupFile = path.join(opts.packageDir, "dist/setup.js").replace(/\\/g, "/");
|
|
50
|
+
const normalizedPackageDir = opts.packageDir.replace(/\\/g, "/");
|
|
51
|
+
const normalizedTestRoot = opts.testRoot.replace(/\\/g, "/");
|
|
52
|
+
const configContent = `import react from '${reactPluginUrl}';
|
|
53
|
+
import { defineConfig } from '${vitestConfigUrl}';
|
|
54
|
+
|
|
55
|
+
export default defineConfig({
|
|
56
|
+
root: ${JSON.stringify(normalizedPackageDir)},
|
|
57
|
+
plugins: [react()],
|
|
58
|
+
define: {
|
|
59
|
+
__TESTSPECTRA_AUTO_IMPORT_MANIFEST__: ${JSON.stringify(JSON.stringify(manifest))},
|
|
60
|
+
__TESTSPECTRA_STEP_DELAY_MS__: ${JSON.stringify(opts.stepDelayMs)},
|
|
61
|
+
__TESTSPECTRA_SEMANTIC_REPORTER__: ${JSON.stringify(opts.semanticReporter)},
|
|
62
|
+
},
|
|
63
|
+
optimizeDeps: {
|
|
64
|
+
include: [
|
|
65
|
+
'react',
|
|
66
|
+
'react-dom',
|
|
67
|
+
'react-dom/client',
|
|
68
|
+
'@testing-library/react',
|
|
69
|
+
'@testing-library/user-event',
|
|
70
|
+
],
|
|
71
|
+
},
|
|
72
|
+
server: {
|
|
73
|
+
fs: {
|
|
74
|
+
allow: [
|
|
75
|
+
${JSON.stringify(normalizedPackageDir)},
|
|
76
|
+
${JSON.stringify(normalizedTestRoot)},
|
|
77
|
+
],
|
|
78
|
+
},
|
|
79
|
+
},
|
|
80
|
+
test: {
|
|
81
|
+
globals: true,
|
|
82
|
+
setupFiles: [${JSON.stringify(setupFile)}],
|
|
83
|
+
reporters: ${opts.semanticReporter ? "[]" : "'default'"},
|
|
84
|
+
dir: ${JSON.stringify(normalizedTestRoot)},
|
|
85
|
+
include: ['specs/**/web.test.tsx', 'specs/**/*.test.tsx'],
|
|
86
|
+
browser: {
|
|
87
|
+
enabled: true,
|
|
88
|
+
provider: 'playwright',
|
|
89
|
+
name: 'chromium',
|
|
90
|
+
headless: ${opts.headless},
|
|
91
|
+
fileParallelism: ${opts.fileParallelism},
|
|
92
|
+
${opts.browserExecutable ? `providerOptions: { launch: { executablePath: ${JSON.stringify(opts.browserExecutable)} } },` : ""}
|
|
93
|
+
},
|
|
94
|
+
},
|
|
95
|
+
});
|
|
96
|
+
`;
|
|
97
|
+
const tmpDir = mkdtempSync(path.join(os.tmpdir(), "testspectra-react-"));
|
|
98
|
+
const configPath = path.join(tmpDir, "vitest.config.mjs");
|
|
99
|
+
writeFileSync(configPath, configContent, "utf-8");
|
|
100
|
+
return {
|
|
101
|
+
configPath,
|
|
102
|
+
cleanup: () => {
|
|
103
|
+
try {
|
|
104
|
+
rmSync(tmpDir, { recursive: true, force: true });
|
|
105
|
+
} catch {
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// src/cli.ts
|
|
5
112
|
async function runComponentTests(options = {}) {
|
|
6
113
|
const rawCwd = options.cwd ?? process.cwd();
|
|
7
|
-
const cwd =
|
|
8
|
-
const
|
|
114
|
+
const cwd = path2.resolve(rawCwd);
|
|
115
|
+
const packageDir = fileURLToPath(new URL("..", import.meta.url));
|
|
9
116
|
const ext = process.platform === "win32" ? ".cmd" : "";
|
|
10
|
-
const
|
|
117
|
+
const localVitestBin = fileURLToPath(new URL(`../node_modules/.bin/vitest${ext}`, import.meta.url));
|
|
118
|
+
const consumerVitestBin = path2.join(cwd, `node_modules/.bin/vitest${ext}`);
|
|
119
|
+
const vitestBin = existsSync2(localVitestBin) ? localVitestBin : consumerVitestBin;
|
|
120
|
+
const semanticReporter = Boolean(options.onLine);
|
|
121
|
+
const { configPath, cleanup } = generateVitestConfig({
|
|
122
|
+
packageDir,
|
|
123
|
+
testRoot: cwd,
|
|
124
|
+
headless: options.headless !== false,
|
|
125
|
+
stepDelayMs: options.stepDelayMs ?? 0,
|
|
126
|
+
semanticReporter,
|
|
127
|
+
fileParallelism: options.concurrency !== 1,
|
|
128
|
+
browserExecutable: options.browserExecutablePath
|
|
129
|
+
});
|
|
11
130
|
const args = ["run", "--config", configPath];
|
|
12
131
|
if (options.outputFile) {
|
|
13
132
|
args.push("--reporter=default", "--reporter=json", `--outputFile.json=${options.outputFile}`);
|
|
14
133
|
}
|
|
15
134
|
if (options.spec) {
|
|
16
135
|
let specPattern = options.spec.replace(/\\/g, "/");
|
|
17
|
-
if (
|
|
18
|
-
specPattern =
|
|
136
|
+
if (path2.isAbsolute(options.spec)) {
|
|
137
|
+
specPattern = path2.relative(cwd, options.spec).replace(/\\/g, "/");
|
|
19
138
|
}
|
|
20
139
|
args.push(specPattern);
|
|
21
140
|
}
|
|
22
|
-
const semanticReporter = Boolean(options.onLine);
|
|
23
141
|
return new Promise((resolve) => {
|
|
24
142
|
const child = spawn(vitestBin, args, {
|
|
25
143
|
stdio: semanticReporter ? ["inherit", "pipe", "pipe"] : "inherit",
|
|
26
144
|
cwd,
|
|
27
145
|
env: {
|
|
28
|
-
...process.env
|
|
29
|
-
TESTSPECTRA_TEST_ROOT: cwd.replace(/\\/g, "/"),
|
|
30
|
-
TESTSPECTRA_HEADLESS: options.headless === false ? "false" : "true",
|
|
31
|
-
TESTSPECTRA_STEP_DELAY: String(options.stepDelayMs ?? 0),
|
|
32
|
-
TESTSPECTRA_FILE_PARALLELISM: options.concurrency === 1 ? "false" : "true",
|
|
33
|
-
...options.browserExecutablePath ? { TESTSPECTRA_BROWSER_EXECUTABLE: options.browserExecutablePath } : {},
|
|
34
|
-
TESTSPECTRA_SEMANTIC_REPORTER: semanticReporter ? "1" : "0"
|
|
146
|
+
...process.env
|
|
35
147
|
}
|
|
36
148
|
});
|
|
37
149
|
if (semanticReporter) {
|
|
@@ -43,8 +155,12 @@ async function runComponentTests(options = {}) {
|
|
|
43
155
|
child.stdout?.on("data", handleData);
|
|
44
156
|
child.stderr?.on("data", handleData);
|
|
45
157
|
}
|
|
46
|
-
child.on("exit", (code) =>
|
|
158
|
+
child.on("exit", (code) => {
|
|
159
|
+
cleanup();
|
|
160
|
+
resolve(code === 0);
|
|
161
|
+
});
|
|
47
162
|
child.on("error", (err) => {
|
|
163
|
+
cleanup();
|
|
48
164
|
console.error(err);
|
|
49
165
|
resolve(false);
|
|
50
166
|
});
|
package/dist/index.js
CHANGED
|
@@ -317,9 +317,13 @@ var DomDriverBridge = class {
|
|
|
317
317
|
};
|
|
318
318
|
|
|
319
319
|
// src/mount.ts
|
|
320
|
-
import
|
|
320
|
+
import * as ReactDOMClient from "react-dom/client";
|
|
321
321
|
var ROOT_ID = "spectra-root";
|
|
322
322
|
var cachedRoot = null;
|
|
323
|
+
function getCreateRoot() {
|
|
324
|
+
const client = ReactDOMClient;
|
|
325
|
+
return client.createRoot || client.default?.createRoot || client.default;
|
|
326
|
+
}
|
|
323
327
|
function ensureContainer() {
|
|
324
328
|
let container = document.getElementById(ROOT_ID);
|
|
325
329
|
if (!container) {
|
|
@@ -332,7 +336,8 @@ function ensureContainer() {
|
|
|
332
336
|
async function mount(element) {
|
|
333
337
|
const container = ensureContainer();
|
|
334
338
|
if (!cachedRoot) {
|
|
335
|
-
|
|
339
|
+
const createRootFn = getCreateRoot();
|
|
340
|
+
cachedRoot = createRootFn(container);
|
|
336
341
|
}
|
|
337
342
|
const componentName = (typeof element.type === "string" ? element.type : element.type?.displayName || element.type?.name) || "component";
|
|
338
343
|
return globalThis.__TS_RUNTIME__.trackStep(
|
|
@@ -347,39 +352,151 @@ async function mount(element) {
|
|
|
347
352
|
}
|
|
348
353
|
|
|
349
354
|
// src/cli.ts
|
|
355
|
+
import { existsSync as existsSync2 } from "node:fs";
|
|
356
|
+
import path2 from "node:path";
|
|
357
|
+
import { fileURLToPath } from "node:url";
|
|
350
358
|
import spawn from "cross-spawn";
|
|
359
|
+
|
|
360
|
+
// src/config_generator.ts
|
|
361
|
+
import { existsSync, mkdtempSync, readdirSync, rmSync, writeFileSync } from "node:fs";
|
|
362
|
+
import { createRequire } from "node:module";
|
|
363
|
+
import os from "node:os";
|
|
351
364
|
import path from "node:path";
|
|
352
|
-
import {
|
|
365
|
+
import { pathToFileURL } from "node:url";
|
|
366
|
+
function buildAutoImportManifest(root) {
|
|
367
|
+
function scanEntityDir(subdir, filePattern) {
|
|
368
|
+
const dir = path.join(root, subdir);
|
|
369
|
+
if (!existsSync(dir)) return [];
|
|
370
|
+
const entries = [];
|
|
371
|
+
for (const name of readdirSync(dir, { withFileTypes: true })) {
|
|
372
|
+
if (!name.isDirectory()) continue;
|
|
373
|
+
for (const platform of ["web", "common"]) {
|
|
374
|
+
for (const ext of ["tsx", "ts"]) {
|
|
375
|
+
const candidate = path.join(dir, name.name, filePattern(platform).replace(/\.tsx?$/, `.${ext}`));
|
|
376
|
+
if (existsSync(candidate)) {
|
|
377
|
+
entries.push({ name: name.name, path: candidate.replace(/\\/g, "/") });
|
|
378
|
+
break;
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
if (entries.at(-1)?.name === name.name) break;
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
return entries;
|
|
385
|
+
}
|
|
386
|
+
function scanFixtures() {
|
|
387
|
+
const dir = path.join(root, "fixtures");
|
|
388
|
+
if (!existsSync(dir)) return [];
|
|
389
|
+
return readdirSync(dir).filter((f) => f.endsWith(".json")).map((f) => ({ name: f.replace(/\.json$/, ""), path: path.join(dir, f).replace(/\\/g, "/") }));
|
|
390
|
+
}
|
|
391
|
+
return {
|
|
392
|
+
pageObjects: scanEntityDir("page-objects", (p) => `${p}.tsx`),
|
|
393
|
+
steps: scanEntityDir("support/steps", (p) => `${p}.step.tsx`),
|
|
394
|
+
fixtures: scanFixtures()
|
|
395
|
+
};
|
|
396
|
+
}
|
|
397
|
+
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;
|
|
401
|
+
const manifest = buildAutoImportManifest(opts.testRoot);
|
|
402
|
+
const setupFile = path.join(opts.packageDir, "dist/setup.js").replace(/\\/g, "/");
|
|
403
|
+
const normalizedPackageDir = opts.packageDir.replace(/\\/g, "/");
|
|
404
|
+
const normalizedTestRoot = opts.testRoot.replace(/\\/g, "/");
|
|
405
|
+
const configContent = `import react from '${reactPluginUrl}';
|
|
406
|
+
import { defineConfig } from '${vitestConfigUrl}';
|
|
407
|
+
|
|
408
|
+
export default defineConfig({
|
|
409
|
+
root: ${JSON.stringify(normalizedPackageDir)},
|
|
410
|
+
plugins: [react()],
|
|
411
|
+
define: {
|
|
412
|
+
__TESTSPECTRA_AUTO_IMPORT_MANIFEST__: ${JSON.stringify(JSON.stringify(manifest))},
|
|
413
|
+
__TESTSPECTRA_STEP_DELAY_MS__: ${JSON.stringify(opts.stepDelayMs)},
|
|
414
|
+
__TESTSPECTRA_SEMANTIC_REPORTER__: ${JSON.stringify(opts.semanticReporter)},
|
|
415
|
+
},
|
|
416
|
+
optimizeDeps: {
|
|
417
|
+
include: [
|
|
418
|
+
'react',
|
|
419
|
+
'react-dom',
|
|
420
|
+
'react-dom/client',
|
|
421
|
+
'@testing-library/react',
|
|
422
|
+
'@testing-library/user-event',
|
|
423
|
+
],
|
|
424
|
+
},
|
|
425
|
+
server: {
|
|
426
|
+
fs: {
|
|
427
|
+
allow: [
|
|
428
|
+
${JSON.stringify(normalizedPackageDir)},
|
|
429
|
+
${JSON.stringify(normalizedTestRoot)},
|
|
430
|
+
],
|
|
431
|
+
},
|
|
432
|
+
},
|
|
433
|
+
test: {
|
|
434
|
+
globals: true,
|
|
435
|
+
setupFiles: [${JSON.stringify(setupFile)}],
|
|
436
|
+
reporters: ${opts.semanticReporter ? "[]" : "'default'"},
|
|
437
|
+
dir: ${JSON.stringify(normalizedTestRoot)},
|
|
438
|
+
include: ['specs/**/web.test.tsx', 'specs/**/*.test.tsx'],
|
|
439
|
+
browser: {
|
|
440
|
+
enabled: true,
|
|
441
|
+
provider: 'playwright',
|
|
442
|
+
name: 'chromium',
|
|
443
|
+
headless: ${opts.headless},
|
|
444
|
+
fileParallelism: ${opts.fileParallelism},
|
|
445
|
+
${opts.browserExecutable ? `providerOptions: { launch: { executablePath: ${JSON.stringify(opts.browserExecutable)} } },` : ""}
|
|
446
|
+
},
|
|
447
|
+
},
|
|
448
|
+
});
|
|
449
|
+
`;
|
|
450
|
+
const tmpDir = mkdtempSync(path.join(os.tmpdir(), "testspectra-react-"));
|
|
451
|
+
const configPath = path.join(tmpDir, "vitest.config.mjs");
|
|
452
|
+
writeFileSync(configPath, configContent, "utf-8");
|
|
453
|
+
return {
|
|
454
|
+
configPath,
|
|
455
|
+
cleanup: () => {
|
|
456
|
+
try {
|
|
457
|
+
rmSync(tmpDir, { recursive: true, force: true });
|
|
458
|
+
} catch {
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
};
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
// src/cli.ts
|
|
353
465
|
async function runComponentTests(options = {}) {
|
|
354
466
|
const rawCwd = options.cwd ?? process.cwd();
|
|
355
|
-
const cwd =
|
|
356
|
-
const
|
|
467
|
+
const cwd = path2.resolve(rawCwd);
|
|
468
|
+
const packageDir = fileURLToPath(new URL("..", import.meta.url));
|
|
357
469
|
const ext = process.platform === "win32" ? ".cmd" : "";
|
|
358
|
-
const
|
|
470
|
+
const localVitestBin = fileURLToPath(new URL(`../node_modules/.bin/vitest${ext}`, import.meta.url));
|
|
471
|
+
const consumerVitestBin = path2.join(cwd, `node_modules/.bin/vitest${ext}`);
|
|
472
|
+
const vitestBin = existsSync2(localVitestBin) ? localVitestBin : consumerVitestBin;
|
|
473
|
+
const semanticReporter = Boolean(options.onLine);
|
|
474
|
+
const { configPath, cleanup } = generateVitestConfig({
|
|
475
|
+
packageDir,
|
|
476
|
+
testRoot: cwd,
|
|
477
|
+
headless: options.headless !== false,
|
|
478
|
+
stepDelayMs: options.stepDelayMs ?? 0,
|
|
479
|
+
semanticReporter,
|
|
480
|
+
fileParallelism: options.concurrency !== 1,
|
|
481
|
+
browserExecutable: options.browserExecutablePath
|
|
482
|
+
});
|
|
359
483
|
const args = ["run", "--config", configPath];
|
|
360
484
|
if (options.outputFile) {
|
|
361
485
|
args.push("--reporter=default", "--reporter=json", `--outputFile.json=${options.outputFile}`);
|
|
362
486
|
}
|
|
363
487
|
if (options.spec) {
|
|
364
488
|
let specPattern = options.spec.replace(/\\/g, "/");
|
|
365
|
-
if (
|
|
366
|
-
specPattern =
|
|
489
|
+
if (path2.isAbsolute(options.spec)) {
|
|
490
|
+
specPattern = path2.relative(cwd, options.spec).replace(/\\/g, "/");
|
|
367
491
|
}
|
|
368
492
|
args.push(specPattern);
|
|
369
493
|
}
|
|
370
|
-
const semanticReporter = Boolean(options.onLine);
|
|
371
494
|
return new Promise((resolve) => {
|
|
372
495
|
const child = spawn(vitestBin, args, {
|
|
373
496
|
stdio: semanticReporter ? ["inherit", "pipe", "pipe"] : "inherit",
|
|
374
497
|
cwd,
|
|
375
498
|
env: {
|
|
376
|
-
...process.env
|
|
377
|
-
TESTSPECTRA_TEST_ROOT: cwd.replace(/\\/g, "/"),
|
|
378
|
-
TESTSPECTRA_HEADLESS: options.headless === false ? "false" : "true",
|
|
379
|
-
TESTSPECTRA_STEP_DELAY: String(options.stepDelayMs ?? 0),
|
|
380
|
-
TESTSPECTRA_FILE_PARALLELISM: options.concurrency === 1 ? "false" : "true",
|
|
381
|
-
...options.browserExecutablePath ? { TESTSPECTRA_BROWSER_EXECUTABLE: options.browserExecutablePath } : {},
|
|
382
|
-
TESTSPECTRA_SEMANTIC_REPORTER: semanticReporter ? "1" : "0"
|
|
499
|
+
...process.env
|
|
383
500
|
}
|
|
384
501
|
});
|
|
385
502
|
if (semanticReporter) {
|
|
@@ -391,8 +508,12 @@ async function runComponentTests(options = {}) {
|
|
|
391
508
|
child.stdout?.on("data", handleData);
|
|
392
509
|
child.stderr?.on("data", handleData);
|
|
393
510
|
}
|
|
394
|
-
child.on("exit", (code) =>
|
|
511
|
+
child.on("exit", (code) => {
|
|
512
|
+
cleanup();
|
|
513
|
+
resolve(code === 0);
|
|
514
|
+
});
|
|
395
515
|
child.on("error", (err) => {
|
|
516
|
+
cleanup();
|
|
396
517
|
console.error(err);
|
|
397
518
|
resolve(false);
|
|
398
519
|
});
|