@testspectra/cli 1.0.65 → 1.0.67
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/doctor.d.ts +17 -1
- package/dist/commands/doctor.js +269 -45
- package/dist/index.js +1 -0
- package/package.json +2 -2
- package/templates/default/package.json +2 -2
- package/templates/nx/package.json +2 -2
|
@@ -1,3 +1,19 @@
|
|
|
1
|
-
export
|
|
1
|
+
export interface DependencyStatus {
|
|
2
|
+
name: string;
|
|
3
|
+
category: "core" | "web" | "mobile";
|
|
4
|
+
installed: boolean;
|
|
5
|
+
version: string | null;
|
|
6
|
+
required: boolean;
|
|
7
|
+
required_version: string | null;
|
|
8
|
+
depends_on: string[];
|
|
9
|
+
}
|
|
10
|
+
export interface SystemCheckResult {
|
|
11
|
+
all_ready: boolean;
|
|
12
|
+
missing_count: number;
|
|
13
|
+
dependencies: DependencyStatus[];
|
|
14
|
+
}
|
|
15
|
+
export declare function runSystemChecks(cwd?: string): Promise<SystemCheckResult>;
|
|
16
|
+
export declare function doctorCommand(options?: {
|
|
2
17
|
fix?: boolean;
|
|
18
|
+
json?: boolean;
|
|
3
19
|
}): Promise<void>;
|
package/dist/commands/doctor.js
CHANGED
|
@@ -1,54 +1,278 @@
|
|
|
1
|
+
import fs from "fs";
|
|
2
|
+
import path from "path";
|
|
1
3
|
import { execSync } from "child_process";
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
}
|
|
10
|
-
{
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
},
|
|
20
|
-
{
|
|
21
|
-
name: "Java JDK",
|
|
22
|
-
command: "java -version",
|
|
23
|
-
required: false,
|
|
24
|
-
},
|
|
25
|
-
{
|
|
26
|
-
name: "Google Chrome",
|
|
27
|
-
command: process.platform === "darwin"
|
|
28
|
-
? '"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome" --version'
|
|
29
|
-
: "google-chrome --version",
|
|
30
|
-
required: false,
|
|
31
|
-
},
|
|
32
|
-
];
|
|
33
|
-
let missingCount = 0;
|
|
34
|
-
for (const check of checks) {
|
|
35
|
-
try {
|
|
36
|
-
const out = execSync(check.command, { stdio: "pipe" }).toString().trim();
|
|
37
|
-
const firstLine = out.split("\n")[0];
|
|
38
|
-
console.log(` \x1b[32m✓\x1b[0m ${check.name.padEnd(30)} \x1b[90m(${firstLine})\x1b[0m`);
|
|
4
|
+
function runCommandSafe(cmd) {
|
|
5
|
+
try {
|
|
6
|
+
const out = execSync(cmd, { stdio: ["pipe", "pipe", "pipe"], timeout: 5000 }).toString().trim();
|
|
7
|
+
if (out)
|
|
8
|
+
return out;
|
|
9
|
+
}
|
|
10
|
+
catch (e) {
|
|
11
|
+
const combined = `${e.stdout?.toString() || ""} ${e.stderr?.toString() || ""}`.trim();
|
|
12
|
+
if (combined) {
|
|
13
|
+
const lower = combined.toLowerCase();
|
|
14
|
+
if (!lower.includes("not found") &&
|
|
15
|
+
!lower.includes("no such file") &&
|
|
16
|
+
!lower.includes("cannot find") &&
|
|
17
|
+
!lower.includes("no java runtime present") &&
|
|
18
|
+
!lower.includes("command not found")) {
|
|
19
|
+
return combined;
|
|
20
|
+
}
|
|
39
21
|
}
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
22
|
+
}
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
function checkChrome() {
|
|
26
|
+
const home = process.env.HOME || process.env.USERPROFILE || "";
|
|
27
|
+
const driversDir = path.join(home, ".testspectra", "drivers");
|
|
28
|
+
if (!fs.existsSync(driversDir)) {
|
|
29
|
+
return { installed: false, version: null };
|
|
30
|
+
}
|
|
31
|
+
try {
|
|
32
|
+
const entries = fs.readdirSync(driversDir, { recursive: true });
|
|
33
|
+
for (const entry of entries) {
|
|
34
|
+
const entryStr = String(entry);
|
|
35
|
+
if (entryStr.endsWith("Google Chrome for Testing") ||
|
|
36
|
+
entryStr.endsWith("chrome") ||
|
|
37
|
+
entryStr.endsWith("chrome.exe")) {
|
|
38
|
+
const fullPath = path.join(driversDir, entryStr);
|
|
39
|
+
if (fs.existsSync(fullPath) && fs.statSync(fullPath).isFile()) {
|
|
40
|
+
const out = runCommandSafe(`"${fullPath}" --version`);
|
|
41
|
+
if (out) {
|
|
42
|
+
return { installed: true, version: `${out.split("\n")[0].trim()} (Chrome for Testing)` };
|
|
43
|
+
}
|
|
44
|
+
// Binary exists but couldn't get version — still count as installed
|
|
45
|
+
return { installed: true, version: "Chrome for Testing (cached)" };
|
|
46
|
+
}
|
|
44
47
|
}
|
|
45
48
|
}
|
|
46
49
|
}
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
+
catch { }
|
|
51
|
+
return { installed: false, version: null };
|
|
52
|
+
}
|
|
53
|
+
function checkWorkspacePackage(pkgName, startDir) {
|
|
54
|
+
let cur = path.resolve(startDir);
|
|
55
|
+
while (cur && cur !== path.dirname(cur)) {
|
|
56
|
+
const pkgJsonPath = path.join(cur, "node_modules", pkgName, "package.json");
|
|
57
|
+
if (fs.existsSync(pkgJsonPath)) {
|
|
58
|
+
try {
|
|
59
|
+
const pkg = JSON.parse(fs.readFileSync(pkgJsonPath, "utf-8"));
|
|
60
|
+
return { installed: true, version: `v${pkg.version || "installed"}` };
|
|
61
|
+
}
|
|
62
|
+
catch { }
|
|
63
|
+
}
|
|
64
|
+
// Also check root package.json devDependencies/dependencies
|
|
65
|
+
const rootPkgPath = path.join(cur, "package.json");
|
|
66
|
+
if (fs.existsSync(rootPkgPath)) {
|
|
67
|
+
try {
|
|
68
|
+
const rPkg = JSON.parse(fs.readFileSync(rootPkgPath, "utf-8"));
|
|
69
|
+
if (rPkg.name === pkgName) {
|
|
70
|
+
return { installed: true, version: `v${rPkg.version || "workspace"}` };
|
|
71
|
+
}
|
|
72
|
+
const depVer = rPkg.devDependencies?.[pkgName] || rPkg.dependencies?.[pkgName];
|
|
73
|
+
if (depVer) {
|
|
74
|
+
return { installed: true, version: depVer };
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
catch { }
|
|
78
|
+
}
|
|
79
|
+
// Check subpackages if monorepo
|
|
80
|
+
const cand1 = path.join(cur, "tools", pkgName.replace("@testspectra/", ""), "package.json");
|
|
81
|
+
const cand2 = path.join(cur, "packages", pkgName.replace("@testspectra/", ""), "package.json");
|
|
82
|
+
const cand3 = path.join(cur, pkgName.replace("@testspectra/", ""), "package.json");
|
|
83
|
+
for (const cand of [cand1, cand2, cand3]) {
|
|
84
|
+
if (fs.existsSync(cand)) {
|
|
85
|
+
try {
|
|
86
|
+
const pkg = JSON.parse(fs.readFileSync(cand, "utf-8"));
|
|
87
|
+
if (pkg.name === pkgName) {
|
|
88
|
+
return { installed: true, version: `v${pkg.version || "workspace"}` };
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
catch { }
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
cur = path.dirname(cur);
|
|
95
|
+
}
|
|
96
|
+
return { installed: false, version: null };
|
|
97
|
+
}
|
|
98
|
+
export async function runSystemChecks(cwd = process.cwd()) {
|
|
99
|
+
const dependencies = [];
|
|
100
|
+
// --- 1. Essential Core ---
|
|
101
|
+
// Node.js
|
|
102
|
+
const nodeOut = runCommandSafe("node -v");
|
|
103
|
+
dependencies.push({
|
|
104
|
+
name: "Node.js",
|
|
105
|
+
category: "core",
|
|
106
|
+
installed: !!nodeOut,
|
|
107
|
+
version: nodeOut ? nodeOut.split("\n")[0].trim() : null,
|
|
108
|
+
required: true,
|
|
109
|
+
required_version: ">=18.0.0",
|
|
110
|
+
depends_on: [],
|
|
111
|
+
});
|
|
112
|
+
// Bun
|
|
113
|
+
const bunOut = runCommandSafe("bun -v");
|
|
114
|
+
dependencies.push({
|
|
115
|
+
name: "Bun",
|
|
116
|
+
category: "core",
|
|
117
|
+
installed: !!bunOut,
|
|
118
|
+
version: bunOut ? `v${bunOut.split("\n")[0].trim()}` : null,
|
|
119
|
+
required: false,
|
|
120
|
+
required_version: null,
|
|
121
|
+
depends_on: [],
|
|
122
|
+
});
|
|
123
|
+
// Git
|
|
124
|
+
const gitOut = runCommandSafe("git --version");
|
|
125
|
+
dependencies.push({
|
|
126
|
+
name: "Git",
|
|
127
|
+
category: "core",
|
|
128
|
+
installed: !!gitOut,
|
|
129
|
+
version: gitOut ? gitOut.replace("git version", "").trim() : null,
|
|
130
|
+
required: false,
|
|
131
|
+
required_version: null,
|
|
132
|
+
depends_on: [],
|
|
133
|
+
});
|
|
134
|
+
// --- 2. Web Automation & Browsers ---
|
|
135
|
+
// Google Chrome / Chromium
|
|
136
|
+
const chromeCheck = checkChrome();
|
|
137
|
+
dependencies.push({
|
|
138
|
+
name: "Google Chrome",
|
|
139
|
+
category: "web",
|
|
140
|
+
installed: chromeCheck.installed,
|
|
141
|
+
version: chromeCheck.version,
|
|
142
|
+
required: true,
|
|
143
|
+
required_version: "Stable / CfT",
|
|
144
|
+
depends_on: [],
|
|
145
|
+
});
|
|
146
|
+
// WebDriverIO Core packages
|
|
147
|
+
const webPackages = [
|
|
148
|
+
{ name: "WDIO CLI", pkg: "@wdio/cli", required: true },
|
|
149
|
+
{ name: "WebDriverIO Core", pkg: "webdriverio", required: true },
|
|
150
|
+
{ name: "WDIO Local Runner", pkg: "@wdio/local-runner", required: true },
|
|
151
|
+
{ name: "WDIO Globals", pkg: "@wdio/globals", required: true },
|
|
152
|
+
{ name: "WDIO Spec Reporter", pkg: "@wdio/spec-reporter", required: true },
|
|
153
|
+
{ name: "WDIO Mocha Framework", pkg: "@wdio/mocha-framework", required: true },
|
|
154
|
+
{ name: "WDIO DevTools Service", pkg: "@wdio/devtools-service", required: false },
|
|
155
|
+
{ name: "TestSpectra CLI", pkg: "@testspectra/cli", required: true },
|
|
156
|
+
{ name: "TestSpectra Matchers", pkg: "@testspectra/matchers", required: true },
|
|
157
|
+
];
|
|
158
|
+
for (const wp of webPackages) {
|
|
159
|
+
const pkgCheck = checkWorkspacePackage(wp.pkg, cwd);
|
|
160
|
+
dependencies.push({
|
|
161
|
+
name: wp.name,
|
|
162
|
+
category: "web",
|
|
163
|
+
installed: pkgCheck.installed,
|
|
164
|
+
version: pkgCheck.version,
|
|
165
|
+
required: wp.required,
|
|
166
|
+
required_version: null,
|
|
167
|
+
depends_on: ["Node.js"],
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
// --- 3. Mobile Testing (Android) ---
|
|
171
|
+
const adbOut = runCommandSafe("adb version");
|
|
172
|
+
let adbVer = null;
|
|
173
|
+
if (adbOut) {
|
|
174
|
+
const m = adbOut.match(/Version\s+([\d.]+)/i) || adbOut.match(/Android Debug Bridge version\s+([\d.]+)/i);
|
|
175
|
+
adbVer = m ? `v${m[1]}` : "Installed";
|
|
176
|
+
}
|
|
177
|
+
dependencies.push({
|
|
178
|
+
name: "ADB (Android Debug Bridge)",
|
|
179
|
+
category: "mobile",
|
|
180
|
+
installed: !!adbOut,
|
|
181
|
+
version: adbVer,
|
|
182
|
+
required: false,
|
|
183
|
+
required_version: null,
|
|
184
|
+
depends_on: [],
|
|
185
|
+
});
|
|
186
|
+
const javaOut = runCommandSafe("java -version 2>&1");
|
|
187
|
+
let javaVer = null;
|
|
188
|
+
if (javaOut && !javaOut.toLowerCase().includes("no java runtime present") && !javaOut.toLowerCase().includes("not found")) {
|
|
189
|
+
const m = javaOut.match(/(?:java|openjdk)\s+version\s+"([^"]+)"/i) || javaOut.match(/(?:java|openjdk)\s+([\d.]+)/i);
|
|
190
|
+
javaVer = m ? `v${m[1]}` : "Installed";
|
|
191
|
+
}
|
|
192
|
+
dependencies.push({
|
|
193
|
+
name: "Java JDK",
|
|
194
|
+
category: "mobile",
|
|
195
|
+
installed: !!javaVer,
|
|
196
|
+
version: javaVer,
|
|
197
|
+
required: false,
|
|
198
|
+
required_version: ">=21.0.0",
|
|
199
|
+
depends_on: [],
|
|
200
|
+
});
|
|
201
|
+
const appiumOut = runCommandSafe("appium -v");
|
|
202
|
+
dependencies.push({
|
|
203
|
+
name: "Appium",
|
|
204
|
+
category: "mobile",
|
|
205
|
+
installed: !!appiumOut,
|
|
206
|
+
version: appiumOut ? `v${appiumOut.split("\n")[0].trim()}` : null,
|
|
207
|
+
required: false,
|
|
208
|
+
required_version: ">=2.0.0",
|
|
209
|
+
depends_on: ["Node.js"],
|
|
210
|
+
});
|
|
211
|
+
const missingRequired = dependencies.filter((d) => d.required && !d.installed);
|
|
212
|
+
const missingCount = missingRequired.length;
|
|
213
|
+
return {
|
|
214
|
+
all_ready: missingCount === 0,
|
|
215
|
+
missing_count: missingCount,
|
|
216
|
+
dependencies,
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
export async function doctorCommand(options = {}) {
|
|
220
|
+
const cwd = process.cwd();
|
|
221
|
+
const result = await runSystemChecks(cwd);
|
|
222
|
+
if (options.json) {
|
|
223
|
+
console.log(JSON.stringify(result, null, 2));
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
226
|
+
console.log("\x1b[36m┌ 🩺 TestSpectra Doctor: System & Dependency Diagnostics\x1b[0m");
|
|
227
|
+
console.log("\x1b[36m│\x1b[0m");
|
|
228
|
+
const categories = [
|
|
229
|
+
{ key: "core", title: "Essential Core Runtimes" },
|
|
230
|
+
{ key: "web", title: "Web Automation (WebDriverIO & Browsers)" },
|
|
231
|
+
{ key: "mobile", title: "Mobile Testing (Android & Appium)" },
|
|
232
|
+
];
|
|
233
|
+
for (const cat of categories) {
|
|
234
|
+
const items = result.dependencies.filter((d) => d.category === cat.key);
|
|
235
|
+
if (items.length === 0)
|
|
236
|
+
continue;
|
|
237
|
+
console.log(`\x1b[36m│\x1b[0m \x1b[1m\x1b[34m${cat.title}\x1b[0m`);
|
|
238
|
+
for (const item of items) {
|
|
239
|
+
if (item.installed) {
|
|
240
|
+
const verStr = item.version ? `\x1b[90m(${item.version})\x1b[0m` : "";
|
|
241
|
+
console.log(`\x1b[36m│\x1b[0m \x1b[32m✓\x1b[0m ${item.name.padEnd(28)} ${verStr}`);
|
|
242
|
+
}
|
|
243
|
+
else if (item.required) {
|
|
244
|
+
console.log(`\x1b[36m│\x1b[0m \x1b[31m✗\x1b[0m ${item.name.padEnd(28)} \x1b[31m[Missing - Required]\x1b[0m`);
|
|
245
|
+
}
|
|
246
|
+
else {
|
|
247
|
+
console.log(`\x1b[36m│\x1b[0m \x1b[33m!\x1b[0m ${item.name.padEnd(28)} \x1b[90m(Not found - Optional)\x1b[0m`);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
console.log("\x1b[36m│\x1b[0m");
|
|
251
|
+
}
|
|
252
|
+
console.log("\x1b[36m└──────────────────────────────────────────────────────────\x1b[0m");
|
|
253
|
+
if (result.all_ready) {
|
|
254
|
+
console.log("\x1b[32m🎉 [TestSpectra] System is 100% ready for Web & Local Test Execution!\x1b[0m\n");
|
|
50
255
|
}
|
|
51
256
|
else {
|
|
52
|
-
console.log(`\x1b[33m[
|
|
257
|
+
console.log(`\x1b[33m⚠️ [TestSpectra] Found ${result.missing_count} missing required dependencies.\x1b[0m`);
|
|
258
|
+
console.log(" Run \x1b[36mspectra doctor --fix\x1b[0m or install missing tools above.\n");
|
|
259
|
+
if (options.fix) {
|
|
260
|
+
console.log("\x1b[36m[Doctor Fix] Attempting automated resolution...\x1b[0m");
|
|
261
|
+
const missingPkgs = result.dependencies
|
|
262
|
+
.filter((d) => !d.installed && d.category === "web" && d.name.startsWith("WDIO"))
|
|
263
|
+
.map((d) => d.name);
|
|
264
|
+
if (missingPkgs.length > 0) {
|
|
265
|
+
console.log(`Installing workspace packages: ${missingPkgs.join(", ")}`);
|
|
266
|
+
try {
|
|
267
|
+
execSync("pnpm add -D @wdio/cli webdriverio @wdio/local-runner @wdio/globals @wdio/spec-reporter @wdio/mocha-framework @wdio/devtools-service", {
|
|
268
|
+
stdio: "inherit",
|
|
269
|
+
});
|
|
270
|
+
console.log("\x1b[32m✓ Installed WebDriverIO packages successfully.\x1b[0m");
|
|
271
|
+
}
|
|
272
|
+
catch (err) {
|
|
273
|
+
console.error("\x1b[31m✗ Failed to install packages:\x1b[0m", err.message);
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
}
|
|
53
277
|
}
|
|
54
278
|
}
|
package/dist/index.js
CHANGED
|
@@ -42,6 +42,7 @@ export function createCliProgram() {
|
|
|
42
42
|
.command("doctor")
|
|
43
43
|
.description("Verify local environment prerequisites (ADB, Java, Chrome, Bun, Node)")
|
|
44
44
|
.option("--fix", "Attempt automatic fix / download of missing tools")
|
|
45
|
+
.option("--json", "Output diagnostic results as JSON")
|
|
45
46
|
.action(doctorCommand);
|
|
46
47
|
program
|
|
47
48
|
.command("devices")
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@testspectra/cli",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.67",
|
|
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.
|
|
33
|
+
"@testspectra/matchers": "^1.0.67",
|
|
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.
|
|
12
|
-
"@testspectra/matchers": "^1.0.
|
|
11
|
+
"@testspectra/cli": "^1.0.67",
|
|
12
|
+
"@testspectra/matchers": "^1.0.67",
|
|
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.
|
|
14
|
-
"@testspectra/matchers": "^1.0.
|
|
13
|
+
"@testspectra/cli": "^1.0.67",
|
|
14
|
+
"@testspectra/matchers": "^1.0.67",
|
|
15
15
|
"@types/node": "^20.14.0",
|
|
16
16
|
"@wdio/cli": "^9.2.8",
|
|
17
17
|
"@wdio/local-runner": "^9.2.8",
|