@testspectra/cli 1.0.68 → 1.0.70

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.
Binary file
@@ -550,7 +550,7 @@ This workspace uses TestSpectra's **"Centralized Configuration, Distributed Impl
550
550
 
551
551
  ### Centralized Root Elements
552
552
  - \`./spectra.config.ts\`: Single source of truth for runtime configurations (Base URL, Appium devices, browser targets, timeouts).
553
- - \`./.testspectra/\`: Centralized app data folder containing driver caches (ChromeDriver, GeckoDriver, Appium), execution logs, and universal ambient types (\`.testspectra/types/\`).
553
+ - \`./.testspectra/\`: Local test project cache (execution logs, temp artifacts, and universal ambient types in \`.testspectra/types/\`). Global binaries and drivers are managed at \`~/.testspectra/drivers/\`.
554
554
  - \`./tsconfig.spectra.json\`: Solution TypeScript entry point equipped with TestSpectra language service plugin.
555
555
  - \`./tsconfig.spectra.web.json\`: Strongly-typed composite configuration for Web platform tests.
556
556
  - \`./tsconfig.spectra.android.json\`: Strongly-typed composite configuration for Android platform tests.
@@ -0,0 +1,3 @@
1
+ export declare function updateCommand(options?: {
2
+ check?: boolean;
3
+ }): Promise<void>;
@@ -0,0 +1,113 @@
1
+ import { execSync, execFileSync } from "child_process";
2
+ import fs from "fs";
3
+ import path from "path";
4
+ import { fileURLToPath } from "url";
5
+ const __filename = fileURLToPath(import.meta.url);
6
+ const __dirname = path.dirname(__filename);
7
+ const PACKAGES = ["@testspectra/cli", "@testspectra/matchers"];
8
+ function getLatestVersion(pkg) {
9
+ try {
10
+ const out = execFileSync("npm", ["view", pkg, "version", "--json"], {
11
+ stdio: ["ignore", "pipe", "ignore"],
12
+ timeout: 10000,
13
+ })
14
+ .toString()
15
+ .trim()
16
+ .replace(/"/g, "");
17
+ return out || null;
18
+ }
19
+ catch {
20
+ return null;
21
+ }
22
+ }
23
+ function getCurrentVersion(pkg, cwd) {
24
+ // Walk up to find the installed version
25
+ let cur = path.resolve(cwd);
26
+ while (cur && cur !== path.dirname(cur)) {
27
+ const pkgJson = path.join(cur, "node_modules", pkg, "package.json");
28
+ if (fs.existsSync(pkgJson)) {
29
+ try {
30
+ const p = JSON.parse(fs.readFileSync(pkgJson, "utf-8"));
31
+ return p.version ?? null;
32
+ }
33
+ catch { }
34
+ }
35
+ cur = path.dirname(cur);
36
+ }
37
+ return null;
38
+ }
39
+ function detectPackageManager(cwd) {
40
+ if (fs.existsSync(path.join(cwd, "pnpm-lock.yaml")))
41
+ return "pnpm";
42
+ if (fs.existsSync(path.join(cwd, "bun.lockb")) || fs.existsSync(path.join(cwd, "bun.lock")))
43
+ return "bun";
44
+ return "npm";
45
+ }
46
+ function buildInstallCmd(pm, pkgSpecs) {
47
+ const list = pkgSpecs.join(" ");
48
+ switch (pm) {
49
+ case "pnpm":
50
+ return `pnpm add -D ${list}`;
51
+ case "bun":
52
+ return `bun add -d ${list}`;
53
+ case "npm":
54
+ default:
55
+ return `npm install -D ${list}`;
56
+ }
57
+ }
58
+ export async function updateCommand(options = {}) {
59
+ const cwd = process.cwd();
60
+ const pm = detectPackageManager(cwd);
61
+ console.log("\x1b[36m┌ 🔄 TestSpectra Self-Update\x1b[0m");
62
+ console.log("\x1b[36m│\x1b[0m");
63
+ console.log(`\x1b[36m│\x1b[0m Package Manager: \x1b[33m${pm}\x1b[0m`);
64
+ console.log("\x1b[36m│\x1b[0m");
65
+ console.log("\x1b[36m│\x1b[0m Fetching latest versions from npm registry...");
66
+ const updates = [];
67
+ for (const pkg of PACKAGES) {
68
+ const current = getCurrentVersion(pkg, cwd);
69
+ const latest = getLatestVersion(pkg);
70
+ const needsUpdate = !!latest && latest !== current;
71
+ updates.push({ pkg, current, latest, needsUpdate });
72
+ const currentStr = current ? `\x1b[33mv${current}\x1b[0m` : "\x1b[90m(not installed)\x1b[0m";
73
+ const latestStr = latest ? `\x1b[32mv${latest}\x1b[0m` : "\x1b[31m(unavailable)\x1b[0m";
74
+ const arrow = needsUpdate ? ` → ${latestStr}` : "";
75
+ const status = !latest
76
+ ? "\x1b[31m✗\x1b[0m"
77
+ : needsUpdate
78
+ ? "\x1b[33m↑\x1b[0m"
79
+ : "\x1b[32m✓\x1b[0m";
80
+ console.log(`\x1b[36m│\x1b[0m ${status} ${pkg.padEnd(28)} ${currentStr}${arrow}`);
81
+ }
82
+ const toUpdate = updates.filter((u) => u.needsUpdate && u.latest);
83
+ console.log("\x1b[36m│\x1b[0m");
84
+ if (toUpdate.length === 0) {
85
+ console.log("\x1b[36m└──────────────────────────────────────────────────────────\x1b[0m");
86
+ console.log("\x1b[32m✓ All TestSpectra packages are already up-to-date!\x1b[0m\n");
87
+ return;
88
+ }
89
+ if (options.check) {
90
+ console.log(`\x1b[36m│\x1b[0m \x1b[33m${toUpdate.length} update(s) available. Run \x1b[36mspectra update\x1b[33m to install.\x1b[0m`);
91
+ console.log("\x1b[36m└──────────────────────────────────────────────────────────\x1b[0m\n");
92
+ return;
93
+ }
94
+ const pkgSpecs = toUpdate.map((u) => `${u.pkg}@${u.latest}`);
95
+ const installCmd = buildInstallCmd(pm, pkgSpecs);
96
+ console.log(`\x1b[36m│\x1b[0m Running: \x1b[90m${installCmd}\x1b[0m`);
97
+ console.log("\x1b[36m│\x1b[0m");
98
+ try {
99
+ execSync(installCmd, { cwd, stdio: "inherit" });
100
+ console.log("\x1b[36m│\x1b[0m");
101
+ console.log("\x1b[36m└──────────────────────────────────────────────────────────\x1b[0m");
102
+ console.log("\x1b[32m🎉 TestSpectra updated successfully!\x1b[0m");
103
+ for (const u of toUpdate) {
104
+ console.log(` \x1b[32m✓\x1b[0m ${u.pkg}: v${u.current ?? "?"} → v${u.latest}`);
105
+ }
106
+ console.log("");
107
+ }
108
+ catch (err) {
109
+ console.log("\x1b[36m└──────────────────────────────────────────────────────────\x1b[0m");
110
+ console.error(`\x1b[31m✗ Update failed: ${err.message}\x1b[0m\n`);
111
+ process.exit(1);
112
+ }
113
+ }
package/dist/index.js CHANGED
@@ -5,6 +5,7 @@ import { devicesCommand } from "./commands/devices.js";
5
5
  import { doctorCommand } from "./commands/doctor.js";
6
6
  import { initCommand } from "./commands/init.js";
7
7
  import { runCommand } from "./commands/run.js";
8
+ import { updateCommand } from "./commands/update.js";
8
9
  import { watchCommand } from "./commands/watch.js";
9
10
  export * from "./config/schema.js";
10
11
  export * from "./config/loader.js";
@@ -44,6 +45,11 @@ export function createCliProgram() {
44
45
  .option("--fix", "Attempt automatic fix / download of missing tools")
45
46
  .option("--json", "Output diagnostic results as JSON")
46
47
  .action(doctorCommand);
48
+ program
49
+ .command("update")
50
+ .description("Update @testspectra/cli and @testspectra/matchers to the latest version from npm")
51
+ .option("--check", "Only check for available updates without installing")
52
+ .action(updateCommand);
47
53
  program
48
54
  .command("devices")
49
55
  .description("List connected Android/iOS devices and local browsers")
@@ -36,18 +36,31 @@ export class RustCoreBridge {
36
36
  config: options.config,
37
37
  targetDevice: options.targetDevice,
38
38
  });
39
+ const home = process.env.HOME || process.env.USERPROFILE || "";
40
+ const globalDriversDir = path.join(home, ".testspectra", "drivers");
41
+ if (!fs.existsSync(globalDriversDir)) {
42
+ try {
43
+ fs.mkdirSync(globalDriversDir, { recursive: true });
44
+ }
45
+ catch { }
46
+ }
47
+ const runnerEnv = {
48
+ ...process.env,
49
+ TEST_SPECTRA_DRIVER_CACHE: globalDriversDir,
50
+ RUST_LOG: "info",
51
+ };
39
52
  return new Promise((resolve, reject) => {
40
53
  let child;
41
54
  if (binPath === "cargo") {
42
55
  const workspaceRoot = path.resolve(__dirname, "../../..");
43
56
  child = spawn("cargo", ["run", "--manifest-path", "core/test-runner/Cargo.toml", "--bin", "testspectra-runner", "--", payload], {
44
57
  cwd: workspaceRoot,
45
- env: { ...process.env, RUST_LOG: "info" },
58
+ env: runnerEnv,
46
59
  });
47
60
  }
48
61
  else {
49
62
  child = spawn(binPath, [payload], {
50
- env: { ...process.env, RUST_LOG: "info" },
63
+ env: runnerEnv,
51
64
  });
52
65
  }
53
66
  let runStatus = "failed";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@testspectra/cli",
3
- "version": "1.0.68",
3
+ "version": "1.0.70",
4
4
  "description": "TestSpectra Zero-Config Cross-Platform Test Runner CLI",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -30,7 +30,7 @@
30
30
  ],
31
31
  "dependencies": {
32
32
  "@clack/prompts": "^1.7.0",
33
- "@testspectra/matchers": "^1.0.68",
33
+ "@testspectra/matchers": "^1.0.70",
34
34
  "chalk": "^5.3.0",
35
35
  "commander": "^12.1.0",
36
36
  "dotenv": "^16.4.5",
@@ -8,8 +8,8 @@
8
8
  "type-check": "tsc -b"
9
9
  },
10
10
  "devDependencies": {
11
- "@testspectra/cli": "^1.0.68",
12
- "@testspectra/matchers": "^1.0.68",
11
+ "@testspectra/cli": "^1.0.70",
12
+ "@testspectra/matchers": "^1.0.70",
13
13
  "@types/node": "^20.14.0",
14
14
  "@wdio/cli": "^9.2.8",
15
15
  "@wdio/local-runner": "^9.2.8",
@@ -10,8 +10,8 @@
10
10
  "build": "nx run-many -t build"
11
11
  },
12
12
  "devDependencies": {
13
- "@testspectra/cli": "^1.0.68",
14
- "@testspectra/matchers": "^1.0.68",
13
+ "@testspectra/cli": "^1.0.70",
14
+ "@testspectra/matchers": "^1.0.70",
15
15
  "@types/node": "^20.14.0",
16
16
  "@wdio/cli": "^9.2.8",
17
17
  "@wdio/local-runner": "^9.2.8",