@testspectra/cli 1.0.21 → 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.
@@ -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
- console.log(`\x1b[33m[TestSpectra]\x1b[0m Config already exists at ${path.basename(existingConfig)}. Use --force to overwrite.`);
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 choices = [
138
- { name: "Standard (Standalone or single project)", value: "default" },
139
- { name: "Nx Monorepo (Interactive multi-module feature discovery & centralized config)", value: "nx" },
140
- ];
141
- const answers = await inquirer.prompt([
142
- {
143
- type: "list",
144
- name: "template",
145
- message: "Select TestSpectra setup mode:",
146
- choices,
147
- default: isMonorepo ? "nx" : "default",
148
- },
149
- ]);
150
- selectedTemplate = answers.template;
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
- throw new Error(`[TestSpectra] Scaffold template directory not found at: ${templateDir}`);
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.17";
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
- const projectAnswers = await inquirer.prompt([
242
- {
243
- type: "checkbox",
244
- name: "selectedProjects",
245
- message: "Select workspace features where TestSpectra should be initialized:",
246
- choices: detectedProjects.map((p) => ({
247
- name: `${p.relPath} (${p.name})${p.hasNxProject ? " [Nx project.json]" : ""}`,
248
- value: p.relPath,
249
- checked: true,
250
- })),
251
- },
252
- ]);
253
- chosenProjects = projectAnswers.selectedProjects;
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 folderAnswers = await inquirer.prompt([
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
- console.log(`\n\x1b[32m[TestSpectra]\x1b[0m Enterprise Nx Monorepo initialized successfully!`);
435
- console.log(`\x1b[36m- Centralized Configuration:\x1b[0m ./spectra.config.ts`);
436
- console.log(`\x1b[36m- Centralized App Data/Cache:\x1b[0m ./.testspectra/`);
437
- console.log(`\x1b[36m- Shared Testing Library:\x1b[0m ./${sharedTestingRelPath}`);
438
- console.log(`\x1b[36m- Architecture Documentation:\x1b[0m ./ARCHITECTURE.md`);
439
- console.log(`\x1b[36m- Initialized Features (${featureE2eDirs.length}):\x1b[0m\n ${featureE2eDirs.map((d) => path.relative(cwd, d)).join("\n ")}`);
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
- console.log(`\x1b[32m[TestSpectra]\x1b[0m Initialized project from template successfully!`);
495
- console.log(`\x1b[32m[TestSpectra]\x1b[0m Generated documentation at ./ARCHITECTURE.md`);
496
- console.log(`\x1b[32m[TestSpectra]\x1b[0m Generated ambient multi-platform types in .testspectra/types/`);
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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@testspectra/cli",
3
- "version": "1.0.21",
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,16 +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
26
  "commander": "^12.1.0",
26
27
  "dotenv": "^16.4.5",
27
- "inquirer": "^9.3.2",
28
28
  "ora": "^8.0.1",
29
29
  "zod": "^3.23.8"
30
30
  },
31
31
  "devDependencies": {
32
- "@types/inquirer": "^9.0.7",
33
32
  "@types/node": "^20.14.0",
34
33
  "typescript": "^5.4.5"
35
34
  },