@testspectra/cli 1.0.18 → 1.0.20
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/README.md +85 -19
- package/dist/commands/init.js +127 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -161,14 +161,14 @@ export default defineConfig({
|
|
|
161
161
|
|
|
162
162
|
| Command | Description |
|
|
163
163
|
| :--- | :--- |
|
|
164
|
-
| `spectra init`
|
|
165
|
-
| `spectra run` |
|
|
164
|
+
| `spectra init` / `npx @testspectra/cli init` | Interactive project setup. Auto-detects standalone or Nx Monorepos, discovers workspace packages from `pnpm-workspace.yaml`, and scaffolds centralized/distributed configurations. |
|
|
165
|
+
| `spectra run [spec]` | Automatically updates ambient declarations and invokes the native Rust test runner to execute the test suite. |
|
|
166
166
|
| `spectra doctor` | Verifies local environment prerequisites (ADB, Java, Chrome, Bun, Node). |
|
|
167
167
|
| `spectra devices` | Lists connected Android/iOS devices and local browsers. |
|
|
168
168
|
|
|
169
169
|
---
|
|
170
170
|
|
|
171
|
-
## 4. Directory Structure
|
|
171
|
+
## 4. Standalone Project Directory Structure
|
|
172
172
|
|
|
173
173
|
```
|
|
174
174
|
├── spectra.config.ts # Central typed project configuration
|
|
@@ -189,19 +189,17 @@ export default defineConfig({
|
|
|
189
189
|
│ └── loginUser/
|
|
190
190
|
│ ├── web.step.ts
|
|
191
191
|
│ └── mobile.step.ts
|
|
192
|
-
├── hooks/
|
|
193
|
-
│ └──
|
|
194
|
-
|
|
195
|
-
│
|
|
196
|
-
│ └──
|
|
197
|
-
├──
|
|
198
|
-
│
|
|
199
|
-
│
|
|
200
|
-
│ ├── android.test.ts
|
|
201
|
-
│ └── ios.test.ts
|
|
192
|
+
├── global-hooks/ # Global lifecycle hooks
|
|
193
|
+
│ └── before.web.hook.ts
|
|
194
|
+
├── specs/ # Layered zero-import test cases
|
|
195
|
+
│ └── Auth/
|
|
196
|
+
│ └── TC-AUTH-01/
|
|
197
|
+
│ ├── web.test.ts
|
|
198
|
+
│ ├── android.test.ts
|
|
199
|
+
│ └── ios.test.ts
|
|
202
200
|
├── fixtures/ # Platform-agnostic test data
|
|
203
201
|
│ └── userData.json
|
|
204
|
-
└── .testspectra/ # Auto-generated ambient typings
|
|
202
|
+
└── .testspectra/ # Auto-generated ambient typings & cache
|
|
205
203
|
└── types/
|
|
206
204
|
├── fixtures.d.ts
|
|
207
205
|
├── web.d.ts
|
|
@@ -213,15 +211,83 @@ export default defineConfig({
|
|
|
213
211
|
|
|
214
212
|
---
|
|
215
213
|
|
|
216
|
-
## 5.
|
|
214
|
+
## 5. Enterprise Monorepo Architecture (Nx & PNPM Workspace)
|
|
215
|
+
|
|
216
|
+
TestSpectra features an enterprise-grade architecture for large monorepos: **"Centralized Configuration, Distributed Implementation"**.
|
|
217
|
+
|
|
218
|
+
```
|
|
219
|
+
┌───────────────────────────────────────────────────────────┐
|
|
220
|
+
│ Root Monorepo Directory │
|
|
221
|
+
│ │
|
|
222
|
+
│ ├── spectra.config.ts (Central Execution Config) │
|
|
223
|
+
│ ├── .testspectra/ (Central Cache, AppData, Log)│
|
|
224
|
+
│ ├── pnpm-workspace.yaml (Workspace Definition) │
|
|
225
|
+
│ ├── nx.json (Nx Target & Caching Rules) │
|
|
226
|
+
│ └── tsconfig.json (Solution References) │
|
|
227
|
+
└─────────────────────────────┬─────────────────────────────┘
|
|
228
|
+
│
|
|
229
|
+
┌──────────────────────────────┴──────────────────────────────┐
|
|
230
|
+
▼ ▼
|
|
231
|
+
┌───────────────────────────────┐ ┌───────────────────────────────┐
|
|
232
|
+
│ shared/testing/ (Library) │ │ packages/features/auth/e2e/ │
|
|
233
|
+
├───────────────────────────────┤ ├───────────────────────────────┤
|
|
234
|
+
│ - Shared Page Objects │ ◄─── Auto-Scanned Globals ──│ - Feature Specs (specs/) │
|
|
235
|
+
│ - Shared Steps (loginAsAdmin) │ (100% Zero-Import!) │ - Feature Page Objects (POMs) │
|
|
236
|
+
│ - Shared Actions (dismissBtn) │ │ - project.json (Nx Target) │
|
|
237
|
+
│ - Shared Fixtures (appConfig) │ │ - tsconfig.web/android/ios │
|
|
238
|
+
└───────────────────────────────┘ └───────────────────────────────┘
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
### Key Pillars of Monorepo Integration:
|
|
242
|
+
|
|
243
|
+
1. **Interactive Workspace Auto-Discovery (`spectra init`)**:
|
|
244
|
+
- Parses `pnpm-workspace.yaml` (e.g. `packages/features/*`, `modules/**`).
|
|
245
|
+
- Scans sub-directories for existing `project.json` or `package.json`.
|
|
246
|
+
- Prompts the user with an interactive multi-select checkbox to choose which features will receive E2E testing.
|
|
247
|
+
- Prompts for customizable E2E folder names (e.g. `e2e`, `test/e2e`) and the shared testing library path (`shared/testing`).
|
|
248
|
+
|
|
249
|
+
2. **Centralized Configuration (`spectra.config.ts`) & App Data (`.testspectra/`)**:
|
|
250
|
+
- `spectra.config.ts` lives exclusively at the root of the monorepo.
|
|
251
|
+
- CLI test runs executed from sub-feature folders automatically traverse up to find the root configuration.
|
|
252
|
+
- Driver caches, binaries, and logs reside in `<root>/.testspectra/`, preventing repository clutter across individual feature folders.
|
|
253
|
+
|
|
254
|
+
3. **Global Zero-Import Consumption of Shared Library**:
|
|
255
|
+
- Entities placed inside `shared/testing/page-objects/`, `shared/testing/steps/`, `shared/testing/actions/`, and `shared/testing/fixtures/` are automatically aggregated into ambient declarations.
|
|
256
|
+
- Feature test specs consume shared Page Objects (`NavigationBar.goToHome()`), steps (`Step.loginAsAdmin()`), actions (`Spectra.dismissBanner()`), and fixtures (`Fixture.appConfig`) **without a single line of `import` statement**.
|
|
257
|
+
|
|
258
|
+
4. **Nx Target Execution & Affected Caching**:
|
|
259
|
+
- Each feature E2E folder includes an Nx `project.json`:
|
|
260
|
+
```json
|
|
261
|
+
{
|
|
262
|
+
"name": "auth-e2e",
|
|
263
|
+
"targets": {
|
|
264
|
+
"e2e": {
|
|
265
|
+
"executor": "nx:run-commands",
|
|
266
|
+
"options": { "command": "spectra run", "cwd": "modules/auth/e2e" },
|
|
267
|
+
"configurations": {
|
|
268
|
+
"android": { "command": "spectra run --target android" },
|
|
269
|
+
"ios": { "command": "spectra run --target ios" },
|
|
270
|
+
"headless": { "command": "spectra run --headless" }
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
```
|
|
276
|
+
- Running `nx run-many -t e2e` executes all feature test suites in parallel.
|
|
277
|
+
5. **Automatic Architecture Documentation (`ARCHITECTURE.md`)**:
|
|
278
|
+
- Every `spectra init` run produces a comprehensive, project-tailored `ARCHITECTURE.md` file in the root workspace.
|
|
279
|
+
- It documents the generated file map, exact distributed feature paths, shared library resolution, and zero-import mechanics for onboarding engineers.
|
|
280
|
+
|
|
281
|
+
---
|
|
282
|
+
|
|
283
|
+
## 6. Development & Verification
|
|
217
284
|
|
|
218
285
|
To verify the CLI and example app:
|
|
219
286
|
```bash
|
|
220
287
|
# Build CLI
|
|
221
288
|
pnpm --filter @testspectra/cli build
|
|
222
289
|
|
|
223
|
-
#
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
npx tsc -b tsconfig.json
|
|
290
|
+
# Test interactive scaffolding
|
|
291
|
+
npx @testspectra/cli init
|
|
292
|
+
pnpm type-check
|
|
227
293
|
```
|
package/dist/commands/init.js
CHANGED
|
@@ -190,6 +190,7 @@ export async function initCommand(options = {}) {
|
|
|
190
190
|
}
|
|
191
191
|
cur = path.dirname(cur);
|
|
192
192
|
}
|
|
193
|
+
const projectName = path.basename(cwd);
|
|
193
194
|
const cliDepVersion = isInternalWorkspace ? "workspace:*" : cliVersion;
|
|
194
195
|
// Helper: Copy directory contents recursively
|
|
195
196
|
function copyRecursive(src, dest, replacements = {}) {
|
|
@@ -343,21 +344,102 @@ export async function initCommand(options = {}) {
|
|
|
343
344
|
else {
|
|
344
345
|
rootTsConfig = { files: [], references };
|
|
345
346
|
}
|
|
346
|
-
fs.writeFileSync(rootTsConfigPath, JSON.stringify(rootTsConfig, null, 2), "utf-8");
|
|
347
347
|
// 6. Generate Ambient Types for Shared + All E2E Feature Directories
|
|
348
348
|
TypeGenerator.writeDeclarationFiles(cwd);
|
|
349
349
|
for (const e2eDir of featureE2eDirs) {
|
|
350
350
|
TypeGenerator.writeDeclarationFiles(e2eDir);
|
|
351
351
|
}
|
|
352
|
+
// 7. Generate ARCHITECTURE.md in root documenting the exact generated layout
|
|
353
|
+
const archDocContent = `# TestSpectra Enterprise Monorepo Architecture
|
|
354
|
+
|
|
355
|
+
This workspace uses TestSpectra's **"Centralized Configuration, Distributed Implementation"** model for automated cross-platform testing.
|
|
356
|
+
|
|
357
|
+
---
|
|
358
|
+
|
|
359
|
+
## 1. Directory & File Overview
|
|
360
|
+
|
|
361
|
+
### Centralized Root Elements
|
|
362
|
+
- \`./spectra.config.ts\`: Single source of truth for runtime configurations (Base URL, Appium devices, browser targets, timeouts).
|
|
363
|
+
- \`./.testspectra/\`: Centralized app data folder containing driver caches (ChromeDriver, GeckoDriver, Appium), execution logs, and universal ambient types (\`.testspectra/types/\`).
|
|
364
|
+
- \`./nx.json\`: Nx target defaults with execution caching and inputs.
|
|
365
|
+
- \`./pnpm-workspace.yaml\`: Monorepo package glob definitions.
|
|
366
|
+
- \`./tsconfig.json\`: Solution-style TypeScript orchestrator linking all distributed E2E projects and shared libraries.
|
|
367
|
+
|
|
368
|
+
### Centralized Shared Library (\`./${sharedTestingRelPath}\`)
|
|
369
|
+
Provides cross-feature reusable test entities with **100% zero-import global resolution**:
|
|
370
|
+
- \`page-objects/\`: Shared Page Object models (e.g. NavigationBar, AppHeader).
|
|
371
|
+
- \`steps/\`: Cross-feature business step flows (e.g. \`Step.loginAsAdmin()\`).
|
|
372
|
+
- \`actions/\`: Custom atomic actions (e.g. \`Spectra.dismissBanner()\`).
|
|
373
|
+
- \`fixtures/\`: Common test data and environment configurations (e.g. \`Fixture.appConfig\`).
|
|
374
|
+
|
|
375
|
+
### Distributed Feature E2E Modules (${featureE2eDirs.length} Features)
|
|
376
|
+
${featureE2eDirs.map((d) => {
|
|
377
|
+
const rel = path.relative(cwd, d);
|
|
378
|
+
const name = path.basename(path.dirname(d));
|
|
379
|
+
return `- \`./${rel}\` (**${name}**):
|
|
380
|
+
- \`specs/\`: Feature test cases (\`specs/<SuiteName>/<CaseId>/\` for web, android, ios).
|
|
381
|
+
- \`page-objects/\`: Feature-specific Page Objects (e.g. LoginPage, ProductPage).
|
|
382
|
+
- \`project.json\`: Nx targets (\`e2e\`, \`type-check\`) supporting configurations (\`--configuration=android\`, \`--configuration=ios\`, \`--configuration=headless\`).
|
|
383
|
+
- \`tsconfig.json\`: Solution TypeScript config reference.`;
|
|
384
|
+
}).join("\n")}
|
|
385
|
+
|
|
386
|
+
---
|
|
387
|
+
|
|
388
|
+
## 2. Zero-Import Consumption Flow
|
|
389
|
+
|
|
390
|
+
\`\`\`
|
|
391
|
+
┌──────────────────────────────┐ ┌──────────────────────────────┐
|
|
392
|
+
│ shared/testing/page-objects │ │ modules/*/e2e/specs/ │
|
|
393
|
+
│ shared/testing/steps │ ───────► │ (Zero-Import Test Cases) │
|
|
394
|
+
│ shared/testing/actions │ │ - NavigationBar.goToHome() │
|
|
395
|
+
│ shared/testing/fixtures │ │ - Step.loginAsAdmin() │
|
|
396
|
+
└──────────────────────────────┘ │ - Spectra.dismissBanner() │
|
|
397
|
+
└──────────────────────────────┘
|
|
398
|
+
\`\`\`
|
|
399
|
+
|
|
400
|
+
---
|
|
401
|
+
|
|
402
|
+
## 3. Running Tests via Nx
|
|
403
|
+
|
|
404
|
+
\`\`\`bash
|
|
405
|
+
# Run all feature E2E suites in parallel
|
|
406
|
+
pnpm nx run-many -t e2e
|
|
407
|
+
|
|
408
|
+
# Run only E2E tests for features changed in PR
|
|
409
|
+
pnpm nx affected -t e2e
|
|
410
|
+
|
|
411
|
+
# Run E2E for a specific feature on Android
|
|
412
|
+
pnpm nx run auth-${e2eFolderName}:e2e --configuration=android
|
|
413
|
+
|
|
414
|
+
# Run type check across all E2E projects
|
|
415
|
+
pnpm nx run-many -t type-check
|
|
416
|
+
# Or standard TypeScript compilation
|
|
417
|
+
pnpm type-check
|
|
418
|
+
\`\`\`
|
|
419
|
+
`;
|
|
420
|
+
fs.writeFileSync(path.join(cwd, "ARCHITECTURE.md"), archDocContent, "utf-8");
|
|
421
|
+
// 8. Safely append to root README.md (do not overwrite existing content)
|
|
422
|
+
const rootReadmePath = path.join(cwd, "README.md");
|
|
423
|
+
const archSection = `\n## E2E Testing Architecture (TestSpectra)\n\nThis workspace uses TestSpectra's **Centralized Configuration, Distributed Implementation** architecture.\nDetailed architecture, folder map, and zero-import mechanics are documented in: \n👉 **[ARCHITECTURE.md](./ARCHITECTURE.md)**\n\n- **Run all E2E tests**: \`pnpm nx run-many -t e2e\`\n- **Run affected E2E tests**: \`pnpm nx affected -t e2e\`\n- **Type-check E2E suites**: \`pnpm type-check\`\n`;
|
|
424
|
+
if (fs.existsSync(rootReadmePath)) {
|
|
425
|
+
const existing = fs.readFileSync(rootReadmePath, "utf-8");
|
|
426
|
+
if (!existing.includes("ARCHITECTURE.md")) {
|
|
427
|
+
fs.appendFileSync(rootReadmePath, archSection, "utf-8");
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
else {
|
|
431
|
+
const readmeContent = `# ${projectName}\n\nAutomated cross-platform E2E testing powered by [TestSpectra](https://github.com/testspectra).\n${archSection}`;
|
|
432
|
+
fs.writeFileSync(rootReadmePath, readmeContent, "utf-8");
|
|
433
|
+
}
|
|
352
434
|
console.log(`\n\x1b[32m[TestSpectra]\x1b[0m Enterprise Nx Monorepo initialized successfully!`);
|
|
353
435
|
console.log(`\x1b[36m- Centralized Configuration:\x1b[0m ./spectra.config.ts`);
|
|
354
436
|
console.log(`\x1b[36m- Centralized App Data/Cache:\x1b[0m ./.testspectra/`);
|
|
355
437
|
console.log(`\x1b[36m- Shared Testing Library:\x1b[0m ./${sharedTestingRelPath}`);
|
|
438
|
+
console.log(`\x1b[36m- Architecture Documentation:\x1b[0m ./ARCHITECTURE.md`);
|
|
356
439
|
console.log(`\x1b[36m- Initialized Features (${featureE2eDirs.length}):\x1b[0m\n ${featureE2eDirs.map((d) => path.relative(cwd, d)).join("\n ")}`);
|
|
357
440
|
return;
|
|
358
441
|
}
|
|
359
442
|
// --- STANDALONE / DEFAULT PROJECT FLOW ---
|
|
360
|
-
const projectName = path.basename(cwd);
|
|
361
443
|
copyRecursive(templateDir, cwd);
|
|
362
444
|
if (!isInternalWorkspace) {
|
|
363
445
|
const pnpmWorkspacePath = path.join(cwd, "pnpm-workspace.yaml");
|
|
@@ -367,6 +449,49 @@ export async function initCommand(options = {}) {
|
|
|
367
449
|
}
|
|
368
450
|
}
|
|
369
451
|
TypeGenerator.writeDeclarationFiles(cwd);
|
|
452
|
+
// Standalone Architecture Documentation
|
|
453
|
+
const standaloneArchDoc = `# TestSpectra Project Architecture
|
|
454
|
+
|
|
455
|
+
This project is built with the TestSpectra testing framework.
|
|
456
|
+
|
|
457
|
+
## Structure & File Map
|
|
458
|
+
- \`./spectra.config.ts\`: Typed runner configuration.
|
|
459
|
+
- \`./specs/\`: Layered test cases (\`specs/<Suite>/<CaseId>/\` with \`web.test.ts\`, \`android.test.ts\`, \`ios.test.ts\`).
|
|
460
|
+
- \`./page-objects/\`: Page Object models per platform.
|
|
461
|
+
- \`./steps/\`: Business flows accessible globally via \`Step.*\`.
|
|
462
|
+
- \`./actions/\`: Atomic actions accessible globally via \`Spectra.*\`.
|
|
463
|
+
- \`./fixtures/\`: JSON fixtures accessible globally via \`Fixture.*\`.
|
|
464
|
+
- \`./global-hooks/\`: Global suite lifecycle hooks.
|
|
465
|
+
- \`./.testspectra/\`: Auto-generated ambient typings and local runtime cache.
|
|
466
|
+
|
|
467
|
+
## Execution
|
|
468
|
+
\`\`\`bash
|
|
469
|
+
# Run web test suite
|
|
470
|
+
pnpm test
|
|
471
|
+
|
|
472
|
+
# Run Android / iOS
|
|
473
|
+
pnpm spectra run --target android
|
|
474
|
+
pnpm spectra run --target ios
|
|
475
|
+
|
|
476
|
+
# Type check
|
|
477
|
+
pnpm type-check
|
|
478
|
+
\`\`\`
|
|
479
|
+
`;
|
|
480
|
+
fs.writeFileSync(path.join(cwd, "ARCHITECTURE.md"), standaloneArchDoc, "utf-8");
|
|
481
|
+
// Safely update root README.md (do not overwrite existing content)
|
|
482
|
+
const standaloneReadmePath = path.join(cwd, "README.md");
|
|
483
|
+
const standaloneArchSection = `\n## E2E Testing Architecture (TestSpectra)\n\nDetailed test architecture and folder map are documented in: \n👉 **[ARCHITECTURE.md](./ARCHITECTURE.md)**\n\n- **Run tests**: \`pnpm test\`\n- **Type-check**: \`pnpm type-check\`\n`;
|
|
484
|
+
if (fs.existsSync(standaloneReadmePath)) {
|
|
485
|
+
const existing = fs.readFileSync(standaloneReadmePath, "utf-8");
|
|
486
|
+
if (!existing.includes("ARCHITECTURE.md")) {
|
|
487
|
+
fs.appendFileSync(standaloneReadmePath, standaloneArchSection, "utf-8");
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
else {
|
|
491
|
+
const standaloneReadme = `# ${projectName}\n\nTestSpectra automated testing project.\n${standaloneArchSection}`;
|
|
492
|
+
fs.writeFileSync(standaloneReadmePath, standaloneReadme, "utf-8");
|
|
493
|
+
}
|
|
370
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`);
|
|
371
496
|
console.log(`\x1b[32m[TestSpectra]\x1b[0m Generated ambient multi-platform types in .testspectra/types/`);
|
|
372
497
|
}
|