@smooai/testing 1.0.0
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 +150 -0
- package/dist/cli.mjs +1365 -0
- package/dist/cli.mjs.map +1 -0
- package/dist/index.d.mts +50 -0
- package/dist/index.d.ts +50 -0
- package/dist/index.js +199 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +172 -0
- package/dist/index.mjs.map +1 -0
- package/dist/lib/types.d.mts +280 -0
- package/dist/lib/types.d.ts +280 -0
- package/dist/lib/types.js +19 -0
- package/dist/lib/types.js.map +1 -0
- package/dist/lib/types.mjs +1 -0
- package/dist/lib/types.mjs.map +1 -0
- package/package.json +77 -0
package/dist/cli.mjs
ADDED
|
@@ -0,0 +1,1365 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
4
|
+
var __esm = (fn, res) => function __init() {
|
|
5
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
6
|
+
};
|
|
7
|
+
var __export = (target, all) => {
|
|
8
|
+
for (var name in all)
|
|
9
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
// src/cli/utils/auth.ts
|
|
13
|
+
async function getAuthToken(credentials) {
|
|
14
|
+
if (cachedToken && Date.now() < tokenExpiresAt - 6e4) {
|
|
15
|
+
return cachedToken;
|
|
16
|
+
}
|
|
17
|
+
const response = await fetch(credentials.authUrl, {
|
|
18
|
+
method: "POST",
|
|
19
|
+
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
20
|
+
body: new URLSearchParams({
|
|
21
|
+
grant_type: "client_credentials",
|
|
22
|
+
provider: "client_credentials",
|
|
23
|
+
client_id: credentials.clientId,
|
|
24
|
+
client_secret: credentials.clientSecret
|
|
25
|
+
})
|
|
26
|
+
});
|
|
27
|
+
if (!response.ok) {
|
|
28
|
+
const body = await response.text().catch(() => "");
|
|
29
|
+
throw new Error(`Authentication failed: HTTP ${response.status}${body ? ` \u2014 ${body}` : ""}`);
|
|
30
|
+
}
|
|
31
|
+
const data = await response.json();
|
|
32
|
+
if (!data.access_token) {
|
|
33
|
+
throw new Error("Authentication failed: no access_token in response");
|
|
34
|
+
}
|
|
35
|
+
cachedToken = data.access_token;
|
|
36
|
+
tokenExpiresAt = Date.now() + (data.expires_in || 3600) * 1e3;
|
|
37
|
+
return cachedToken;
|
|
38
|
+
}
|
|
39
|
+
function clearTokenCache() {
|
|
40
|
+
cachedToken = null;
|
|
41
|
+
tokenExpiresAt = 0;
|
|
42
|
+
}
|
|
43
|
+
var cachedToken, tokenExpiresAt;
|
|
44
|
+
var init_auth = __esm({
|
|
45
|
+
"src/cli/utils/auth.ts"() {
|
|
46
|
+
"use strict";
|
|
47
|
+
cachedToken = null;
|
|
48
|
+
tokenExpiresAt = 0;
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
// src/cli/utils/api-client.ts
|
|
53
|
+
var ApiClient;
|
|
54
|
+
var init_api_client = __esm({
|
|
55
|
+
"src/cli/utils/api-client.ts"() {
|
|
56
|
+
"use strict";
|
|
57
|
+
init_auth();
|
|
58
|
+
ApiClient = class {
|
|
59
|
+
credentials;
|
|
60
|
+
baseUrl;
|
|
61
|
+
constructor(credentials) {
|
|
62
|
+
this.credentials = credentials;
|
|
63
|
+
this.baseUrl = credentials.apiUrl.replace(/\/+$/, "");
|
|
64
|
+
}
|
|
65
|
+
orgPath(path) {
|
|
66
|
+
return `${this.baseUrl}/organizations/${this.credentials.orgId}${path}`;
|
|
67
|
+
}
|
|
68
|
+
async request(method, url, body, retry = true) {
|
|
69
|
+
const token = await getAuthToken(this.credentials);
|
|
70
|
+
const response = await fetch(url, {
|
|
71
|
+
method,
|
|
72
|
+
headers: {
|
|
73
|
+
Authorization: `Bearer ${token}`,
|
|
74
|
+
"Content-Type": "application/json"
|
|
75
|
+
},
|
|
76
|
+
body: body != null ? JSON.stringify(body) : void 0
|
|
77
|
+
});
|
|
78
|
+
if (response.status === 401 && retry) {
|
|
79
|
+
clearTokenCache();
|
|
80
|
+
return this.request(method, url, body, false);
|
|
81
|
+
}
|
|
82
|
+
if (!response.ok) {
|
|
83
|
+
const errorBody = await response.text().catch(() => "");
|
|
84
|
+
throw new Error(`API error: HTTP ${response.status} ${response.statusText}${errorBody ? ` \u2014 ${errorBody}` : ""}`);
|
|
85
|
+
}
|
|
86
|
+
const text = await response.text();
|
|
87
|
+
if (!text) return void 0;
|
|
88
|
+
return JSON.parse(text);
|
|
89
|
+
}
|
|
90
|
+
async get(path, params) {
|
|
91
|
+
let url = this.orgPath(path);
|
|
92
|
+
if (params) {
|
|
93
|
+
const searchParams = new URLSearchParams();
|
|
94
|
+
for (const [key, value] of Object.entries(params)) {
|
|
95
|
+
if (value != null) searchParams.set(key, String(value));
|
|
96
|
+
}
|
|
97
|
+
const qs = searchParams.toString();
|
|
98
|
+
if (qs) url += `?${qs}`;
|
|
99
|
+
}
|
|
100
|
+
return this.request("GET", url);
|
|
101
|
+
}
|
|
102
|
+
async post(path, body) {
|
|
103
|
+
return this.request("POST", this.orgPath(path), body);
|
|
104
|
+
}
|
|
105
|
+
async patch(path, body) {
|
|
106
|
+
return this.request("PATCH", this.orgPath(path), body);
|
|
107
|
+
}
|
|
108
|
+
async delete(path) {
|
|
109
|
+
return this.request("DELETE", this.orgPath(path));
|
|
110
|
+
}
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
// src/cli/utils/credentials.ts
|
|
116
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
117
|
+
import { homedir } from "os";
|
|
118
|
+
import { join } from "path";
|
|
119
|
+
function loadCredentials() {
|
|
120
|
+
const envClientId = process.env.SMOOAI_CLIENT_ID;
|
|
121
|
+
const envClientSecret = process.env.SMOOAI_CLIENT_SECRET;
|
|
122
|
+
const envOrgId = process.env.SMOOAI_ORG_ID;
|
|
123
|
+
if (envClientId && envClientSecret && envOrgId) {
|
|
124
|
+
return {
|
|
125
|
+
clientId: envClientId,
|
|
126
|
+
clientSecret: envClientSecret,
|
|
127
|
+
orgId: envOrgId,
|
|
128
|
+
apiUrl: process.env.SMOOAI_API_URL || DEFAULT_API_URL,
|
|
129
|
+
authUrl: process.env.SMOOAI_AUTH_URL || DEFAULT_AUTH_URL
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
try {
|
|
133
|
+
if (!existsSync(CREDENTIALS_FILE)) return null;
|
|
134
|
+
const raw = readFileSync(CREDENTIALS_FILE, "utf-8");
|
|
135
|
+
const parsed = JSON.parse(raw);
|
|
136
|
+
if (!parsed.clientId || !parsed.clientSecret || !parsed.orgId) return null;
|
|
137
|
+
return {
|
|
138
|
+
clientId: parsed.clientId,
|
|
139
|
+
clientSecret: parsed.clientSecret,
|
|
140
|
+
orgId: parsed.orgId,
|
|
141
|
+
apiUrl: parsed.apiUrl || DEFAULT_API_URL,
|
|
142
|
+
authUrl: parsed.authUrl || DEFAULT_AUTH_URL
|
|
143
|
+
};
|
|
144
|
+
} catch {
|
|
145
|
+
return null;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
function saveCredentials(credentials) {
|
|
149
|
+
mkdirSync(SMOOAI_DIR, { recursive: true });
|
|
150
|
+
let existing = {};
|
|
151
|
+
try {
|
|
152
|
+
if (existsSync(CREDENTIALS_FILE)) {
|
|
153
|
+
existing = JSON.parse(readFileSync(CREDENTIALS_FILE, "utf-8"));
|
|
154
|
+
}
|
|
155
|
+
} catch {
|
|
156
|
+
}
|
|
157
|
+
const merged = { ...existing, ...credentials };
|
|
158
|
+
writeFileSync(CREDENTIALS_FILE, JSON.stringify(merged, null, 2), { mode: 384 });
|
|
159
|
+
}
|
|
160
|
+
function clearCredentials() {
|
|
161
|
+
if (!existsSync(CREDENTIALS_FILE)) return;
|
|
162
|
+
try {
|
|
163
|
+
const existing = JSON.parse(readFileSync(CREDENTIALS_FILE, "utf-8"));
|
|
164
|
+
delete existing.clientId;
|
|
165
|
+
delete existing.clientSecret;
|
|
166
|
+
writeFileSync(CREDENTIALS_FILE, JSON.stringify(existing, null, 2), { mode: 384 });
|
|
167
|
+
} catch {
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
function getCredentialsOrExit() {
|
|
171
|
+
const creds = loadCredentials();
|
|
172
|
+
if (!creds) {
|
|
173
|
+
console.error("Not logged in. Run `smooai-testing login` first, or set SMOOAI_CLIENT_ID, SMOOAI_CLIENT_SECRET, and SMOOAI_ORG_ID env vars.");
|
|
174
|
+
process.exit(1);
|
|
175
|
+
}
|
|
176
|
+
return creds;
|
|
177
|
+
}
|
|
178
|
+
var SMOOAI_DIR, CREDENTIALS_FILE, DEFAULT_API_URL, DEFAULT_AUTH_URL;
|
|
179
|
+
var init_credentials = __esm({
|
|
180
|
+
"src/cli/utils/credentials.ts"() {
|
|
181
|
+
"use strict";
|
|
182
|
+
SMOOAI_DIR = join(homedir(), ".smooai");
|
|
183
|
+
CREDENTIALS_FILE = join(SMOOAI_DIR, "credentials.json");
|
|
184
|
+
DEFAULT_API_URL = "https://api.production.smoo.ai";
|
|
185
|
+
DEFAULT_AUTH_URL = "https://auth.production.smoo.ai/token";
|
|
186
|
+
}
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
// src/cli/utils/output.ts
|
|
190
|
+
function isInteractive(jsonFlag) {
|
|
191
|
+
if (jsonFlag) return false;
|
|
192
|
+
return Boolean(process.stdout.isTTY);
|
|
193
|
+
}
|
|
194
|
+
function jsonOutput(data, exitCode = 0) {
|
|
195
|
+
console.log(JSON.stringify(data, null, 2));
|
|
196
|
+
process.exit(exitCode);
|
|
197
|
+
}
|
|
198
|
+
function errorOutput(message, details) {
|
|
199
|
+
if (isInteractive()) {
|
|
200
|
+
console.error(`Error: ${message}`);
|
|
201
|
+
if (details) console.error(details);
|
|
202
|
+
process.exit(1);
|
|
203
|
+
}
|
|
204
|
+
jsonOutput({ success: false, error: message, details }, 1);
|
|
205
|
+
}
|
|
206
|
+
var init_output = __esm({
|
|
207
|
+
"src/cli/utils/output.ts"() {
|
|
208
|
+
"use strict";
|
|
209
|
+
}
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
// src/cli/commands/cases/create.ts
|
|
213
|
+
var create_exports = {};
|
|
214
|
+
__export(create_exports, {
|
|
215
|
+
runCreate: () => runCreate
|
|
216
|
+
});
|
|
217
|
+
async function runCreate(options) {
|
|
218
|
+
try {
|
|
219
|
+
const creds = getCredentialsOrExit();
|
|
220
|
+
const client = new ApiClient(creds);
|
|
221
|
+
const body = { title: options.title };
|
|
222
|
+
if (options.description) body.description = options.description;
|
|
223
|
+
if (options.priority) body.priority = options.priority;
|
|
224
|
+
if (options.automationStatus) body.automationStatus = options.automationStatus;
|
|
225
|
+
if (options.tags) body.tags = options.tags.split(",").map((t) => t.trim());
|
|
226
|
+
const tc = await client.post("/testing/cases", body);
|
|
227
|
+
if (!isInteractive(options.json)) {
|
|
228
|
+
jsonOutput(tc);
|
|
229
|
+
}
|
|
230
|
+
console.log(`Created test case: ${tc.id}`);
|
|
231
|
+
console.log(` Title: ${tc.title}`);
|
|
232
|
+
console.log(` Priority: ${tc.priority ?? "N/A"}`);
|
|
233
|
+
} catch (err) {
|
|
234
|
+
errorOutput(err instanceof Error ? err.message : String(err));
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
var init_create = __esm({
|
|
238
|
+
"src/cli/commands/cases/create.ts"() {
|
|
239
|
+
"use strict";
|
|
240
|
+
init_api_client();
|
|
241
|
+
init_credentials();
|
|
242
|
+
init_output();
|
|
243
|
+
}
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
// src/cli/commands/cases/list.ts
|
|
247
|
+
var list_exports = {};
|
|
248
|
+
__export(list_exports, {
|
|
249
|
+
runList: () => runList
|
|
250
|
+
});
|
|
251
|
+
async function runList(options) {
|
|
252
|
+
try {
|
|
253
|
+
const creds = getCredentialsOrExit();
|
|
254
|
+
const client = new ApiClient(creds);
|
|
255
|
+
const result = await client.get("/testing/cases", {
|
|
256
|
+
tags: options.tags,
|
|
257
|
+
priority: options.priority,
|
|
258
|
+
automationStatus: options.automationStatus,
|
|
259
|
+
limit: options.limit,
|
|
260
|
+
offset: options.offset
|
|
261
|
+
});
|
|
262
|
+
if (!isInteractive(options.json)) {
|
|
263
|
+
jsonOutput(result);
|
|
264
|
+
}
|
|
265
|
+
console.log(`Test Cases (${result.data.length} of ${result.pagination.total}):
|
|
266
|
+
`);
|
|
267
|
+
for (const tc of result.data) {
|
|
268
|
+
const tags = tc.tags?.length ? ` [${tc.tags.join(", ")}]` : "";
|
|
269
|
+
console.log(` ${tc.id} ${(tc.priority ?? "-").padEnd(8)} ${tc.title}${tags}`);
|
|
270
|
+
}
|
|
271
|
+
if (result.pagination.hasMore) {
|
|
272
|
+
console.log(`
|
|
273
|
+
... ${result.pagination.total - result.data.length} more. Use --offset to paginate.`);
|
|
274
|
+
}
|
|
275
|
+
} catch (err) {
|
|
276
|
+
errorOutput(err instanceof Error ? err.message : String(err));
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
var init_list = __esm({
|
|
280
|
+
"src/cli/commands/cases/list.ts"() {
|
|
281
|
+
"use strict";
|
|
282
|
+
init_api_client();
|
|
283
|
+
init_credentials();
|
|
284
|
+
init_output();
|
|
285
|
+
}
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
// src/cli/commands/cases/get.ts
|
|
289
|
+
var get_exports = {};
|
|
290
|
+
__export(get_exports, {
|
|
291
|
+
runGet: () => runGet
|
|
292
|
+
});
|
|
293
|
+
async function runGet(id, options) {
|
|
294
|
+
try {
|
|
295
|
+
const creds = getCredentialsOrExit();
|
|
296
|
+
const client = new ApiClient(creds);
|
|
297
|
+
const tc = await client.get(`/testing/cases/${id}`);
|
|
298
|
+
if (!isInteractive(options.json)) {
|
|
299
|
+
jsonOutput(tc);
|
|
300
|
+
}
|
|
301
|
+
console.log(`Test Case: ${tc.id}`);
|
|
302
|
+
console.log(` Title: ${tc.title}`);
|
|
303
|
+
console.log(` Priority: ${tc.priority ?? "N/A"}`);
|
|
304
|
+
console.log(` Automation: ${tc.automationStatus ?? "N/A"}`);
|
|
305
|
+
if (tc.description) console.log(` Description: ${tc.description}`);
|
|
306
|
+
if (tc.tags?.length) console.log(` Tags: ${tc.tags.join(", ")}`);
|
|
307
|
+
console.log(` Created: ${tc.createdAt}`);
|
|
308
|
+
} catch (err) {
|
|
309
|
+
errorOutput(err instanceof Error ? err.message : String(err));
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
var init_get = __esm({
|
|
313
|
+
"src/cli/commands/cases/get.ts"() {
|
|
314
|
+
"use strict";
|
|
315
|
+
init_api_client();
|
|
316
|
+
init_credentials();
|
|
317
|
+
init_output();
|
|
318
|
+
}
|
|
319
|
+
});
|
|
320
|
+
|
|
321
|
+
// src/cli/commands/cases/update.ts
|
|
322
|
+
var update_exports = {};
|
|
323
|
+
__export(update_exports, {
|
|
324
|
+
runUpdate: () => runUpdate
|
|
325
|
+
});
|
|
326
|
+
async function runUpdate(id, options) {
|
|
327
|
+
try {
|
|
328
|
+
const creds = getCredentialsOrExit();
|
|
329
|
+
const client = new ApiClient(creds);
|
|
330
|
+
const body = {};
|
|
331
|
+
if (options.title) body.title = options.title;
|
|
332
|
+
if (options.description) body.description = options.description;
|
|
333
|
+
if (options.priority) body.priority = options.priority;
|
|
334
|
+
if (options.automationStatus) body.automationStatus = options.automationStatus;
|
|
335
|
+
if (options.tags) body.tags = options.tags.split(",").map((t) => t.trim());
|
|
336
|
+
const tc = await client.patch(`/testing/cases/${id}`, body);
|
|
337
|
+
if (!isInteractive(options.json)) {
|
|
338
|
+
jsonOutput(tc);
|
|
339
|
+
}
|
|
340
|
+
console.log(`Updated test case: ${tc.id}`);
|
|
341
|
+
console.log(` Title: ${tc.title}`);
|
|
342
|
+
} catch (err) {
|
|
343
|
+
errorOutput(err instanceof Error ? err.message : String(err));
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
var init_update = __esm({
|
|
347
|
+
"src/cli/commands/cases/update.ts"() {
|
|
348
|
+
"use strict";
|
|
349
|
+
init_api_client();
|
|
350
|
+
init_credentials();
|
|
351
|
+
init_output();
|
|
352
|
+
}
|
|
353
|
+
});
|
|
354
|
+
|
|
355
|
+
// src/cli/commands/cases/delete.ts
|
|
356
|
+
var delete_exports = {};
|
|
357
|
+
__export(delete_exports, {
|
|
358
|
+
runDelete: () => runDelete
|
|
359
|
+
});
|
|
360
|
+
async function runDelete(id, options) {
|
|
361
|
+
try {
|
|
362
|
+
const creds = getCredentialsOrExit();
|
|
363
|
+
const client = new ApiClient(creds);
|
|
364
|
+
await client.delete(`/testing/cases/${id}`);
|
|
365
|
+
if (!isInteractive(options.json)) {
|
|
366
|
+
jsonOutput({ success: true, id });
|
|
367
|
+
}
|
|
368
|
+
console.log(`Deleted test case: ${id}`);
|
|
369
|
+
} catch (err) {
|
|
370
|
+
errorOutput(err instanceof Error ? err.message : String(err));
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
var init_delete = __esm({
|
|
374
|
+
"src/cli/commands/cases/delete.ts"() {
|
|
375
|
+
"use strict";
|
|
376
|
+
init_api_client();
|
|
377
|
+
init_credentials();
|
|
378
|
+
init_output();
|
|
379
|
+
}
|
|
380
|
+
});
|
|
381
|
+
|
|
382
|
+
// src/cli/commands/deployments/create.ts
|
|
383
|
+
var create_exports2 = {};
|
|
384
|
+
__export(create_exports2, {
|
|
385
|
+
runCreate: () => runCreate2
|
|
386
|
+
});
|
|
387
|
+
async function runCreate2(options) {
|
|
388
|
+
try {
|
|
389
|
+
const creds = getCredentialsOrExit();
|
|
390
|
+
const client = new ApiClient(creds);
|
|
391
|
+
const body = { name: options.name };
|
|
392
|
+
if (options.environmentId) body.environmentId = options.environmentId;
|
|
393
|
+
if (options.status) body.status = options.status;
|
|
394
|
+
if (options.source) body.source = options.source;
|
|
395
|
+
if (options.externalId) body.externalId = options.externalId;
|
|
396
|
+
if (options.externalUrl) body.externalUrl = options.externalUrl;
|
|
397
|
+
if (options.ref) body.ref = options.ref;
|
|
398
|
+
const deployment = await client.post("/testing/deployments", body);
|
|
399
|
+
if (!isInteractive(options.json)) {
|
|
400
|
+
jsonOutput(deployment);
|
|
401
|
+
}
|
|
402
|
+
console.log(`Created deployment: ${deployment.id}`);
|
|
403
|
+
console.log(` Name: ${deployment.name}`);
|
|
404
|
+
console.log(` Status: ${deployment.status}`);
|
|
405
|
+
} catch (err) {
|
|
406
|
+
errorOutput(err instanceof Error ? err.message : String(err));
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
var init_create2 = __esm({
|
|
410
|
+
"src/cli/commands/deployments/create.ts"() {
|
|
411
|
+
"use strict";
|
|
412
|
+
init_api_client();
|
|
413
|
+
init_credentials();
|
|
414
|
+
init_output();
|
|
415
|
+
}
|
|
416
|
+
});
|
|
417
|
+
|
|
418
|
+
// src/cli/commands/deployments/list.ts
|
|
419
|
+
var list_exports2 = {};
|
|
420
|
+
__export(list_exports2, {
|
|
421
|
+
runList: () => runList2
|
|
422
|
+
});
|
|
423
|
+
async function runList2(options) {
|
|
424
|
+
try {
|
|
425
|
+
const creds = getCredentialsOrExit();
|
|
426
|
+
const client = new ApiClient(creds);
|
|
427
|
+
const result = await client.get("/testing/deployments", {
|
|
428
|
+
status: options.status,
|
|
429
|
+
environmentId: options.environmentId,
|
|
430
|
+
source: options.source,
|
|
431
|
+
limit: options.limit,
|
|
432
|
+
offset: options.offset
|
|
433
|
+
});
|
|
434
|
+
if (!isInteractive(options.json)) {
|
|
435
|
+
jsonOutput(result);
|
|
436
|
+
}
|
|
437
|
+
console.log(`Deployments (${result.data.length} of ${result.pagination.total}):
|
|
438
|
+
`);
|
|
439
|
+
for (const d of result.data) {
|
|
440
|
+
const ref = d.ref ? ` (${d.ref})` : "";
|
|
441
|
+
console.log(` ${d.id} ${d.status.padEnd(12)} ${d.name}${ref}`);
|
|
442
|
+
}
|
|
443
|
+
if (result.pagination.hasMore) {
|
|
444
|
+
console.log(`
|
|
445
|
+
... ${result.pagination.total - result.data.length} more. Use --offset to paginate.`);
|
|
446
|
+
}
|
|
447
|
+
} catch (err) {
|
|
448
|
+
errorOutput(err instanceof Error ? err.message : String(err));
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
var init_list2 = __esm({
|
|
452
|
+
"src/cli/commands/deployments/list.ts"() {
|
|
453
|
+
"use strict";
|
|
454
|
+
init_api_client();
|
|
455
|
+
init_credentials();
|
|
456
|
+
init_output();
|
|
457
|
+
}
|
|
458
|
+
});
|
|
459
|
+
|
|
460
|
+
// src/cli/commands/deployments/get.ts
|
|
461
|
+
var get_exports2 = {};
|
|
462
|
+
__export(get_exports2, {
|
|
463
|
+
runGet: () => runGet2
|
|
464
|
+
});
|
|
465
|
+
async function runGet2(id, options) {
|
|
466
|
+
try {
|
|
467
|
+
const creds = getCredentialsOrExit();
|
|
468
|
+
const client = new ApiClient(creds);
|
|
469
|
+
const deployment = await client.get(`/testing/deployments/${id}`);
|
|
470
|
+
if (!isInteractive(options.json)) {
|
|
471
|
+
jsonOutput(deployment);
|
|
472
|
+
}
|
|
473
|
+
console.log(`Deployment: ${deployment.id}`);
|
|
474
|
+
console.log(` Name: ${deployment.name}`);
|
|
475
|
+
console.log(` Status: ${deployment.status}`);
|
|
476
|
+
console.log(` Source: ${deployment.source ?? "N/A"}`);
|
|
477
|
+
console.log(` Ref: ${deployment.ref ?? "N/A"}`);
|
|
478
|
+
console.log(` External URL: ${deployment.externalUrl ?? "N/A"}`);
|
|
479
|
+
console.log(` Created: ${deployment.createdAt}`);
|
|
480
|
+
if (deployment.completedAt) {
|
|
481
|
+
console.log(` Completed: ${deployment.completedAt}`);
|
|
482
|
+
}
|
|
483
|
+
} catch (err) {
|
|
484
|
+
errorOutput(err instanceof Error ? err.message : String(err));
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
var init_get2 = __esm({
|
|
488
|
+
"src/cli/commands/deployments/get.ts"() {
|
|
489
|
+
"use strict";
|
|
490
|
+
init_api_client();
|
|
491
|
+
init_credentials();
|
|
492
|
+
init_output();
|
|
493
|
+
}
|
|
494
|
+
});
|
|
495
|
+
|
|
496
|
+
// src/cli/commands/deployments/update.ts
|
|
497
|
+
var update_exports2 = {};
|
|
498
|
+
__export(update_exports2, {
|
|
499
|
+
runUpdate: () => runUpdate2
|
|
500
|
+
});
|
|
501
|
+
async function runUpdate2(id, options) {
|
|
502
|
+
try {
|
|
503
|
+
const creds = getCredentialsOrExit();
|
|
504
|
+
const client = new ApiClient(creds);
|
|
505
|
+
const body = {};
|
|
506
|
+
if (options.name) body.name = options.name;
|
|
507
|
+
if (options.status) body.status = options.status;
|
|
508
|
+
if (options.externalUrl) body.externalUrl = options.externalUrl;
|
|
509
|
+
const deployment = await client.patch(`/testing/deployments/${id}`, body);
|
|
510
|
+
if (!isInteractive(options.json)) {
|
|
511
|
+
jsonOutput(deployment);
|
|
512
|
+
}
|
|
513
|
+
console.log(`Updated deployment: ${deployment.id}`);
|
|
514
|
+
console.log(` Status: ${deployment.status}`);
|
|
515
|
+
} catch (err) {
|
|
516
|
+
errorOutput(err instanceof Error ? err.message : String(err));
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
var init_update2 = __esm({
|
|
520
|
+
"src/cli/commands/deployments/update.ts"() {
|
|
521
|
+
"use strict";
|
|
522
|
+
init_api_client();
|
|
523
|
+
init_credentials();
|
|
524
|
+
init_output();
|
|
525
|
+
}
|
|
526
|
+
});
|
|
527
|
+
|
|
528
|
+
// src/cli/commands/deployments/delete.ts
|
|
529
|
+
var delete_exports2 = {};
|
|
530
|
+
__export(delete_exports2, {
|
|
531
|
+
runDelete: () => runDelete2
|
|
532
|
+
});
|
|
533
|
+
async function runDelete2(id, options) {
|
|
534
|
+
try {
|
|
535
|
+
const creds = getCredentialsOrExit();
|
|
536
|
+
const client = new ApiClient(creds);
|
|
537
|
+
await client.delete(`/testing/deployments/${id}`);
|
|
538
|
+
if (!isInteractive(options.json)) {
|
|
539
|
+
jsonOutput({ success: true, id });
|
|
540
|
+
}
|
|
541
|
+
console.log(`Deleted deployment: ${id}`);
|
|
542
|
+
} catch (err) {
|
|
543
|
+
errorOutput(err instanceof Error ? err.message : String(err));
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
var init_delete2 = __esm({
|
|
547
|
+
"src/cli/commands/deployments/delete.ts"() {
|
|
548
|
+
"use strict";
|
|
549
|
+
init_api_client();
|
|
550
|
+
init_credentials();
|
|
551
|
+
init_output();
|
|
552
|
+
}
|
|
553
|
+
});
|
|
554
|
+
|
|
555
|
+
// src/cli/commands/environments/create.ts
|
|
556
|
+
var create_exports3 = {};
|
|
557
|
+
__export(create_exports3, {
|
|
558
|
+
runCreate: () => runCreate3
|
|
559
|
+
});
|
|
560
|
+
async function runCreate3(options) {
|
|
561
|
+
try {
|
|
562
|
+
const creds = getCredentialsOrExit();
|
|
563
|
+
const client = new ApiClient(creds);
|
|
564
|
+
const body = { name: options.name };
|
|
565
|
+
if (options.description) body.description = options.description;
|
|
566
|
+
if (options.baseUrl) body.baseUrl = options.baseUrl;
|
|
567
|
+
const env = await client.post("/testing/environments", body);
|
|
568
|
+
if (!isInteractive(options.json)) {
|
|
569
|
+
jsonOutput(env);
|
|
570
|
+
}
|
|
571
|
+
console.log(`Created environment: ${env.id}`);
|
|
572
|
+
console.log(` Name: ${env.name}`);
|
|
573
|
+
} catch (err) {
|
|
574
|
+
errorOutput(err instanceof Error ? err.message : String(err));
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
var init_create3 = __esm({
|
|
578
|
+
"src/cli/commands/environments/create.ts"() {
|
|
579
|
+
"use strict";
|
|
580
|
+
init_api_client();
|
|
581
|
+
init_credentials();
|
|
582
|
+
init_output();
|
|
583
|
+
}
|
|
584
|
+
});
|
|
585
|
+
|
|
586
|
+
// src/cli/commands/environments/list.ts
|
|
587
|
+
var list_exports3 = {};
|
|
588
|
+
__export(list_exports3, {
|
|
589
|
+
runList: () => runList3
|
|
590
|
+
});
|
|
591
|
+
async function runList3(options) {
|
|
592
|
+
try {
|
|
593
|
+
const creds = getCredentialsOrExit();
|
|
594
|
+
const client = new ApiClient(creds);
|
|
595
|
+
const envs = await client.get("/testing/environments");
|
|
596
|
+
if (!isInteractive(options.json)) {
|
|
597
|
+
jsonOutput(envs);
|
|
598
|
+
}
|
|
599
|
+
console.log(`Test Environments (${envs.length}):
|
|
600
|
+
`);
|
|
601
|
+
for (const env of envs) {
|
|
602
|
+
const url = env.baseUrl ? ` (${env.baseUrl})` : "";
|
|
603
|
+
console.log(` ${env.id} ${env.name}${url}`);
|
|
604
|
+
}
|
|
605
|
+
} catch (err) {
|
|
606
|
+
errorOutput(err instanceof Error ? err.message : String(err));
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
var init_list3 = __esm({
|
|
610
|
+
"src/cli/commands/environments/list.ts"() {
|
|
611
|
+
"use strict";
|
|
612
|
+
init_api_client();
|
|
613
|
+
init_credentials();
|
|
614
|
+
init_output();
|
|
615
|
+
}
|
|
616
|
+
});
|
|
617
|
+
|
|
618
|
+
// src/cli/commands/environments/get.ts
|
|
619
|
+
var get_exports3 = {};
|
|
620
|
+
__export(get_exports3, {
|
|
621
|
+
runGet: () => runGet3
|
|
622
|
+
});
|
|
623
|
+
async function runGet3(id, options) {
|
|
624
|
+
try {
|
|
625
|
+
const creds = getCredentialsOrExit();
|
|
626
|
+
const client = new ApiClient(creds);
|
|
627
|
+
const env = await client.get(`/testing/environments/${id}`);
|
|
628
|
+
if (!isInteractive(options.json)) {
|
|
629
|
+
jsonOutput(env);
|
|
630
|
+
}
|
|
631
|
+
console.log(`Environment: ${env.id}`);
|
|
632
|
+
console.log(` Name: ${env.name}`);
|
|
633
|
+
console.log(` Description: ${env.description ?? "N/A"}`);
|
|
634
|
+
console.log(` Base URL: ${env.baseUrl ?? "N/A"}`);
|
|
635
|
+
console.log(` Created: ${env.createdAt}`);
|
|
636
|
+
} catch (err) {
|
|
637
|
+
errorOutput(err instanceof Error ? err.message : String(err));
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
var init_get3 = __esm({
|
|
641
|
+
"src/cli/commands/environments/get.ts"() {
|
|
642
|
+
"use strict";
|
|
643
|
+
init_api_client();
|
|
644
|
+
init_credentials();
|
|
645
|
+
init_output();
|
|
646
|
+
}
|
|
647
|
+
});
|
|
648
|
+
|
|
649
|
+
// src/cli/commands/environments/update.ts
|
|
650
|
+
var update_exports3 = {};
|
|
651
|
+
__export(update_exports3, {
|
|
652
|
+
runUpdate: () => runUpdate3
|
|
653
|
+
});
|
|
654
|
+
async function runUpdate3(id, options) {
|
|
655
|
+
try {
|
|
656
|
+
const creds = getCredentialsOrExit();
|
|
657
|
+
const client = new ApiClient(creds);
|
|
658
|
+
const body = {};
|
|
659
|
+
if (options.name) body.name = options.name;
|
|
660
|
+
if (options.description) body.description = options.description;
|
|
661
|
+
if (options.baseUrl) body.baseUrl = options.baseUrl;
|
|
662
|
+
const env = await client.patch(`/testing/environments/${id}`, body);
|
|
663
|
+
if (!isInteractive(options.json)) {
|
|
664
|
+
jsonOutput(env);
|
|
665
|
+
}
|
|
666
|
+
console.log(`Updated environment: ${env.id}`);
|
|
667
|
+
console.log(` Name: ${env.name}`);
|
|
668
|
+
} catch (err) {
|
|
669
|
+
errorOutput(err instanceof Error ? err.message : String(err));
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
var init_update3 = __esm({
|
|
673
|
+
"src/cli/commands/environments/update.ts"() {
|
|
674
|
+
"use strict";
|
|
675
|
+
init_api_client();
|
|
676
|
+
init_credentials();
|
|
677
|
+
init_output();
|
|
678
|
+
}
|
|
679
|
+
});
|
|
680
|
+
|
|
681
|
+
// src/cli/commands/runs/create.ts
|
|
682
|
+
var create_exports4 = {};
|
|
683
|
+
__export(create_exports4, {
|
|
684
|
+
runCreate: () => runCreate4
|
|
685
|
+
});
|
|
686
|
+
async function runCreate4(options) {
|
|
687
|
+
try {
|
|
688
|
+
const creds = getCredentialsOrExit();
|
|
689
|
+
const client = new ApiClient(creds);
|
|
690
|
+
const body = { name: options.name };
|
|
691
|
+
if (options.environment) body.environment = options.environment;
|
|
692
|
+
if (options.environmentId) body.environmentId = options.environmentId;
|
|
693
|
+
if (options.deploymentId) body.deploymentId = options.deploymentId;
|
|
694
|
+
if (options.tool) body.tool = options.tool;
|
|
695
|
+
if (options.runnerName) body.runnerName = options.runnerName;
|
|
696
|
+
if (options.runnerUrl) body.runnerUrl = options.runnerUrl;
|
|
697
|
+
const run = await client.post("/testing/runs", body);
|
|
698
|
+
if (!isInteractive(options.json)) {
|
|
699
|
+
jsonOutput(run);
|
|
700
|
+
}
|
|
701
|
+
console.log(`Created test run: ${run.id}`);
|
|
702
|
+
console.log(` Name: ${run.name}`);
|
|
703
|
+
console.log(` Status: ${run.status}`);
|
|
704
|
+
} catch (err) {
|
|
705
|
+
errorOutput(err instanceof Error ? err.message : String(err));
|
|
706
|
+
}
|
|
707
|
+
}
|
|
708
|
+
var init_create4 = __esm({
|
|
709
|
+
"src/cli/commands/runs/create.ts"() {
|
|
710
|
+
"use strict";
|
|
711
|
+
init_api_client();
|
|
712
|
+
init_credentials();
|
|
713
|
+
init_output();
|
|
714
|
+
}
|
|
715
|
+
});
|
|
716
|
+
|
|
717
|
+
// src/cli/commands/runs/list.ts
|
|
718
|
+
var list_exports4 = {};
|
|
719
|
+
__export(list_exports4, {
|
|
720
|
+
runList: () => runList4
|
|
721
|
+
});
|
|
722
|
+
async function runList4(options) {
|
|
723
|
+
try {
|
|
724
|
+
const creds = getCredentialsOrExit();
|
|
725
|
+
const client = new ApiClient(creds);
|
|
726
|
+
const result = await client.get("/testing/runs", {
|
|
727
|
+
status: options.status,
|
|
728
|
+
environmentId: options.environmentId,
|
|
729
|
+
tool: options.tool,
|
|
730
|
+
limit: options.limit,
|
|
731
|
+
offset: options.offset
|
|
732
|
+
});
|
|
733
|
+
if (!isInteractive(options.json)) {
|
|
734
|
+
jsonOutput(result);
|
|
735
|
+
}
|
|
736
|
+
console.log(`Test Runs (${result.data.length} of ${result.pagination.total}):
|
|
737
|
+
`);
|
|
738
|
+
for (const run of result.data) {
|
|
739
|
+
const summary = run.summary ? ` [${run.summary.passed ?? 0}P/${run.summary.failed ?? 0}F/${run.summary.skipped ?? 0}S]` : "";
|
|
740
|
+
console.log(` ${run.id} ${run.status.padEnd(8)} ${run.name}${summary}`);
|
|
741
|
+
}
|
|
742
|
+
if (result.pagination.hasMore) {
|
|
743
|
+
console.log(`
|
|
744
|
+
... ${result.pagination.total - result.data.length} more. Use --offset to paginate.`);
|
|
745
|
+
}
|
|
746
|
+
} catch (err) {
|
|
747
|
+
errorOutput(err instanceof Error ? err.message : String(err));
|
|
748
|
+
}
|
|
749
|
+
}
|
|
750
|
+
var init_list4 = __esm({
|
|
751
|
+
"src/cli/commands/runs/list.ts"() {
|
|
752
|
+
"use strict";
|
|
753
|
+
init_api_client();
|
|
754
|
+
init_credentials();
|
|
755
|
+
init_output();
|
|
756
|
+
}
|
|
757
|
+
});
|
|
758
|
+
|
|
759
|
+
// src/cli/commands/runs/get.ts
|
|
760
|
+
var get_exports4 = {};
|
|
761
|
+
__export(get_exports4, {
|
|
762
|
+
runGet: () => runGet4
|
|
763
|
+
});
|
|
764
|
+
async function runGet4(id, options) {
|
|
765
|
+
try {
|
|
766
|
+
const creds = getCredentialsOrExit();
|
|
767
|
+
const client = new ApiClient(creds);
|
|
768
|
+
const run = await client.get(`/testing/runs/${id}`);
|
|
769
|
+
if (!isInteractive(options.json)) {
|
|
770
|
+
jsonOutput(run);
|
|
771
|
+
}
|
|
772
|
+
console.log(`Test Run: ${run.id}`);
|
|
773
|
+
console.log(` Name: ${run.name}`);
|
|
774
|
+
console.log(` Status: ${run.status}`);
|
|
775
|
+
console.log(` Tool: ${run.tool ?? "N/A"}`);
|
|
776
|
+
if (run.summary) {
|
|
777
|
+
console.log(` Summary: ${run.summary.passed ?? 0} passed, ${run.summary.failed ?? 0} failed, ${run.summary.skipped ?? 0} skipped`);
|
|
778
|
+
}
|
|
779
|
+
if (run.durationMs) {
|
|
780
|
+
console.log(` Duration: ${(run.durationMs / 1e3).toFixed(1)}s`);
|
|
781
|
+
}
|
|
782
|
+
console.log(` Created: ${run.createdAt}`);
|
|
783
|
+
if (run.completedAt) {
|
|
784
|
+
console.log(` Completed: ${run.completedAt}`);
|
|
785
|
+
}
|
|
786
|
+
if (run.results && run.results.length > 0) {
|
|
787
|
+
console.log(` Results: ${run.results.length} test(s)`);
|
|
788
|
+
}
|
|
789
|
+
} catch (err) {
|
|
790
|
+
errorOutput(err instanceof Error ? err.message : String(err));
|
|
791
|
+
}
|
|
792
|
+
}
|
|
793
|
+
var init_get4 = __esm({
|
|
794
|
+
"src/cli/commands/runs/get.ts"() {
|
|
795
|
+
"use strict";
|
|
796
|
+
init_api_client();
|
|
797
|
+
init_credentials();
|
|
798
|
+
init_output();
|
|
799
|
+
}
|
|
800
|
+
});
|
|
801
|
+
|
|
802
|
+
// src/cli/commands/runs/update.ts
|
|
803
|
+
var update_exports4 = {};
|
|
804
|
+
__export(update_exports4, {
|
|
805
|
+
runUpdate: () => runUpdate4
|
|
806
|
+
});
|
|
807
|
+
async function runUpdate4(id, options) {
|
|
808
|
+
try {
|
|
809
|
+
const creds = getCredentialsOrExit();
|
|
810
|
+
const client = new ApiClient(creds);
|
|
811
|
+
const body = {};
|
|
812
|
+
if (options.status) body.status = options.status;
|
|
813
|
+
const run = await client.patch(`/testing/runs/${id}`, body);
|
|
814
|
+
if (!isInteractive(options.json)) {
|
|
815
|
+
jsonOutput(run);
|
|
816
|
+
}
|
|
817
|
+
console.log(`Updated test run: ${run.id}`);
|
|
818
|
+
console.log(` Status: ${run.status}`);
|
|
819
|
+
} catch (err) {
|
|
820
|
+
errorOutput(err instanceof Error ? err.message : String(err));
|
|
821
|
+
}
|
|
822
|
+
}
|
|
823
|
+
var init_update4 = __esm({
|
|
824
|
+
"src/cli/commands/runs/update.ts"() {
|
|
825
|
+
"use strict";
|
|
826
|
+
init_api_client();
|
|
827
|
+
init_credentials();
|
|
828
|
+
init_output();
|
|
829
|
+
}
|
|
830
|
+
});
|
|
831
|
+
|
|
832
|
+
// src/cli/components/Banner.tsx
|
|
833
|
+
import { Box, Text } from "ink";
|
|
834
|
+
import BigText from "ink-big-text";
|
|
835
|
+
import Gradient from "ink-gradient";
|
|
836
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
837
|
+
function Banner({ title }) {
|
|
838
|
+
return /* @__PURE__ */ jsxs(Box, { marginBottom: 1, flexDirection: "column", children: [
|
|
839
|
+
/* @__PURE__ */ jsx(Gradient, { colors: ["#f49f0a", "#ff6b6c"], children: /* @__PURE__ */ jsx(BigText, { text: "Smoo AI", font: "tiny" }) }),
|
|
840
|
+
/* @__PURE__ */ jsx(Text, { bold: true, children: title })
|
|
841
|
+
] });
|
|
842
|
+
}
|
|
843
|
+
var init_Banner = __esm({
|
|
844
|
+
"src/cli/components/Banner.tsx"() {
|
|
845
|
+
"use strict";
|
|
846
|
+
}
|
|
847
|
+
});
|
|
848
|
+
|
|
849
|
+
// src/cli/components/TaskList.tsx
|
|
850
|
+
import { Box as Box2, Text as Text2 } from "ink";
|
|
851
|
+
import Spinner from "ink-spinner";
|
|
852
|
+
import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
853
|
+
function TaskList({ tasks }) {
|
|
854
|
+
return /* @__PURE__ */ jsx2(Box2, { flexDirection: "column", marginTop: 1, children: tasks.map((task, i) => /* @__PURE__ */ jsxs2(Box2, { children: [
|
|
855
|
+
/* @__PURE__ */ jsxs2(Box2, { width: 3, children: [
|
|
856
|
+
task.status === "running" && /* @__PURE__ */ jsx2(Text2, { color: "yellow", children: /* @__PURE__ */ jsx2(Spinner, { type: "dots" }) }),
|
|
857
|
+
task.status === "done" && /* @__PURE__ */ jsx2(Text2, { color: "green", children: "\u2713" }),
|
|
858
|
+
task.status === "error" && /* @__PURE__ */ jsx2(Text2, { color: "red", children: "\u2717" }),
|
|
859
|
+
task.status === "pending" && /* @__PURE__ */ jsx2(Text2, { color: "gray", children: "\u25CB" })
|
|
860
|
+
] }),
|
|
861
|
+
/* @__PURE__ */ jsx2(Text2, { color: task.status === "error" ? "red" : task.status === "done" ? "green" : void 0, children: task.label }),
|
|
862
|
+
task.error && /* @__PURE__ */ jsxs2(Text2, { color: "red", children: [
|
|
863
|
+
" \u2014 ",
|
|
864
|
+
task.error
|
|
865
|
+
] })
|
|
866
|
+
] }, i)) });
|
|
867
|
+
}
|
|
868
|
+
var init_TaskList = __esm({
|
|
869
|
+
"src/cli/components/TaskList.tsx"() {
|
|
870
|
+
"use strict";
|
|
871
|
+
}
|
|
872
|
+
});
|
|
873
|
+
|
|
874
|
+
// src/cli/utils/ctrf.ts
|
|
875
|
+
import { readFileSync as readFileSync2 } from "fs";
|
|
876
|
+
import { z } from "zod";
|
|
877
|
+
function parseCtrfFile(filePath) {
|
|
878
|
+
let raw;
|
|
879
|
+
try {
|
|
880
|
+
raw = readFileSync2(filePath, "utf-8");
|
|
881
|
+
} catch (err) {
|
|
882
|
+
throw new Error(`Failed to read CTRF file: ${filePath} \u2014 ${err instanceof Error ? err.message : String(err)}`);
|
|
883
|
+
}
|
|
884
|
+
let parsed;
|
|
885
|
+
try {
|
|
886
|
+
parsed = JSON.parse(raw);
|
|
887
|
+
} catch {
|
|
888
|
+
throw new Error(`Invalid JSON in CTRF file: ${filePath}`);
|
|
889
|
+
}
|
|
890
|
+
const result = CtrfReportSchema.safeParse(parsed);
|
|
891
|
+
if (!result.success) {
|
|
892
|
+
const issues = result.error.issues.map((i) => ` ${i.path.join(".")}: ${i.message}`).join("\n");
|
|
893
|
+
throw new Error(`Invalid CTRF format:
|
|
894
|
+
${issues}`);
|
|
895
|
+
}
|
|
896
|
+
return result.data;
|
|
897
|
+
}
|
|
898
|
+
function summarizeCtrfResults(report) {
|
|
899
|
+
const summary = report.results.summary;
|
|
900
|
+
if (summary) {
|
|
901
|
+
const total = summary.tests ?? 0;
|
|
902
|
+
const passed = summary.passed ?? 0;
|
|
903
|
+
const failed = summary.failed ?? 0;
|
|
904
|
+
const skipped = summary.skipped ?? 0;
|
|
905
|
+
const pending = summary.pending ?? 0;
|
|
906
|
+
const other = summary.other ?? 0;
|
|
907
|
+
return { total, passed, failed, skipped, pending, other, hasFailed: failed > 0 };
|
|
908
|
+
}
|
|
909
|
+
const tests = report.results.tests ?? [];
|
|
910
|
+
const counts = { total: tests.length, passed: 0, failed: 0, skipped: 0, pending: 0, other: 0 };
|
|
911
|
+
for (const test of tests) {
|
|
912
|
+
if (test.status in counts) {
|
|
913
|
+
counts[test.status]++;
|
|
914
|
+
}
|
|
915
|
+
}
|
|
916
|
+
return { ...counts, hasFailed: counts.failed > 0 };
|
|
917
|
+
}
|
|
918
|
+
var CtrfTestSchema, CtrfReportSchema;
|
|
919
|
+
var init_ctrf = __esm({
|
|
920
|
+
"src/cli/utils/ctrf.ts"() {
|
|
921
|
+
"use strict";
|
|
922
|
+
CtrfTestSchema = z.object({
|
|
923
|
+
name: z.string(),
|
|
924
|
+
status: z.enum(["passed", "failed", "skipped", "pending", "other"]),
|
|
925
|
+
duration: z.number().optional(),
|
|
926
|
+
suite: z.string().optional(),
|
|
927
|
+
filePath: z.string().optional(),
|
|
928
|
+
message: z.string().optional(),
|
|
929
|
+
trace: z.string().optional(),
|
|
930
|
+
retries: z.number().optional(),
|
|
931
|
+
flaky: z.boolean().optional(),
|
|
932
|
+
browser: z.string().optional(),
|
|
933
|
+
tags: z.array(z.string()).optional(),
|
|
934
|
+
extra: z.record(z.string(), z.unknown()).optional()
|
|
935
|
+
});
|
|
936
|
+
CtrfReportSchema = z.object({
|
|
937
|
+
results: z.object({
|
|
938
|
+
tool: z.object({
|
|
939
|
+
name: z.string().optional(),
|
|
940
|
+
version: z.string().optional()
|
|
941
|
+
}).optional(),
|
|
942
|
+
summary: z.object({
|
|
943
|
+
tests: z.number().optional(),
|
|
944
|
+
passed: z.number().optional(),
|
|
945
|
+
failed: z.number().optional(),
|
|
946
|
+
skipped: z.number().optional(),
|
|
947
|
+
pending: z.number().optional(),
|
|
948
|
+
other: z.number().optional(),
|
|
949
|
+
start: z.number().optional(),
|
|
950
|
+
stop: z.number().optional()
|
|
951
|
+
}).optional(),
|
|
952
|
+
tests: z.array(CtrfTestSchema).optional(),
|
|
953
|
+
environment: z.record(z.string(), z.unknown()).optional()
|
|
954
|
+
})
|
|
955
|
+
});
|
|
956
|
+
}
|
|
957
|
+
});
|
|
958
|
+
|
|
959
|
+
// src/cli/commands/runs/report.tsx
|
|
960
|
+
var report_exports = {};
|
|
961
|
+
__export(report_exports, {
|
|
962
|
+
reportLogic: () => reportLogic,
|
|
963
|
+
runReport: () => runReport
|
|
964
|
+
});
|
|
965
|
+
import { basename } from "path";
|
|
966
|
+
import { render, Box as Box3, Text as Text3 } from "ink";
|
|
967
|
+
import { useEffect, useState } from "react";
|
|
968
|
+
import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
969
|
+
async function reportLogic(ctrfFile, options) {
|
|
970
|
+
const report = parseCtrfFile(ctrfFile);
|
|
971
|
+
const summary = summarizeCtrfResults(report);
|
|
972
|
+
const creds = getCredentialsOrExit();
|
|
973
|
+
const client = new ApiClient(creds);
|
|
974
|
+
const runName = options.name ?? basename(ctrfFile, ".json");
|
|
975
|
+
const runBody = {
|
|
976
|
+
name: runName,
|
|
977
|
+
tool: options.tool ?? report.results.tool?.name,
|
|
978
|
+
buildName: options.buildName ?? process.env.GITHUB_SHA
|
|
979
|
+
};
|
|
980
|
+
if (options.environment) runBody.environment = options.environment;
|
|
981
|
+
if (options.deploymentId) runBody.deploymentId = options.deploymentId;
|
|
982
|
+
if (options.buildUrl) {
|
|
983
|
+
runBody.buildUrl = options.buildUrl;
|
|
984
|
+
} else if (process.env.GITHUB_SERVER_URL && process.env.GITHUB_REPOSITORY && process.env.GITHUB_RUN_ID) {
|
|
985
|
+
runBody.buildUrl = `${process.env.GITHUB_SERVER_URL}/${process.env.GITHUB_REPOSITORY}/actions/runs/${process.env.GITHUB_RUN_ID}`;
|
|
986
|
+
}
|
|
987
|
+
const run = await client.post("/testing/runs", runBody);
|
|
988
|
+
let resultCount = 0;
|
|
989
|
+
try {
|
|
990
|
+
const resultResponse = await client.post(`/testing/runs/${run.id}/results`, {
|
|
991
|
+
results: report.results
|
|
992
|
+
});
|
|
993
|
+
resultCount = resultResponse.count;
|
|
994
|
+
} catch (err) {
|
|
995
|
+
await client.patch(`/testing/runs/${run.id}`, {
|
|
996
|
+
status: "errored",
|
|
997
|
+
completedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
998
|
+
});
|
|
999
|
+
throw err;
|
|
1000
|
+
}
|
|
1001
|
+
const updatedRun = await client.get(`/testing/runs/${run.id}`);
|
|
1002
|
+
return { run: updatedRun, resultCount, summary };
|
|
1003
|
+
}
|
|
1004
|
+
function ReportUI({ ctrfFile, options }) {
|
|
1005
|
+
const [tasks, setTasks] = useState([
|
|
1006
|
+
{ label: "Parsing CTRF report", status: "pending" },
|
|
1007
|
+
{ label: "Authenticating", status: "pending" },
|
|
1008
|
+
{ label: "Creating test run", status: "pending" },
|
|
1009
|
+
{ label: "Submitting results", status: "pending" }
|
|
1010
|
+
]);
|
|
1011
|
+
const [result, setResult] = useState(null);
|
|
1012
|
+
useEffect(() => {
|
|
1013
|
+
(async () => {
|
|
1014
|
+
try {
|
|
1015
|
+
setTasks((t) => t.map((task, i) => i === 0 ? { ...task, status: "running" } : task));
|
|
1016
|
+
const report = parseCtrfFile(ctrfFile);
|
|
1017
|
+
const summary = summarizeCtrfResults(report);
|
|
1018
|
+
setTasks((t) => t.map((task, i) => i === 0 ? { ...task, status: "done" } : task));
|
|
1019
|
+
setTasks((t) => t.map((task, i) => i === 1 ? { ...task, status: "running" } : task));
|
|
1020
|
+
const creds = getCredentialsOrExit();
|
|
1021
|
+
const client = new ApiClient(creds);
|
|
1022
|
+
setTasks((t) => t.map((task, i) => i === 1 ? { ...task, status: "done" } : task));
|
|
1023
|
+
setTasks((t) => t.map((task, i) => i === 2 ? { ...task, status: "running" } : task));
|
|
1024
|
+
const runName = options.name ?? basename(ctrfFile, ".json");
|
|
1025
|
+
const runBody = {
|
|
1026
|
+
name: runName,
|
|
1027
|
+
tool: options.tool ?? report.results.tool?.name,
|
|
1028
|
+
buildName: options.buildName ?? process.env.GITHUB_SHA
|
|
1029
|
+
};
|
|
1030
|
+
if (options.environment) runBody.environment = options.environment;
|
|
1031
|
+
if (options.deploymentId) runBody.deploymentId = options.deploymentId;
|
|
1032
|
+
if (options.buildUrl) {
|
|
1033
|
+
runBody.buildUrl = options.buildUrl;
|
|
1034
|
+
} else if (process.env.GITHUB_SERVER_URL && process.env.GITHUB_REPOSITORY && process.env.GITHUB_RUN_ID) {
|
|
1035
|
+
runBody.buildUrl = `${process.env.GITHUB_SERVER_URL}/${process.env.GITHUB_REPOSITORY}/actions/runs/${process.env.GITHUB_RUN_ID}`;
|
|
1036
|
+
}
|
|
1037
|
+
const run = await client.post("/testing/runs", runBody);
|
|
1038
|
+
setTasks((t) => t.map((task, i) => i === 2 ? { ...task, status: "done" } : task));
|
|
1039
|
+
setTasks((t) => t.map((task, i) => i === 3 ? { ...task, status: "running" } : task));
|
|
1040
|
+
const resultResponse = await client.post(`/testing/runs/${run.id}/results`, {
|
|
1041
|
+
results: report.results
|
|
1042
|
+
});
|
|
1043
|
+
setTasks((t) => t.map((task, i) => i === 3 ? { ...task, status: "done" } : task));
|
|
1044
|
+
const updatedRun = await client.get(`/testing/runs/${run.id}`);
|
|
1045
|
+
setResult({ run: updatedRun, resultCount: resultResponse.count, summary });
|
|
1046
|
+
} catch (err) {
|
|
1047
|
+
setTasks(
|
|
1048
|
+
(t) => t.map((task) => task.status === "running" ? { ...task, status: "error", error: err instanceof Error ? err.message : String(err) } : task)
|
|
1049
|
+
);
|
|
1050
|
+
}
|
|
1051
|
+
})();
|
|
1052
|
+
}, []);
|
|
1053
|
+
return /* @__PURE__ */ jsxs3(Box3, { flexDirection: "column", children: [
|
|
1054
|
+
/* @__PURE__ */ jsx3(Banner, { title: "Report Test Results" }),
|
|
1055
|
+
/* @__PURE__ */ jsx3(TaskList, { tasks }),
|
|
1056
|
+
result && /* @__PURE__ */ jsxs3(Box3, { marginTop: 1, flexDirection: "column", children: [
|
|
1057
|
+
/* @__PURE__ */ jsxs3(Text3, { color: result.summary.hasFailed ? "red" : "green", bold: true, children: [
|
|
1058
|
+
result.summary.hasFailed ? "\u2717 FAILED" : "\u2713 PASSED",
|
|
1059
|
+
" \u2014 ",
|
|
1060
|
+
result.resultCount,
|
|
1061
|
+
" results submitted"
|
|
1062
|
+
] }),
|
|
1063
|
+
/* @__PURE__ */ jsxs3(Text3, { children: [
|
|
1064
|
+
result.summary.passed,
|
|
1065
|
+
" passed, ",
|
|
1066
|
+
result.summary.failed,
|
|
1067
|
+
" failed, ",
|
|
1068
|
+
result.summary.skipped,
|
|
1069
|
+
" skipped"
|
|
1070
|
+
] }),
|
|
1071
|
+
/* @__PURE__ */ jsxs3(Text3, { dimColor: true, children: [
|
|
1072
|
+
"Run ID: ",
|
|
1073
|
+
result.run.id
|
|
1074
|
+
] })
|
|
1075
|
+
] })
|
|
1076
|
+
] });
|
|
1077
|
+
}
|
|
1078
|
+
function runReport(ctrfFile, options) {
|
|
1079
|
+
if (!isInteractive(options.json)) {
|
|
1080
|
+
reportLogic(ctrfFile, options).then(
|
|
1081
|
+
(result) => jsonOutput({
|
|
1082
|
+
success: true,
|
|
1083
|
+
runId: result.run.id,
|
|
1084
|
+
status: result.run.status,
|
|
1085
|
+
resultCount: result.resultCount,
|
|
1086
|
+
summary: result.summary
|
|
1087
|
+
}),
|
|
1088
|
+
(err) => {
|
|
1089
|
+
errorOutput(err instanceof Error ? err.message : String(err));
|
|
1090
|
+
}
|
|
1091
|
+
);
|
|
1092
|
+
return;
|
|
1093
|
+
}
|
|
1094
|
+
render(/* @__PURE__ */ jsx3(ReportUI, { ctrfFile, options }));
|
|
1095
|
+
}
|
|
1096
|
+
var init_report = __esm({
|
|
1097
|
+
"src/cli/commands/runs/report.tsx"() {
|
|
1098
|
+
"use strict";
|
|
1099
|
+
init_Banner();
|
|
1100
|
+
init_TaskList();
|
|
1101
|
+
init_api_client();
|
|
1102
|
+
init_credentials();
|
|
1103
|
+
init_ctrf();
|
|
1104
|
+
init_output();
|
|
1105
|
+
}
|
|
1106
|
+
});
|
|
1107
|
+
|
|
1108
|
+
// src/cli/commands/auth/login.tsx
|
|
1109
|
+
var login_exports = {};
|
|
1110
|
+
__export(login_exports, {
|
|
1111
|
+
loginLogic: () => loginLogic,
|
|
1112
|
+
runLogin: () => runLogin
|
|
1113
|
+
});
|
|
1114
|
+
import { render as render2, Box as Box4, Text as Text4 } from "ink";
|
|
1115
|
+
import { useEffect as useEffect2, useState as useState2 } from "react";
|
|
1116
|
+
import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
1117
|
+
async function loginLogic(options) {
|
|
1118
|
+
const clientId = options.clientId;
|
|
1119
|
+
const clientSecret = options.clientSecret;
|
|
1120
|
+
const orgId = options.orgId;
|
|
1121
|
+
const apiUrl = options.apiUrl ?? DEFAULT_API_URL2;
|
|
1122
|
+
const authUrl = options.authUrl ?? DEFAULT_AUTH_URL2;
|
|
1123
|
+
if (!clientId) throw new Error("Client ID is required. Use --client-id flag.");
|
|
1124
|
+
if (!clientSecret) throw new Error("Client secret is required. Use --client-secret flag.");
|
|
1125
|
+
if (!orgId) throw new Error("Organization ID is required. Use --org-id flag.");
|
|
1126
|
+
const credentials = { clientId, clientSecret, orgId, apiUrl, authUrl };
|
|
1127
|
+
const client = new ApiClient(credentials);
|
|
1128
|
+
await client.get("/testing/environments");
|
|
1129
|
+
saveCredentials(credentials);
|
|
1130
|
+
return { success: true, orgId };
|
|
1131
|
+
}
|
|
1132
|
+
function LoginUI({ options }) {
|
|
1133
|
+
const [tasks, setTasks] = useState2([
|
|
1134
|
+
{ label: "Validating credentials", status: "pending" },
|
|
1135
|
+
{ label: "Saving to ~/.smooai/credentials.json", status: "pending" }
|
|
1136
|
+
]);
|
|
1137
|
+
const [result, setResult] = useState2(null);
|
|
1138
|
+
useEffect2(() => {
|
|
1139
|
+
(async () => {
|
|
1140
|
+
setTasks((t) => t.map((task, i) => i === 0 ? { ...task, status: "running" } : task));
|
|
1141
|
+
try {
|
|
1142
|
+
const res = await loginLogic(options);
|
|
1143
|
+
setTasks([
|
|
1144
|
+
{ label: "Validating credentials", status: "done" },
|
|
1145
|
+
{ label: "Saving to ~/.smooai/credentials.json", status: "done" }
|
|
1146
|
+
]);
|
|
1147
|
+
setResult({ orgId: res.orgId });
|
|
1148
|
+
} catch (err) {
|
|
1149
|
+
setTasks(
|
|
1150
|
+
(t) => t.map((task) => task.status === "running" ? { ...task, status: "error", error: err instanceof Error ? err.message : String(err) } : task)
|
|
1151
|
+
);
|
|
1152
|
+
}
|
|
1153
|
+
})();
|
|
1154
|
+
}, []);
|
|
1155
|
+
return /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", children: [
|
|
1156
|
+
/* @__PURE__ */ jsx4(Banner, { title: "Login" }),
|
|
1157
|
+
/* @__PURE__ */ jsx4(TaskList, { tasks }),
|
|
1158
|
+
result && /* @__PURE__ */ jsx4(Box4, { marginTop: 1, children: /* @__PURE__ */ jsxs4(Text4, { color: "green", bold: true, children: [
|
|
1159
|
+
"Logged in successfully! Organization: ",
|
|
1160
|
+
result.orgId
|
|
1161
|
+
] }) })
|
|
1162
|
+
] });
|
|
1163
|
+
}
|
|
1164
|
+
function runLogin(options) {
|
|
1165
|
+
if (!isInteractive(options.json)) {
|
|
1166
|
+
loginLogic(options).then(
|
|
1167
|
+
(result) => jsonOutput(result),
|
|
1168
|
+
(err) => jsonOutput({ success: false, error: err.message }, 1)
|
|
1169
|
+
);
|
|
1170
|
+
return;
|
|
1171
|
+
}
|
|
1172
|
+
render2(/* @__PURE__ */ jsx4(LoginUI, { options }));
|
|
1173
|
+
}
|
|
1174
|
+
var DEFAULT_API_URL2, DEFAULT_AUTH_URL2;
|
|
1175
|
+
var init_login = __esm({
|
|
1176
|
+
"src/cli/commands/auth/login.tsx"() {
|
|
1177
|
+
"use strict";
|
|
1178
|
+
init_Banner();
|
|
1179
|
+
init_TaskList();
|
|
1180
|
+
init_api_client();
|
|
1181
|
+
init_credentials();
|
|
1182
|
+
init_output();
|
|
1183
|
+
DEFAULT_API_URL2 = "https://api.production.smoo.ai";
|
|
1184
|
+
DEFAULT_AUTH_URL2 = "https://auth.production.smoo.ai/token";
|
|
1185
|
+
}
|
|
1186
|
+
});
|
|
1187
|
+
|
|
1188
|
+
// src/cli/commands/auth/logout.ts
|
|
1189
|
+
var logout_exports = {};
|
|
1190
|
+
__export(logout_exports, {
|
|
1191
|
+
runLogout: () => runLogout
|
|
1192
|
+
});
|
|
1193
|
+
function runLogout(options) {
|
|
1194
|
+
clearCredentials();
|
|
1195
|
+
if (!isInteractive(options.json)) {
|
|
1196
|
+
jsonOutput({ success: true, message: "Logged out" });
|
|
1197
|
+
}
|
|
1198
|
+
console.log("Logged out. M2M credentials removed from ~/.smooai/credentials.json");
|
|
1199
|
+
}
|
|
1200
|
+
var init_logout = __esm({
|
|
1201
|
+
"src/cli/commands/auth/logout.ts"() {
|
|
1202
|
+
"use strict";
|
|
1203
|
+
init_credentials();
|
|
1204
|
+
init_output();
|
|
1205
|
+
}
|
|
1206
|
+
});
|
|
1207
|
+
|
|
1208
|
+
// src/cli/commands/auth/status.ts
|
|
1209
|
+
var status_exports = {};
|
|
1210
|
+
__export(status_exports, {
|
|
1211
|
+
runStatus: () => runStatus
|
|
1212
|
+
});
|
|
1213
|
+
function runStatus(options) {
|
|
1214
|
+
const creds = loadCredentials();
|
|
1215
|
+
if (!isInteractive(options.json)) {
|
|
1216
|
+
jsonOutput({
|
|
1217
|
+
loggedIn: !!creds,
|
|
1218
|
+
orgId: creds?.orgId ?? null,
|
|
1219
|
+
apiUrl: creds?.apiUrl ?? null,
|
|
1220
|
+
authUrl: creds?.authUrl ?? null
|
|
1221
|
+
});
|
|
1222
|
+
}
|
|
1223
|
+
if (!creds) {
|
|
1224
|
+
console.log("Not logged in. Run `smooai-testing login` to authenticate.");
|
|
1225
|
+
return;
|
|
1226
|
+
}
|
|
1227
|
+
console.log(`Logged in`);
|
|
1228
|
+
console.log(` Organization: ${creds.orgId}`);
|
|
1229
|
+
console.log(` API URL: ${creds.apiUrl}`);
|
|
1230
|
+
console.log(` Auth URL: ${creds.authUrl}`);
|
|
1231
|
+
console.log(` Client ID: ${creds.clientId.slice(0, 8)}...`);
|
|
1232
|
+
}
|
|
1233
|
+
var init_status = __esm({
|
|
1234
|
+
"src/cli/commands/auth/status.ts"() {
|
|
1235
|
+
"use strict";
|
|
1236
|
+
init_credentials();
|
|
1237
|
+
init_output();
|
|
1238
|
+
}
|
|
1239
|
+
});
|
|
1240
|
+
|
|
1241
|
+
// src/cli/index.ts
|
|
1242
|
+
import { Command } from "commander";
|
|
1243
|
+
|
|
1244
|
+
// src/cli/commands/cases/index.ts
|
|
1245
|
+
function createCasesCommand(program2) {
|
|
1246
|
+
const cases = program2.command("cases").description("Manage test cases");
|
|
1247
|
+
cases.command("create").description("Create a test case").requiredOption("--title <title>", "Test case title").option("--description <desc>", "Description").option("--priority <priority>", "Priority (e.g., critical, high, medium, low)").option("--automation-status <status>", "Automation status").option("--tags <tags>", "Comma-separated tags").action(async (opts) => {
|
|
1248
|
+
const { runCreate: runCreate5 } = await Promise.resolve().then(() => (init_create(), create_exports));
|
|
1249
|
+
runCreate5({ ...opts, json: program2.opts().json ?? opts.json });
|
|
1250
|
+
});
|
|
1251
|
+
cases.command("list").description("List test cases").option("--tags <tags>", "Filter by comma-separated tags").option("--priority <priority>", "Filter by priority").option("--automation-status <status>", "Filter by automation status").option("--limit <n>", "Max results", "50").option("--offset <n>", "Offset", "0").action(async (opts) => {
|
|
1252
|
+
const { runList: runList5 } = await Promise.resolve().then(() => (init_list(), list_exports));
|
|
1253
|
+
runList5({ ...opts, json: program2.opts().json ?? opts.json });
|
|
1254
|
+
});
|
|
1255
|
+
cases.command("get").description("Get a test case by ID").argument("<id>", "Test case ID").action(async (id, opts) => {
|
|
1256
|
+
const { runGet: runGet5 } = await Promise.resolve().then(() => (init_get(), get_exports));
|
|
1257
|
+
runGet5(id, { json: program2.opts().json ?? opts.json });
|
|
1258
|
+
});
|
|
1259
|
+
cases.command("update").description("Update a test case").argument("<id>", "Test case ID").option("--title <title>", "New title").option("--description <desc>", "New description").option("--priority <priority>", "New priority").option("--automation-status <status>", "New automation status").option("--tags <tags>", "New comma-separated tags").action(async (id, opts) => {
|
|
1260
|
+
const { runUpdate: runUpdate5 } = await Promise.resolve().then(() => (init_update(), update_exports));
|
|
1261
|
+
runUpdate5(id, { ...opts, json: program2.opts().json ?? opts.json });
|
|
1262
|
+
});
|
|
1263
|
+
cases.command("delete").description("Delete a test case").argument("<id>", "Test case ID").action(async (id, opts) => {
|
|
1264
|
+
const { runDelete: runDelete3 } = await Promise.resolve().then(() => (init_delete(), delete_exports));
|
|
1265
|
+
runDelete3(id, { json: program2.opts().json ?? opts.json });
|
|
1266
|
+
});
|
|
1267
|
+
return cases;
|
|
1268
|
+
}
|
|
1269
|
+
|
|
1270
|
+
// src/cli/commands/deployments/index.ts
|
|
1271
|
+
function createDeploymentsCommand(program2) {
|
|
1272
|
+
const deployments = program2.command("deployments").description("Manage deployments");
|
|
1273
|
+
deployments.command("create").description("Create a deployment").requiredOption("--name <name>", "Deployment name").option("--environment-id <id>", "Environment ID").option("--status <status>", "Status (pending, in_progress, success, failure, cancelled)").option("--source <source>", "Source (e.g., github, gitlab)").option("--external-id <id>", "External ID").option("--external-url <url>", "External URL").option("--ref <ref>", "Git ref").action(async (opts) => {
|
|
1274
|
+
const { runCreate: runCreate5 } = await Promise.resolve().then(() => (init_create2(), create_exports2));
|
|
1275
|
+
runCreate5({ ...opts, json: program2.opts().json ?? opts.json });
|
|
1276
|
+
});
|
|
1277
|
+
deployments.command("list").description("List deployments").option("--status <status>", "Filter by status").option("--environment-id <id>", "Filter by environment ID").option("--source <source>", "Filter by source").option("--limit <n>", "Max results", "50").option("--offset <n>", "Offset", "0").action(async (opts) => {
|
|
1278
|
+
const { runList: runList5 } = await Promise.resolve().then(() => (init_list2(), list_exports2));
|
|
1279
|
+
runList5({ ...opts, json: program2.opts().json ?? opts.json });
|
|
1280
|
+
});
|
|
1281
|
+
deployments.command("get").description("Get a deployment by ID").argument("<id>", "Deployment ID").action(async (id, opts) => {
|
|
1282
|
+
const { runGet: runGet5 } = await Promise.resolve().then(() => (init_get2(), get_exports2));
|
|
1283
|
+
runGet5(id, { json: program2.opts().json ?? opts.json });
|
|
1284
|
+
});
|
|
1285
|
+
deployments.command("update").description("Update a deployment").argument("<id>", "Deployment ID").option("--name <name>", "New name").option("--status <status>", "New status").option("--external-url <url>", "New external URL").action(async (id, opts) => {
|
|
1286
|
+
const { runUpdate: runUpdate5 } = await Promise.resolve().then(() => (init_update2(), update_exports2));
|
|
1287
|
+
runUpdate5(id, { ...opts, json: program2.opts().json ?? opts.json });
|
|
1288
|
+
});
|
|
1289
|
+
deployments.command("delete").description("Delete a deployment").argument("<id>", "Deployment ID").action(async (id, opts) => {
|
|
1290
|
+
const { runDelete: runDelete3 } = await Promise.resolve().then(() => (init_delete2(), delete_exports2));
|
|
1291
|
+
runDelete3(id, { json: program2.opts().json ?? opts.json });
|
|
1292
|
+
});
|
|
1293
|
+
return deployments;
|
|
1294
|
+
}
|
|
1295
|
+
|
|
1296
|
+
// src/cli/commands/environments/index.ts
|
|
1297
|
+
function createEnvironmentsCommand(program2) {
|
|
1298
|
+
const envs = program2.command("envs").description("Manage test environments");
|
|
1299
|
+
envs.command("create").description("Create a test environment").requiredOption("--name <name>", "Environment name").option("--description <desc>", "Description").option("--base-url <url>", "Base URL").action(async (opts) => {
|
|
1300
|
+
const { runCreate: runCreate5 } = await Promise.resolve().then(() => (init_create3(), create_exports3));
|
|
1301
|
+
runCreate5({ ...opts, json: program2.opts().json ?? opts.json });
|
|
1302
|
+
});
|
|
1303
|
+
envs.command("list").description("List test environments").action(async (opts) => {
|
|
1304
|
+
const { runList: runList5 } = await Promise.resolve().then(() => (init_list3(), list_exports3));
|
|
1305
|
+
runList5({ json: program2.opts().json ?? opts.json });
|
|
1306
|
+
});
|
|
1307
|
+
envs.command("get").description("Get a test environment by ID").argument("<id>", "Environment ID").action(async (id, opts) => {
|
|
1308
|
+
const { runGet: runGet5 } = await Promise.resolve().then(() => (init_get3(), get_exports3));
|
|
1309
|
+
runGet5(id, { json: program2.opts().json ?? opts.json });
|
|
1310
|
+
});
|
|
1311
|
+
envs.command("update").description("Update a test environment").argument("<id>", "Environment ID").option("--name <name>", "New name").option("--description <desc>", "New description").option("--base-url <url>", "New base URL").action(async (id, opts) => {
|
|
1312
|
+
const { runUpdate: runUpdate5 } = await Promise.resolve().then(() => (init_update3(), update_exports3));
|
|
1313
|
+
runUpdate5(id, { ...opts, json: program2.opts().json ?? opts.json });
|
|
1314
|
+
});
|
|
1315
|
+
return envs;
|
|
1316
|
+
}
|
|
1317
|
+
|
|
1318
|
+
// src/cli/commands/runs/index.ts
|
|
1319
|
+
function createRunsCommand(program2) {
|
|
1320
|
+
const runs = program2.command("runs").description("Manage test runs");
|
|
1321
|
+
runs.command("create").description("Create a test run").requiredOption("--name <name>", "Run name").option("--environment <name>", "Environment name (auto-creates if needed)").option("--environment-id <id>", "Environment ID").option("--deployment-id <id>", "Deployment ID").option("--tool <name>", "Tool name (e.g., vitest, playwright)").option("--runner-name <name>", "Runner name").option("--runner-url <url>", "Runner URL").action(async (opts) => {
|
|
1322
|
+
const { runCreate: runCreate5 } = await Promise.resolve().then(() => (init_create4(), create_exports4));
|
|
1323
|
+
runCreate5({ ...opts, json: program2.opts().json ?? opts.json });
|
|
1324
|
+
});
|
|
1325
|
+
runs.command("list").description("List test runs").option("--status <status>", "Filter by status").option("--environment-id <id>", "Filter by environment ID").option("--tool <name>", "Filter by tool").option("--limit <n>", "Max results", "50").option("--offset <n>", "Offset", "0").action(async (opts) => {
|
|
1326
|
+
const { runList: runList5 } = await Promise.resolve().then(() => (init_list4(), list_exports4));
|
|
1327
|
+
runList5({ ...opts, json: program2.opts().json ?? opts.json });
|
|
1328
|
+
});
|
|
1329
|
+
runs.command("get").description("Get a test run by ID").argument("<id>", "Test run ID").action(async (id, opts) => {
|
|
1330
|
+
const { runGet: runGet5 } = await Promise.resolve().then(() => (init_get4(), get_exports4));
|
|
1331
|
+
runGet5(id, { json: program2.opts().json ?? opts.json });
|
|
1332
|
+
});
|
|
1333
|
+
runs.command("update").description("Update a test run").argument("<id>", "Test run ID").option("--status <status>", "New status").action(async (id, opts) => {
|
|
1334
|
+
const { runUpdate: runUpdate5 } = await Promise.resolve().then(() => (init_update4(), update_exports4));
|
|
1335
|
+
runUpdate5(id, { ...opts, json: program2.opts().json ?? opts.json });
|
|
1336
|
+
});
|
|
1337
|
+
runs.command("report").description("Report test results from a CTRF file (create run + submit results)").argument("<ctrf-file>", "Path to CTRF JSON report file").option("--name <name>", "Run name (defaults to filename)").option("--environment <name>", "Environment name").option("--deployment-id <id>", "Deployment ID").option("--tool <name>", "Override tool name from CTRF").option("--build-name <name>", "Build name (e.g., git SHA)").option("--build-url <url>", "Build URL (e.g., CI run link)").action(async (ctrfFile, opts) => {
|
|
1338
|
+
const { runReport: runReport2 } = await Promise.resolve().then(() => (init_report(), report_exports));
|
|
1339
|
+
runReport2(ctrfFile, { ...opts, json: program2.opts().json ?? opts.json });
|
|
1340
|
+
});
|
|
1341
|
+
return runs;
|
|
1342
|
+
}
|
|
1343
|
+
|
|
1344
|
+
// src/cli/index.ts
|
|
1345
|
+
var program = new Command();
|
|
1346
|
+
program.name("smooai-testing").description("Smoo AI Testing SDK \u2014 manage test runs, cases, environments, and deployments").version("0.1.0");
|
|
1347
|
+
program.option("--json", "Output in JSON format (auto-enabled when no TTY detected)");
|
|
1348
|
+
program.command("login").description("Store M2M credentials for CLI access").requiredOption("--client-id <id>", "M2M client ID").requiredOption("--client-secret <secret>", "M2M client secret").requiredOption("--org-id <id>", "Organization ID").option("--api-url <url>", "API base URL", "https://api.production.smoo.ai").option("--auth-url <url>", "Auth token URL", "https://auth.production.smoo.ai/token").action(async (opts) => {
|
|
1349
|
+
const { runLogin: runLogin2 } = await Promise.resolve().then(() => (init_login(), login_exports));
|
|
1350
|
+
runLogin2({ ...opts, json: program.opts().json ?? opts.json });
|
|
1351
|
+
});
|
|
1352
|
+
program.command("logout").description("Remove stored credentials").action(async (opts) => {
|
|
1353
|
+
const { runLogout: runLogout2 } = await Promise.resolve().then(() => (init_logout(), logout_exports));
|
|
1354
|
+
runLogout2({ json: program.opts().json ?? opts.json });
|
|
1355
|
+
});
|
|
1356
|
+
program.command("status").description("Show current authentication status").action(async (opts) => {
|
|
1357
|
+
const { runStatus: runStatus2 } = await Promise.resolve().then(() => (init_status(), status_exports));
|
|
1358
|
+
runStatus2({ json: program.opts().json ?? opts.json });
|
|
1359
|
+
});
|
|
1360
|
+
createRunsCommand(program);
|
|
1361
|
+
createCasesCommand(program);
|
|
1362
|
+
createEnvironmentsCommand(program);
|
|
1363
|
+
createDeploymentsCommand(program);
|
|
1364
|
+
program.parse();
|
|
1365
|
+
//# sourceMappingURL=cli.mjs.map
|