@testspectra/cli 1.0.20 → 1.0.22
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/commands/init.js +80 -61
- package/dist/types/generator.js +2 -2
- package/package.json +2 -4
package/dist/commands/init.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import fs from "fs";
|
|
2
2
|
import path from "path";
|
|
3
3
|
import { fileURLToPath } from "url";
|
|
4
|
+
import * as p from "@clack/prompts";
|
|
5
|
+
import chalk from "chalk";
|
|
4
6
|
import { ConfigLoader } from "../config/loader.js";
|
|
5
7
|
import { TypeGenerator } from "../types/generator.js";
|
|
6
|
-
import inquirer from "inquirer";
|
|
7
8
|
function parsePnpmWorkspaceGlobs(content) {
|
|
8
9
|
const lines = content.split("\n");
|
|
9
10
|
const globs = [];
|
|
@@ -112,9 +113,10 @@ export async function initCommand(options = {}) {
|
|
|
112
113
|
const cwd = process.cwd();
|
|
113
114
|
const existingConfig = ConfigLoader.findConfigFile(cwd);
|
|
114
115
|
if (existingConfig && existingConfig === path.join(cwd, path.basename(existingConfig)) && !options.force) {
|
|
115
|
-
|
|
116
|
+
p.log.warn(chalk.yellow(`Config already exists at ${path.basename(existingConfig)}. Use --force to overwrite.`));
|
|
116
117
|
return;
|
|
117
118
|
}
|
|
119
|
+
p.intro(chalk.bold.cyan("✨ TestSpectra Enterprise Scaffolder"));
|
|
118
120
|
// Detect monorepo environment
|
|
119
121
|
let isMonorepo = false;
|
|
120
122
|
let pnpmPatterns = [];
|
|
@@ -134,20 +136,27 @@ export async function initCommand(options = {}) {
|
|
|
134
136
|
const detectedProjects = isMonorepo ? scanDirectoriesForProjects(cwd, pnpmPatterns) : [];
|
|
135
137
|
let selectedTemplate = options.template;
|
|
136
138
|
if (!selectedTemplate && !options.force) {
|
|
137
|
-
const
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
139
|
+
const templateChoice = await p.select({
|
|
140
|
+
message: "Select TestSpectra setup mode:",
|
|
141
|
+
initialValue: isMonorepo ? "nx" : "default",
|
|
142
|
+
options: [
|
|
143
|
+
{
|
|
144
|
+
label: "Nx Monorepo (Recommended)",
|
|
145
|
+
value: "nx",
|
|
146
|
+
hint: "Interactive workspace feature discovery & centralized config",
|
|
147
|
+
},
|
|
148
|
+
{
|
|
149
|
+
label: "Standard Project",
|
|
150
|
+
value: "default",
|
|
151
|
+
hint: "Standalone single-suite E2E testing project",
|
|
152
|
+
},
|
|
153
|
+
],
|
|
154
|
+
});
|
|
155
|
+
if (p.isCancel(templateChoice)) {
|
|
156
|
+
p.cancel("Setup cancelled.");
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
selectedTemplate = templateChoice;
|
|
151
160
|
}
|
|
152
161
|
const templateName = selectedTemplate || (isMonorepo ? "nx" : "default");
|
|
153
162
|
// 1. Resolve template directory
|
|
@@ -161,10 +170,11 @@ export async function initCommand(options = {}) {
|
|
|
161
170
|
templateDir = path.resolve(__dirname, `../../../templates/${templateName}`);
|
|
162
171
|
}
|
|
163
172
|
if (!fs.existsSync(templateDir)) {
|
|
164
|
-
|
|
173
|
+
p.cancel(chalk.red(`Template directory not found: ${templateDir}`));
|
|
174
|
+
return;
|
|
165
175
|
}
|
|
166
176
|
// 2. Resolve CLI dependency version
|
|
167
|
-
let cliVersion = "^1.0.
|
|
177
|
+
let cliVersion = "^1.0.21";
|
|
168
178
|
try {
|
|
169
179
|
const cliPackageJsonPath = path.resolve(__dirname, "../../package.json");
|
|
170
180
|
if (fs.existsSync(cliPackageJsonPath)) {
|
|
@@ -234,47 +244,53 @@ export async function initCommand(options = {}) {
|
|
|
234
244
|
}
|
|
235
245
|
// --- MONOREPO INTERACTIVE FLOW ---
|
|
236
246
|
if (templateName === "nx" && (detectedProjects.length > 0 || isMonorepo)) {
|
|
237
|
-
console.log(`\n\x1b[36m[TestSpectra Monorepo Discovery]\x1b[0m`);
|
|
238
|
-
console.log(`Found ${detectedProjects.length} workspace feature project(s).`);
|
|
239
247
|
let chosenProjects = [];
|
|
240
248
|
if (detectedProjects.length > 0) {
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
249
|
+
p.log.step(chalk.cyan(`Monorepo Auto-Discovery: Found ${detectedProjects.length} workspace feature package(s)`));
|
|
250
|
+
const projectChoices = await p.multiselect({
|
|
251
|
+
message: "Select workspace features to initialize with TestSpectra:",
|
|
252
|
+
options: detectedProjects.map((proj) => ({
|
|
253
|
+
value: proj.relPath,
|
|
254
|
+
label: `${proj.relPath} (${proj.name})`,
|
|
255
|
+
hint: proj.hasNxProject ? "Nx project.json" : "package.json",
|
|
256
|
+
})),
|
|
257
|
+
initialValues: detectedProjects.map((p) => p.relPath),
|
|
258
|
+
required: true,
|
|
259
|
+
});
|
|
260
|
+
if (p.isCancel(projectChoices)) {
|
|
261
|
+
p.cancel("Setup cancelled.");
|
|
262
|
+
return;
|
|
263
|
+
}
|
|
264
|
+
chosenProjects = projectChoices;
|
|
265
|
+
}
|
|
266
|
+
const e2eFolderNameInput = await p.text({
|
|
267
|
+
message: "Enter E2E test folder name for each selected feature:",
|
|
268
|
+
placeholder: "e2e",
|
|
269
|
+
defaultValue: "e2e",
|
|
270
|
+
});
|
|
271
|
+
if (p.isCancel(e2eFolderNameInput)) {
|
|
272
|
+
p.cancel("Setup cancelled.");
|
|
273
|
+
return;
|
|
274
|
+
}
|
|
275
|
+
const sharedTestingPathInput = await p.text({
|
|
276
|
+
message: "Enter path for Shared Testing library (POMs, Steps, Actions, Fixtures):",
|
|
277
|
+
placeholder: "shared/testing",
|
|
278
|
+
defaultValue: "shared/testing",
|
|
279
|
+
});
|
|
280
|
+
if (p.isCancel(sharedTestingPathInput)) {
|
|
281
|
+
p.cancel("Setup cancelled.");
|
|
282
|
+
return;
|
|
254
283
|
}
|
|
255
|
-
const
|
|
256
|
-
|
|
257
|
-
type: "input",
|
|
258
|
-
name: "e2eFolderName",
|
|
259
|
-
message: "Enter E2E test folder name for each selected feature:",
|
|
260
|
-
default: "e2e",
|
|
261
|
-
},
|
|
262
|
-
{
|
|
263
|
-
type: "input",
|
|
264
|
-
name: "sharedTestingPath",
|
|
265
|
-
message: "Enter path for Shared Testing library (POMs, Steps, Actions, Fixtures):",
|
|
266
|
-
default: "shared/testing",
|
|
267
|
-
},
|
|
268
|
-
]);
|
|
269
|
-
const e2eFolderName = folderAnswers.e2eFolderName.trim() || "e2e";
|
|
270
|
-
const sharedTestingRelPath = folderAnswers.sharedTestingPath.trim() || "shared/testing";
|
|
284
|
+
const e2eFolderName = e2eFolderNameInput.trim() || "e2e";
|
|
285
|
+
const sharedTestingRelPath = sharedTestingPathInput.trim() || "shared/testing";
|
|
271
286
|
const sharedTestingAbsPath = path.join(cwd, sharedTestingRelPath);
|
|
287
|
+
const s = p.spinner();
|
|
288
|
+
s.start("Scaffolding distributed monorepo structure and ambient types...");
|
|
272
289
|
// 1. Centralized Root Config
|
|
273
290
|
const rootConfigSrc = path.join(templateDir, "spectra.config.ts");
|
|
274
291
|
const rootConfigDest = path.join(cwd, "spectra.config.ts");
|
|
275
292
|
if (fs.existsSync(rootConfigSrc) && (!fs.existsSync(rootConfigDest) || options.force)) {
|
|
276
293
|
fs.copyFileSync(rootConfigSrc, rootConfigDest);
|
|
277
|
-
console.log(`\x1b[32m[TestSpectra]\x1b[0m Created centralized configuration at ./spectra.config.ts`);
|
|
278
294
|
}
|
|
279
295
|
// 2. Centralized Root .testspectra folder for cache/logs
|
|
280
296
|
const rootTestDataDir = path.join(cwd, ".testspectra");
|
|
@@ -285,7 +301,6 @@ export async function initCommand(options = {}) {
|
|
|
285
301
|
const sharedTestingSrc = path.join(templateDir, "shared/testing");
|
|
286
302
|
if (fs.existsSync(sharedTestingSrc)) {
|
|
287
303
|
copyRecursive(sharedTestingSrc, sharedTestingAbsPath);
|
|
288
|
-
console.log(`\x1b[32m[TestSpectra]\x1b[0m Initialized shared testing library at ./${sharedTestingRelPath}`);
|
|
289
304
|
}
|
|
290
305
|
// 4. Distribute E2E Folders to Selected Feature Modules
|
|
291
306
|
const sampleE2eSrc = path.join(templateDir, "modules/auth/e2e");
|
|
@@ -318,7 +333,6 @@ export async function initCommand(options = {}) {
|
|
|
318
333
|
if (fs.existsSync(localConfig))
|
|
319
334
|
fs.unlinkSync(localConfig);
|
|
320
335
|
featureE2eDirs.push(targetE2eDir);
|
|
321
|
-
console.log(`\x1b[32m[TestSpectra]\x1b[0m Initialized ${e2eProjectName} in ./${path.relative(cwd, targetE2eDir)}`);
|
|
322
336
|
}
|
|
323
337
|
// 5. Update Root Solution tsconfig.json references
|
|
324
338
|
const rootTsConfigPath = path.join(cwd, "tsconfig.json");
|
|
@@ -344,6 +358,7 @@ export async function initCommand(options = {}) {
|
|
|
344
358
|
else {
|
|
345
359
|
rootTsConfig = { files: [], references };
|
|
346
360
|
}
|
|
361
|
+
fs.writeFileSync(rootTsConfigPath, JSON.stringify(rootTsConfig, null, 2), "utf-8");
|
|
347
362
|
// 6. Generate Ambient Types for Shared + All E2E Feature Directories
|
|
348
363
|
TypeGenerator.writeDeclarationFiles(cwd);
|
|
349
364
|
for (const e2eDir of featureE2eDirs) {
|
|
@@ -431,15 +446,18 @@ pnpm type-check
|
|
|
431
446
|
const readmeContent = `# ${projectName}\n\nAutomated cross-platform E2E testing powered by [TestSpectra](https://github.com/testspectra).\n${archSection}`;
|
|
432
447
|
fs.writeFileSync(rootReadmePath, readmeContent, "utf-8");
|
|
433
448
|
}
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
449
|
+
s.stop("Monorepo scaffolding complete!");
|
|
450
|
+
p.log.success(chalk.green("Centralized Configuration: ./spectra.config.ts"));
|
|
451
|
+
p.log.success(chalk.green("Centralized App Data/Cache: ./.testspectra/"));
|
|
452
|
+
p.log.success(chalk.green(`Shared Testing Library: ./${sharedTestingRelPath}`));
|
|
453
|
+
p.log.success(chalk.green("Architecture Documentation: ./ARCHITECTURE.md"));
|
|
454
|
+
p.log.message(chalk.cyan(`Initialized Features (${featureE2eDirs.length}):\n${featureE2eDirs.map((d) => ` - ./${path.relative(cwd, d)}`).join("\n")}`));
|
|
455
|
+
p.outro(chalk.bold.green("🎉 Enterprise Nx Monorepo ready for testing!"));
|
|
440
456
|
return;
|
|
441
457
|
}
|
|
442
458
|
// --- STANDALONE / DEFAULT PROJECT FLOW ---
|
|
459
|
+
const s = p.spinner();
|
|
460
|
+
s.start("Scaffolding standalone TestSpectra project...");
|
|
443
461
|
copyRecursive(templateDir, cwd);
|
|
444
462
|
if (!isInternalWorkspace) {
|
|
445
463
|
const pnpmWorkspacePath = path.join(cwd, "pnpm-workspace.yaml");
|
|
@@ -491,7 +509,8 @@ pnpm type-check
|
|
|
491
509
|
const standaloneReadme = `# ${projectName}\n\nTestSpectra automated testing project.\n${standaloneArchSection}`;
|
|
492
510
|
fs.writeFileSync(standaloneReadmePath, standaloneReadme, "utf-8");
|
|
493
511
|
}
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
512
|
+
s.stop("Standalone project ready!");
|
|
513
|
+
p.log.success(chalk.green("Generated ambient multi-platform types in .testspectra/types/"));
|
|
514
|
+
p.log.success(chalk.green("Architecture documentation created at ./ARCHITECTURE.md"));
|
|
515
|
+
p.outro(chalk.bold.green("🎉 Project initialized successfully!"));
|
|
497
516
|
}
|
package/dist/types/generator.js
CHANGED
|
@@ -59,7 +59,7 @@ export class TypeGenerator {
|
|
|
59
59
|
for (const platform of platforms) {
|
|
60
60
|
const hierarchy = PLATFORM_HIERARCHY[platform];
|
|
61
61
|
let content = `// Auto-generated ambient declarations for TestSpectra [${platform.toUpperCase()}]\n`;
|
|
62
|
-
content += `//
|
|
62
|
+
content += `// Managed automatically by TestSpectra Language Service Plugin.\n\n`;
|
|
63
63
|
content += `/// <reference types="@wdio/globals/types" />\n`;
|
|
64
64
|
content += `/// <reference types="@wdio/mocha-framework" />\n`;
|
|
65
65
|
content += `/// <reference path="./fixtures.d.ts" />\n\n`;
|
|
@@ -271,7 +271,7 @@ export class TypeGenerator {
|
|
|
271
271
|
static generateFixturesDeclaration(cwd) {
|
|
272
272
|
const fixturesDirs = this.getEntityDirs(cwd, "fixtures");
|
|
273
273
|
let content = `// Auto-generated ambient fixture declarations for TestSpectra\n`;
|
|
274
|
-
content += `//
|
|
274
|
+
content += `// Managed automatically by TestSpectra Language Service Plugin.\n\n`;
|
|
275
275
|
content += `interface TestSpectraFixtures {\n`;
|
|
276
276
|
const declaredFixtures = new Set();
|
|
277
277
|
for (const fixturesDir of fixturesDirs) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@testspectra/cli",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.22",
|
|
4
4
|
"description": "TestSpectra Zero-Config Cross-Platform Test Runner CLI",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -20,17 +20,15 @@
|
|
|
20
20
|
"README.md"
|
|
21
21
|
],
|
|
22
22
|
"dependencies": {
|
|
23
|
+
"@clack/prompts": "^1.7.0",
|
|
23
24
|
"@testspectra/matchers": "^1.0.0",
|
|
24
25
|
"chalk": "^5.3.0",
|
|
25
|
-
"chokidar": "^3.6.0",
|
|
26
26
|
"commander": "^12.1.0",
|
|
27
27
|
"dotenv": "^16.4.5",
|
|
28
|
-
"inquirer": "^9.3.2",
|
|
29
28
|
"ora": "^8.0.1",
|
|
30
29
|
"zod": "^3.23.8"
|
|
31
30
|
},
|
|
32
31
|
"devDependencies": {
|
|
33
|
-
"@types/inquirer": "^9.0.7",
|
|
34
32
|
"@types/node": "^20.14.0",
|
|
35
33
|
"typescript": "^5.4.5"
|
|
36
34
|
},
|