@desplega.ai/qa-use 2.0.1
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/LICENSE +21 -0
- package/README.md +1003 -0
- package/bin/qa-use.js +7 -0
- package/dist/lib/api/index.d.ts +296 -0
- package/dist/lib/api/index.d.ts.map +1 -0
- package/dist/lib/api/index.js +564 -0
- package/dist/lib/api/index.js.map +1 -0
- package/dist/lib/api/sse.d.ts +33 -0
- package/dist/lib/api/sse.d.ts.map +1 -0
- package/dist/lib/api/sse.js +97 -0
- package/dist/lib/api/sse.js.map +1 -0
- package/dist/lib/browser/index.d.ts +28 -0
- package/dist/lib/browser/index.d.ts.map +1 -0
- package/dist/lib/browser/index.js +145 -0
- package/dist/lib/browser/index.js.map +1 -0
- package/dist/lib/env/index.d.ts +41 -0
- package/dist/lib/env/index.d.ts.map +1 -0
- package/dist/lib/env/index.js +125 -0
- package/dist/lib/env/index.js.map +1 -0
- package/dist/lib/tunnel/index.d.ts +38 -0
- package/dist/lib/tunnel/index.d.ts.map +1 -0
- package/dist/lib/tunnel/index.js +154 -0
- package/dist/lib/tunnel/index.js.map +1 -0
- package/dist/package.json +100 -0
- package/dist/src/cli/commands/info.d.ts +6 -0
- package/dist/src/cli/commands/info.d.ts.map +1 -0
- package/dist/src/cli/commands/info.js +32 -0
- package/dist/src/cli/commands/info.js.map +1 -0
- package/dist/src/cli/commands/mcp.d.ts +6 -0
- package/dist/src/cli/commands/mcp.d.ts.map +1 -0
- package/dist/src/cli/commands/mcp.js +45 -0
- package/dist/src/cli/commands/mcp.js.map +1 -0
- package/dist/src/cli/commands/setup.d.ts +6 -0
- package/dist/src/cli/commands/setup.d.ts.map +1 -0
- package/dist/src/cli/commands/setup.js +59 -0
- package/dist/src/cli/commands/setup.js.map +1 -0
- package/dist/src/cli/commands/test/index.d.ts +6 -0
- package/dist/src/cli/commands/test/index.d.ts.map +1 -0
- package/dist/src/cli/commands/test/index.js +15 -0
- package/dist/src/cli/commands/test/index.js.map +1 -0
- package/dist/src/cli/commands/test/init.d.ts +6 -0
- package/dist/src/cli/commands/test/init.d.ts.map +1 -0
- package/dist/src/cli/commands/test/init.js +64 -0
- package/dist/src/cli/commands/test/init.js.map +1 -0
- package/dist/src/cli/commands/test/list.d.ts +6 -0
- package/dist/src/cli/commands/test/list.d.ts.map +1 -0
- package/dist/src/cli/commands/test/list.js +70 -0
- package/dist/src/cli/commands/test/list.js.map +1 -0
- package/dist/src/cli/commands/test/run.d.ts +6 -0
- package/dist/src/cli/commands/test/run.d.ts.map +1 -0
- package/dist/src/cli/commands/test/run.js +95 -0
- package/dist/src/cli/commands/test/run.js.map +1 -0
- package/dist/src/cli/commands/test/validate.d.ts +6 -0
- package/dist/src/cli/commands/test/validate.d.ts.map +1 -0
- package/dist/src/cli/commands/test/validate.js +70 -0
- package/dist/src/cli/commands/test/validate.js.map +1 -0
- package/dist/src/cli/index.d.ts +6 -0
- package/dist/src/cli/index.d.ts.map +1 -0
- package/dist/src/cli/index.js +21 -0
- package/dist/src/cli/index.js.map +1 -0
- package/dist/src/cli/lib/config.d.ts +36 -0
- package/dist/src/cli/lib/config.d.ts.map +1 -0
- package/dist/src/cli/lib/config.js +89 -0
- package/dist/src/cli/lib/config.js.map +1 -0
- package/dist/src/cli/lib/loader.d.ts +49 -0
- package/dist/src/cli/lib/loader.d.ts.map +1 -0
- package/dist/src/cli/lib/loader.js +122 -0
- package/dist/src/cli/lib/loader.js.map +1 -0
- package/dist/src/cli/lib/output.d.ts +53 -0
- package/dist/src/cli/lib/output.d.ts.map +1 -0
- package/dist/src/cli/lib/output.js +133 -0
- package/dist/src/cli/lib/output.js.map +1 -0
- package/dist/src/cli/lib/runner.d.ts +23 -0
- package/dist/src/cli/lib/runner.d.ts.map +1 -0
- package/dist/src/cli/lib/runner.js +40 -0
- package/dist/src/cli/lib/runner.js.map +1 -0
- package/dist/src/http-server.d.ts +14 -0
- package/dist/src/http-server.d.ts.map +1 -0
- package/dist/src/http-server.js +145 -0
- package/dist/src/http-server.js.map +1 -0
- package/dist/src/index.d.ts +9 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +21 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/server.d.ts +58 -0
- package/dist/src/server.d.ts.map +1 -0
- package/dist/src/server.js +2376 -0
- package/dist/src/server.js.map +1 -0
- package/dist/src/tunnel-mode.d.ts +13 -0
- package/dist/src/tunnel-mode.d.ts.map +1 -0
- package/dist/src/tunnel-mode.js +159 -0
- package/dist/src/tunnel-mode.js.map +1 -0
- package/dist/src/types/test-definition.d.ts +320 -0
- package/dist/src/types/test-definition.d.ts.map +1 -0
- package/dist/src/types/test-definition.js +11 -0
- package/dist/src/types/test-definition.js.map +1 -0
- package/dist/src/types.d.ts +209 -0
- package/dist/src/types.d.ts.map +1 -0
- package/dist/src/types.js +34 -0
- package/dist/src/types.js.map +1 -0
- package/dist/src/utils/package.d.ts +12 -0
- package/dist/src/utils/package.d.ts.map +1 -0
- package/dist/src/utils/package.js +36 -0
- package/dist/src/utils/package.js.map +1 -0
- package/dist/src/utils/summary.d.ts +45 -0
- package/dist/src/utils/summary.d.ts.map +1 -0
- package/dist/src/utils/summary.js +198 -0
- package/dist/src/utils/summary.js.map +1 -0
- package/lib/api/index.ts +977 -0
- package/lib/api/sse.ts +112 -0
- package/lib/browser/index.ts +181 -0
- package/lib/env/index.ts +156 -0
- package/lib/tunnel/index.test.ts +344 -0
- package/lib/tunnel/index.ts +197 -0
- package/lib/tunnel/integration.test.ts +98 -0
- package/package.json +100 -0
- package/server.json +16 -0
package/lib/api/index.ts
ADDED
|
@@ -0,0 +1,977 @@
|
|
|
1
|
+
import axios from 'axios';
|
|
2
|
+
import type { AxiosInstance, AxiosResponse } from 'axios';
|
|
3
|
+
import 'dotenv/config';
|
|
4
|
+
import { getEnv } from '../env/index.js';
|
|
5
|
+
import type {
|
|
6
|
+
TestAgentV2Session,
|
|
7
|
+
IssueType,
|
|
8
|
+
Severity,
|
|
9
|
+
IssueReport,
|
|
10
|
+
TestCreatorDoneIntent,
|
|
11
|
+
} from '../../src/types.js';
|
|
12
|
+
import {
|
|
13
|
+
generateEnhancedTestSummary,
|
|
14
|
+
formatEnhancedTestReport,
|
|
15
|
+
generateIssueStatistics,
|
|
16
|
+
categorizeIssues,
|
|
17
|
+
} from '../../src/utils/summary.js';
|
|
18
|
+
import type { EnhancedTestSummary, BlockSummary } from '../../src/utils/summary.js';
|
|
19
|
+
import { streamSSE, type SSEEvent } from './sse.js';
|
|
20
|
+
import type { TestDefinition } from '../../src/types/test-definition.js';
|
|
21
|
+
|
|
22
|
+
// Re-export new types for external consumers
|
|
23
|
+
export type { IssueType, Severity, IssueReport, TestCreatorDoneIntent };
|
|
24
|
+
export type { EnhancedTestSummary, BlockSummary };
|
|
25
|
+
|
|
26
|
+
// Re-export utility functions for external consumers
|
|
27
|
+
export {
|
|
28
|
+
generateEnhancedTestSummary,
|
|
29
|
+
formatEnhancedTestReport,
|
|
30
|
+
generateIssueStatistics,
|
|
31
|
+
categorizeIssues,
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
export interface CreateSessionOptions {
|
|
35
|
+
url?: string;
|
|
36
|
+
task: string;
|
|
37
|
+
wsUrl?: string;
|
|
38
|
+
dependencyId?: string;
|
|
39
|
+
devMode?: boolean;
|
|
40
|
+
region?: string;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export interface CreateSessionResponse {
|
|
44
|
+
sessionId: string;
|
|
45
|
+
appConfigId: string;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export interface SendMessageOptions {
|
|
49
|
+
sessionId: string;
|
|
50
|
+
action: 'pause' | 'response' | 'close';
|
|
51
|
+
data?: any;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export interface ListOptions {
|
|
55
|
+
limit?: number;
|
|
56
|
+
offset?: number;
|
|
57
|
+
query?: string;
|
|
58
|
+
self_only?: boolean;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export interface RunTestsOptions {
|
|
62
|
+
test_ids: string[];
|
|
63
|
+
ws_url?: string;
|
|
64
|
+
app_config_id?: string;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export interface RunTestsResponse {
|
|
68
|
+
success: boolean;
|
|
69
|
+
message?: string;
|
|
70
|
+
test_run_id?: string;
|
|
71
|
+
sessions?: any[];
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export type APIKeyScope = 'admin' | 'recordings' | 'webhooks' | 'api' | 'mcp';
|
|
75
|
+
|
|
76
|
+
export type APIKeySource = 'app' | 'api' | 'vibe-qa' | 'mcp' | 'other';
|
|
77
|
+
|
|
78
|
+
export interface APIKey {
|
|
79
|
+
id: string;
|
|
80
|
+
|
|
81
|
+
name: string;
|
|
82
|
+
key: string;
|
|
83
|
+
|
|
84
|
+
expiration_date?: string | null; // ISO datetime string
|
|
85
|
+
|
|
86
|
+
scope: APIKeyScope; // default "admin"
|
|
87
|
+
|
|
88
|
+
source?: APIKeySource | null; // optional
|
|
89
|
+
|
|
90
|
+
last_used_at?: string | null; // ISO datetime string
|
|
91
|
+
last_used_by?: string | null;
|
|
92
|
+
last_used_ip?: string | null;
|
|
93
|
+
|
|
94
|
+
app_config_id?: string | null;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export interface RegisterResponse {
|
|
98
|
+
success: boolean;
|
|
99
|
+
message?: string;
|
|
100
|
+
apiKey?: string;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export interface AutomatedTest {
|
|
104
|
+
id: string;
|
|
105
|
+
name: string;
|
|
106
|
+
description?: string;
|
|
107
|
+
url?: string;
|
|
108
|
+
task?: string;
|
|
109
|
+
status?: string;
|
|
110
|
+
created_at: string;
|
|
111
|
+
updated_at?: string;
|
|
112
|
+
organization_id?: string;
|
|
113
|
+
app_config_id?: string;
|
|
114
|
+
dependency_test_ids?: string[];
|
|
115
|
+
metadata?: any;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export interface TestRun {
|
|
119
|
+
id: string;
|
|
120
|
+
rerun_id?: string;
|
|
121
|
+
name: string;
|
|
122
|
+
external_id?: string;
|
|
123
|
+
matrix_option_id?: string;
|
|
124
|
+
test_id: string;
|
|
125
|
+
test_version_hash?: string;
|
|
126
|
+
test_suite_id?: string;
|
|
127
|
+
test_suite_run_id?: string;
|
|
128
|
+
used_variables?: any[];
|
|
129
|
+
run_status: 'pending' | 'running' | 'passed' | 'failed' | 'skipped' | 'cancelled' | 'timeout';
|
|
130
|
+
final_run_status?:
|
|
131
|
+
| 'pending'
|
|
132
|
+
| 'running'
|
|
133
|
+
| 'passed'
|
|
134
|
+
| 'failed'
|
|
135
|
+
| 'skipped'
|
|
136
|
+
| 'cancelled'
|
|
137
|
+
| 'timeout';
|
|
138
|
+
final_comment_id?: string;
|
|
139
|
+
allow_fix?: boolean;
|
|
140
|
+
result?: string;
|
|
141
|
+
error_message?: string;
|
|
142
|
+
recording_path?: string;
|
|
143
|
+
har_path?: string;
|
|
144
|
+
live_view_url?: string;
|
|
145
|
+
start_time?: string;
|
|
146
|
+
end_time?: string;
|
|
147
|
+
duration_seconds?: number;
|
|
148
|
+
pfs_score?: number;
|
|
149
|
+
pfs_bad_state_prob?: number;
|
|
150
|
+
pfs_confidence_lower?: number;
|
|
151
|
+
pfs_confidence_upper?: number;
|
|
152
|
+
pfs_num_observations?: number;
|
|
153
|
+
created_at: string;
|
|
154
|
+
updated_at?: string;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
export interface ListTestRunsOptions {
|
|
158
|
+
test_id?: string;
|
|
159
|
+
run_id?: string;
|
|
160
|
+
limit?: number;
|
|
161
|
+
offset?: number;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
export type ViewportConfigType = 'big_desktop' | 'desktop' | 'mobile' | 'tablet';
|
|
165
|
+
export type AppConfigType = 'production' | 'staging' | 'development' | 'local';
|
|
166
|
+
|
|
167
|
+
export interface UpdateAppConfigSchema {
|
|
168
|
+
base_url?: string;
|
|
169
|
+
login_url?: string;
|
|
170
|
+
login_username?: string;
|
|
171
|
+
login_password?: string;
|
|
172
|
+
vp_type?: ViewportConfigType;
|
|
173
|
+
cfg_type?: AppConfigType;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
export interface UpdateAppConfigResponse {
|
|
177
|
+
success: boolean;
|
|
178
|
+
message?: string;
|
|
179
|
+
data?: any;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
export interface AppConfig {
|
|
183
|
+
id: string;
|
|
184
|
+
name: string;
|
|
185
|
+
base_url: string;
|
|
186
|
+
login_url: string;
|
|
187
|
+
login_username: string;
|
|
188
|
+
login_password: string;
|
|
189
|
+
login_instructions: string;
|
|
190
|
+
login_steps: any[];
|
|
191
|
+
organization_id: string;
|
|
192
|
+
status: string;
|
|
193
|
+
vp_type: ViewportConfigType;
|
|
194
|
+
cfg_type?: AppConfigType | null;
|
|
195
|
+
width?: number;
|
|
196
|
+
height?: number;
|
|
197
|
+
remove_popups: boolean;
|
|
198
|
+
failure_status: string;
|
|
199
|
+
created_at: string;
|
|
200
|
+
updated_at?: string;
|
|
201
|
+
created_by: string;
|
|
202
|
+
updated_by?: string;
|
|
203
|
+
deleted_at?: string;
|
|
204
|
+
deleted_by?: string;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
export interface AuthResponse {
|
|
208
|
+
success: boolean;
|
|
209
|
+
message?: string;
|
|
210
|
+
apiKey?: string;
|
|
211
|
+
data?: {
|
|
212
|
+
api_key?: APIKey;
|
|
213
|
+
app_config?: AppConfig;
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
export interface ListAppConfigsOptions {
|
|
218
|
+
limit?: number;
|
|
219
|
+
offset?: number;
|
|
220
|
+
query?: string;
|
|
221
|
+
source?: APIKeySource;
|
|
222
|
+
cfg_type?: AppConfigType;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// ==========================================
|
|
226
|
+
// CLI Test Definition Types
|
|
227
|
+
// ==========================================
|
|
228
|
+
|
|
229
|
+
export interface RunCliTestOptions {
|
|
230
|
+
test_definitions?: TestDefinition[];
|
|
231
|
+
test_id?: string;
|
|
232
|
+
persist?: boolean;
|
|
233
|
+
headless?: boolean;
|
|
234
|
+
allow_fix?: boolean;
|
|
235
|
+
capture_screenshots?: boolean;
|
|
236
|
+
store_recording?: boolean;
|
|
237
|
+
store_har?: boolean;
|
|
238
|
+
ws_url?: string;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
export type SSECallback = (event: SSEEvent) => void;
|
|
242
|
+
|
|
243
|
+
export interface StepResult {
|
|
244
|
+
step_index: number;
|
|
245
|
+
name: string;
|
|
246
|
+
status: 'passed' | 'failed';
|
|
247
|
+
duration: number;
|
|
248
|
+
screenshot_url?: string;
|
|
249
|
+
error?: string;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
export interface RunCliTestResult {
|
|
253
|
+
run_id: string;
|
|
254
|
+
test_id?: string;
|
|
255
|
+
status: 'passed' | 'failed' | 'error' | 'cancelled' | 'timeout';
|
|
256
|
+
duration_seconds: number;
|
|
257
|
+
steps: StepResult[];
|
|
258
|
+
assets?: {
|
|
259
|
+
recording_url?: string;
|
|
260
|
+
har_url?: string;
|
|
261
|
+
};
|
|
262
|
+
error?: string;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
export interface ValidationError {
|
|
266
|
+
path: string;
|
|
267
|
+
message: string;
|
|
268
|
+
severity: 'error' | 'warning' | 'info';
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
export interface ValidationResult {
|
|
272
|
+
valid: boolean;
|
|
273
|
+
errors: ValidationError[];
|
|
274
|
+
warnings: ValidationError[];
|
|
275
|
+
resolved?: {
|
|
276
|
+
app_config_id?: string;
|
|
277
|
+
total_steps?: number;
|
|
278
|
+
dependencies?: string[];
|
|
279
|
+
};
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
export interface ImportOptions {
|
|
283
|
+
upsert?: boolean;
|
|
284
|
+
dry_run?: boolean;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
export interface ImportedTest {
|
|
288
|
+
name: string;
|
|
289
|
+
id: string;
|
|
290
|
+
action: 'created' | 'updated' | 'skipped';
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
export interface ImportResult {
|
|
294
|
+
success: boolean;
|
|
295
|
+
imported: ImportedTest[];
|
|
296
|
+
errors: Array<{
|
|
297
|
+
name: string;
|
|
298
|
+
error: string;
|
|
299
|
+
}>;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
// Re-export TestDefinition for convenience
|
|
303
|
+
export type { TestDefinition };
|
|
304
|
+
|
|
305
|
+
export class ApiClient {
|
|
306
|
+
private readonly client: AxiosInstance;
|
|
307
|
+
private apiKey: string | null = null;
|
|
308
|
+
private appConfigId: string | null = null;
|
|
309
|
+
|
|
310
|
+
constructor(baseUrl?: string) {
|
|
311
|
+
// Use environment variable if available, otherwise use provided baseUrl, finally fall back to production
|
|
312
|
+
const apiUrl = getEnv('QA_USE_API_URL') || baseUrl || 'https://api.desplega.ai';
|
|
313
|
+
|
|
314
|
+
this.client = axios.create({
|
|
315
|
+
baseURL: apiUrl,
|
|
316
|
+
timeout: 30000,
|
|
317
|
+
headers: {
|
|
318
|
+
'Content-Type': 'application/json',
|
|
319
|
+
},
|
|
320
|
+
});
|
|
321
|
+
|
|
322
|
+
// Auto-load API key from environment or config file if available
|
|
323
|
+
const envApiKey = getEnv('QA_USE_API_KEY');
|
|
324
|
+
if (envApiKey) {
|
|
325
|
+
this.setApiKey(envApiKey);
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
setApiKey(apiKey: string): void {
|
|
330
|
+
this.apiKey = apiKey;
|
|
331
|
+
this.client.defaults.headers['Authorization'] = `Bearer ${apiKey}`;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
getApiKey(): string | null {
|
|
335
|
+
return this.apiKey;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
setAppConfigId(appConfigId: string): void {
|
|
339
|
+
this.appConfigId = appConfigId;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
getAppConfigId(): string | null {
|
|
343
|
+
return this.appConfigId;
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
static getAppUrl(): string {
|
|
347
|
+
return getEnv('QA_USE_APP_URL') || 'https://app.desplega.ai';
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
getApiUrl(): string {
|
|
351
|
+
return this.client.defaults.baseURL || 'https://api.desplega.ai';
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
async validateApiKey(apiKey?: string): Promise<AuthResponse> {
|
|
355
|
+
const keyToValidate = apiKey || this.apiKey;
|
|
356
|
+
if (!keyToValidate) {
|
|
357
|
+
return {
|
|
358
|
+
success: false,
|
|
359
|
+
message: 'No API key provided',
|
|
360
|
+
};
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
try {
|
|
364
|
+
const response: AxiosResponse = await this.client.get('/vibe-qa/check', {
|
|
365
|
+
headers: {
|
|
366
|
+
Authorization: `Bearer ${keyToValidate}`,
|
|
367
|
+
},
|
|
368
|
+
});
|
|
369
|
+
|
|
370
|
+
// Store app_config.id if available
|
|
371
|
+
if (response.data?.data?.app_config?.id) {
|
|
372
|
+
this.appConfigId = response.data.data.app_config.id;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
return {
|
|
376
|
+
success: true,
|
|
377
|
+
...response.data,
|
|
378
|
+
};
|
|
379
|
+
} catch (error) {
|
|
380
|
+
if (axios.isAxiosError(error)) {
|
|
381
|
+
const statusCode = error.response?.status;
|
|
382
|
+
const errorData = error.response?.data;
|
|
383
|
+
|
|
384
|
+
return {
|
|
385
|
+
success: false,
|
|
386
|
+
message: errorData?.message || `HTTP ${statusCode}: ${error.message}`,
|
|
387
|
+
};
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
return {
|
|
391
|
+
success: false,
|
|
392
|
+
message: error instanceof Error ? error.message : 'Unknown error',
|
|
393
|
+
};
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
async createSession(options: CreateSessionOptions): Promise<CreateSessionResponse> {
|
|
398
|
+
try {
|
|
399
|
+
// Determine region from options or environment variable or config file
|
|
400
|
+
const region = options.region || getEnv('QA_USE_REGION') || 'auto';
|
|
401
|
+
|
|
402
|
+
const sessionData = {
|
|
403
|
+
url: options.url,
|
|
404
|
+
task: options.task,
|
|
405
|
+
ws_url: options.wsUrl,
|
|
406
|
+
dep_id: options.dependencyId,
|
|
407
|
+
source: 'mcp',
|
|
408
|
+
autopilot: true,
|
|
409
|
+
use_storage_path: false,
|
|
410
|
+
persist: true,
|
|
411
|
+
region: region,
|
|
412
|
+
};
|
|
413
|
+
|
|
414
|
+
if (options.devMode) {
|
|
415
|
+
sessionData.autopilot = false;
|
|
416
|
+
sessionData.use_storage_path = true;
|
|
417
|
+
sessionData.persist = false;
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
const response: AxiosResponse = await this.client.post('/vibe-qa/sessions', sessionData);
|
|
421
|
+
|
|
422
|
+
return {
|
|
423
|
+
sessionId: response.data.data.agent_id,
|
|
424
|
+
appConfigId: response.data.data.app_config_id,
|
|
425
|
+
} as CreateSessionResponse;
|
|
426
|
+
} catch (error) {
|
|
427
|
+
if (axios.isAxiosError(error)) {
|
|
428
|
+
const statusCode = error.response?.status;
|
|
429
|
+
const errorData = error.response?.data;
|
|
430
|
+
throw new Error(
|
|
431
|
+
errorData?.message || errorData?.detail || `HTTP ${statusCode}: Failed to create session`
|
|
432
|
+
);
|
|
433
|
+
}
|
|
434
|
+
throw new Error(error instanceof Error ? error.message : 'Unknown error creating session');
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
async listSessions(options: ListOptions = {}): Promise<TestAgentV2Session[]> {
|
|
439
|
+
try {
|
|
440
|
+
const params = new URLSearchParams();
|
|
441
|
+
|
|
442
|
+
if (options.limit !== undefined) params.append('limit', options.limit.toString());
|
|
443
|
+
if (options.offset !== undefined) params.append('offset', options.offset.toString());
|
|
444
|
+
if (options.query) params.append('query', options.query);
|
|
445
|
+
|
|
446
|
+
params.append('self_only', 'true');
|
|
447
|
+
|
|
448
|
+
const response: AxiosResponse = await this.client.get(
|
|
449
|
+
`/vibe-qa/sessions?${params.toString()}`
|
|
450
|
+
);
|
|
451
|
+
return response.data as TestAgentV2Session[];
|
|
452
|
+
} catch (error) {
|
|
453
|
+
if (axios.isAxiosError(error)) {
|
|
454
|
+
const statusCode = error.response?.status;
|
|
455
|
+
const errorData = error.response?.data;
|
|
456
|
+
throw new Error(
|
|
457
|
+
errorData?.message || errorData?.detail || `HTTP ${statusCode}: Failed to fetch sessions`
|
|
458
|
+
);
|
|
459
|
+
}
|
|
460
|
+
throw new Error(error instanceof Error ? error.message : 'Unknown error fetching sessions');
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
async getSession(sessionId: string, selfOnly: boolean = true): Promise<TestAgentV2Session> {
|
|
465
|
+
try {
|
|
466
|
+
const params = new URLSearchParams();
|
|
467
|
+
|
|
468
|
+
if (selfOnly) {
|
|
469
|
+
params.append('self_only', 'true');
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
const response: AxiosResponse = await this.client.get(
|
|
473
|
+
`/vibe-qa/sessions/${sessionId}?${params.toString()}`
|
|
474
|
+
);
|
|
475
|
+
|
|
476
|
+
return response.data as TestAgentV2Session;
|
|
477
|
+
} catch (error) {
|
|
478
|
+
if (axios.isAxiosError(error)) {
|
|
479
|
+
const statusCode = error.response?.status;
|
|
480
|
+
const errorData = error.response?.data;
|
|
481
|
+
|
|
482
|
+
if (statusCode === 404) {
|
|
483
|
+
throw new Error(`Session not found: ${sessionId}`);
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
throw new Error(
|
|
487
|
+
errorData?.message || errorData?.detail || `HTTP ${statusCode}: Failed to fetch session`
|
|
488
|
+
);
|
|
489
|
+
}
|
|
490
|
+
throw new Error(error instanceof Error ? error.message : 'Unknown error fetching session');
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
async sendMessage(options: SendMessageOptions): Promise<any> {
|
|
495
|
+
try {
|
|
496
|
+
const eventData = {
|
|
497
|
+
group_key: options.sessionId,
|
|
498
|
+
event_type: 'test_agent_2.event.incoming',
|
|
499
|
+
event_data: {
|
|
500
|
+
type: options.action,
|
|
501
|
+
message: options.data || '',
|
|
502
|
+
},
|
|
503
|
+
};
|
|
504
|
+
|
|
505
|
+
const response: AxiosResponse = await this.client.post('/vibe-qa/new-event', eventData);
|
|
506
|
+
return response.data;
|
|
507
|
+
} catch (error) {
|
|
508
|
+
if (axios.isAxiosError(error)) {
|
|
509
|
+
const statusCode = error.response?.status;
|
|
510
|
+
const errorData = error.response?.data;
|
|
511
|
+
throw new Error(
|
|
512
|
+
errorData?.message ||
|
|
513
|
+
errorData?.detail ||
|
|
514
|
+
`HTTP ${statusCode}: Failed to send message to session`
|
|
515
|
+
);
|
|
516
|
+
}
|
|
517
|
+
throw new Error(
|
|
518
|
+
error instanceof Error ? error.message : 'Unknown error sending message to session'
|
|
519
|
+
);
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
async register(email: string): Promise<RegisterResponse> {
|
|
524
|
+
try {
|
|
525
|
+
const response: AxiosResponse = await this.client.post('/vibe-qa/register', {
|
|
526
|
+
email,
|
|
527
|
+
cfg_type: 'local',
|
|
528
|
+
source: 'mcp',
|
|
529
|
+
});
|
|
530
|
+
|
|
531
|
+
return {
|
|
532
|
+
success: true,
|
|
533
|
+
message: response.data.message || 'Registration successful',
|
|
534
|
+
apiKey: response.data.apiKey,
|
|
535
|
+
...response.data,
|
|
536
|
+
};
|
|
537
|
+
} catch (error) {
|
|
538
|
+
if (axios.isAxiosError(error)) {
|
|
539
|
+
const statusCode = error.response?.status;
|
|
540
|
+
const errorData = error.response?.data;
|
|
541
|
+
|
|
542
|
+
return {
|
|
543
|
+
success: false,
|
|
544
|
+
message:
|
|
545
|
+
errorData?.message || errorData?.detail || `HTTP ${statusCode}: Registration failed`,
|
|
546
|
+
};
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
return {
|
|
550
|
+
success: false,
|
|
551
|
+
message: error instanceof Error ? error.message : 'Unknown error during registration',
|
|
552
|
+
};
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
async listTests(options: ListOptions = {}): Promise<AutomatedTest[]> {
|
|
557
|
+
try {
|
|
558
|
+
const params = new URLSearchParams();
|
|
559
|
+
if (options.limit !== undefined) params.append('limit', options.limit.toString());
|
|
560
|
+
if (options.offset !== undefined) params.append('offset', options.offset.toString());
|
|
561
|
+
if (options.query) params.append('query', options.query);
|
|
562
|
+
|
|
563
|
+
if (options.self_only === undefined || options.self_only === true) {
|
|
564
|
+
params.append('self_only', 'true');
|
|
565
|
+
} else {
|
|
566
|
+
params.append('self_only', 'false');
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
const response: AxiosResponse = await this.client.get(`/vibe-qa/tests?${params.toString()}`);
|
|
570
|
+
return response.data.items as AutomatedTest[];
|
|
571
|
+
} catch (error) {
|
|
572
|
+
if (axios.isAxiosError(error)) {
|
|
573
|
+
const statusCode = error.response?.status;
|
|
574
|
+
const errorData = error.response?.data;
|
|
575
|
+
throw new Error(
|
|
576
|
+
errorData?.message || errorData?.detail || `HTTP ${statusCode}: Failed to fetch tests`
|
|
577
|
+
);
|
|
578
|
+
}
|
|
579
|
+
throw new Error(error instanceof Error ? error.message : 'Unknown error fetching tests');
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
async getTest(testId: string, selfOnly: boolean = true): Promise<AutomatedTest> {
|
|
584
|
+
try {
|
|
585
|
+
const params = new URLSearchParams();
|
|
586
|
+
|
|
587
|
+
if (selfOnly) {
|
|
588
|
+
params.append('self_only', 'true');
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
const response: AxiosResponse = await this.client.get(
|
|
592
|
+
`/vibe-qa/tests/${testId}?${params.toString()}`
|
|
593
|
+
);
|
|
594
|
+
|
|
595
|
+
return response.data as AutomatedTest;
|
|
596
|
+
} catch (error) {
|
|
597
|
+
if (axios.isAxiosError(error)) {
|
|
598
|
+
const statusCode = error.response?.status;
|
|
599
|
+
const errorData = error.response?.data;
|
|
600
|
+
|
|
601
|
+
if (statusCode === 404) {
|
|
602
|
+
throw new Error(`Test not found: ${testId}`);
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
throw new Error(
|
|
606
|
+
errorData?.message || errorData?.detail || `HTTP ${statusCode}: Failed to fetch test`
|
|
607
|
+
);
|
|
608
|
+
}
|
|
609
|
+
throw new Error(error instanceof Error ? error.message : 'Unknown error fetching test');
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
async runTests(options: RunTestsOptions): Promise<RunTestsResponse> {
|
|
614
|
+
try {
|
|
615
|
+
const requestData = {
|
|
616
|
+
test_ids: options.test_ids,
|
|
617
|
+
ws_url: options.ws_url,
|
|
618
|
+
app_config_id: options.app_config_id,
|
|
619
|
+
};
|
|
620
|
+
|
|
621
|
+
const response: AxiosResponse = await this.client.post('/vibe-qa/run-tests', requestData);
|
|
622
|
+
|
|
623
|
+
return {
|
|
624
|
+
success: true,
|
|
625
|
+
message: response.data.message || 'Tests started successfully',
|
|
626
|
+
test_run_id: response.data.test_run_id,
|
|
627
|
+
sessions: response.data.sessions,
|
|
628
|
+
...response.data,
|
|
629
|
+
};
|
|
630
|
+
} catch (error) {
|
|
631
|
+
if (axios.isAxiosError(error)) {
|
|
632
|
+
const statusCode = error.response?.status;
|
|
633
|
+
const errorData = error.response?.data;
|
|
634
|
+
|
|
635
|
+
return {
|
|
636
|
+
success: false,
|
|
637
|
+
message:
|
|
638
|
+
errorData?.message || errorData?.detail || `HTTP ${statusCode}: Failed to run tests`,
|
|
639
|
+
};
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
return {
|
|
643
|
+
success: false,
|
|
644
|
+
message: error instanceof Error ? error.message : 'Unknown error running tests',
|
|
645
|
+
};
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
async listTestRuns(options: ListTestRunsOptions = {}): Promise<TestRun[]> {
|
|
650
|
+
try {
|
|
651
|
+
const params = new URLSearchParams();
|
|
652
|
+
if (options.test_id) params.append('test_id', options.test_id);
|
|
653
|
+
if (options.run_id) params.append('run_id', options.run_id);
|
|
654
|
+
if (options.limit !== undefined) params.append('limit', options.limit.toString());
|
|
655
|
+
if (options.offset !== undefined) params.append('offset', options.offset.toString());
|
|
656
|
+
|
|
657
|
+
const response: AxiosResponse = await this.client.get(
|
|
658
|
+
`/vibe-qa/tests-runs?${params.toString()}`
|
|
659
|
+
);
|
|
660
|
+
return response.data as TestRun[];
|
|
661
|
+
} catch (error) {
|
|
662
|
+
if (axios.isAxiosError(error)) {
|
|
663
|
+
const statusCode = error.response?.status;
|
|
664
|
+
const errorData = error.response?.data;
|
|
665
|
+
throw new Error(
|
|
666
|
+
errorData?.message || errorData?.detail || `HTTP ${statusCode}: Failed to fetch test runs`
|
|
667
|
+
);
|
|
668
|
+
}
|
|
669
|
+
throw new Error(error instanceof Error ? error.message : 'Unknown error fetching test runs');
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
async updateAppConfig(config: UpdateAppConfigSchema): Promise<UpdateAppConfigResponse> {
|
|
674
|
+
try {
|
|
675
|
+
if (!this.apiKey) {
|
|
676
|
+
return {
|
|
677
|
+
success: false,
|
|
678
|
+
message: 'API key not configured. Please set an API key first.',
|
|
679
|
+
};
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
// Apply default for vp_type if not specified
|
|
683
|
+
const configWithDefaults = {
|
|
684
|
+
...config,
|
|
685
|
+
vp_type: config.vp_type || 'desktop',
|
|
686
|
+
cfg_type: config.cfg_type || 'local',
|
|
687
|
+
};
|
|
688
|
+
|
|
689
|
+
const response: AxiosResponse = await this.client.post(
|
|
690
|
+
'/vibe-qa/app-configs',
|
|
691
|
+
configWithDefaults
|
|
692
|
+
);
|
|
693
|
+
|
|
694
|
+
return {
|
|
695
|
+
success: true,
|
|
696
|
+
message: response.data.message || 'App config updated successfully',
|
|
697
|
+
data: response.data,
|
|
698
|
+
};
|
|
699
|
+
} catch (error) {
|
|
700
|
+
if (axios.isAxiosError(error)) {
|
|
701
|
+
const statusCode = error.response?.status;
|
|
702
|
+
const errorData = error.response?.data;
|
|
703
|
+
|
|
704
|
+
return {
|
|
705
|
+
success: false,
|
|
706
|
+
message:
|
|
707
|
+
errorData?.message ||
|
|
708
|
+
errorData?.detail ||
|
|
709
|
+
`HTTP ${statusCode}: Failed to update app config`,
|
|
710
|
+
};
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
return {
|
|
714
|
+
success: false,
|
|
715
|
+
message: error instanceof Error ? error.message : 'Unknown error updating app config',
|
|
716
|
+
};
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
|
|
720
|
+
async listAppConfigs(options: ListAppConfigsOptions = {}): Promise<AppConfig[]> {
|
|
721
|
+
try {
|
|
722
|
+
if (!this.apiKey) {
|
|
723
|
+
throw new Error('API key not configured. Please set an API key first.');
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
const params = new URLSearchParams();
|
|
727
|
+
if (options.limit !== undefined) params.append('limit', options.limit.toString());
|
|
728
|
+
if (options.offset !== undefined) params.append('offset', options.offset.toString());
|
|
729
|
+
if (options.query) params.append('query', options.query);
|
|
730
|
+
if (options.source) params.append('source', options.source);
|
|
731
|
+
if (options.cfg_type) params.append('cfg_type', options.cfg_type);
|
|
732
|
+
|
|
733
|
+
const response: AxiosResponse = await this.client.get(
|
|
734
|
+
`/vibe-qa/app-configs?${params.toString()}`
|
|
735
|
+
);
|
|
736
|
+
|
|
737
|
+
return response.data as AppConfig[];
|
|
738
|
+
} catch (error) {
|
|
739
|
+
if (axios.isAxiosError(error)) {
|
|
740
|
+
const statusCode = error.response?.status;
|
|
741
|
+
const errorData = error.response?.data;
|
|
742
|
+
throw new Error(
|
|
743
|
+
errorData?.message ||
|
|
744
|
+
errorData?.detail ||
|
|
745
|
+
`HTTP ${statusCode}: Failed to fetch app configs`
|
|
746
|
+
);
|
|
747
|
+
}
|
|
748
|
+
throw new Error(
|
|
749
|
+
error instanceof Error ? error.message : 'Unknown error fetching app configs'
|
|
750
|
+
);
|
|
751
|
+
}
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
async setWsUrl(wsUrl: string): Promise<{ success: boolean; message?: string; data?: any }> {
|
|
755
|
+
try {
|
|
756
|
+
if (!this.apiKey) {
|
|
757
|
+
return {
|
|
758
|
+
success: false,
|
|
759
|
+
message: 'API key not configured. Please set an API key first.',
|
|
760
|
+
};
|
|
761
|
+
}
|
|
762
|
+
|
|
763
|
+
const response: AxiosResponse = await this.client.post('/vibe-qa/ws-url', {
|
|
764
|
+
ws_url: wsUrl,
|
|
765
|
+
});
|
|
766
|
+
|
|
767
|
+
return {
|
|
768
|
+
success: true,
|
|
769
|
+
message: response.data.message || 'WebSocket URL set successfully',
|
|
770
|
+
data: response.data.data,
|
|
771
|
+
};
|
|
772
|
+
} catch (error) {
|
|
773
|
+
if (axios.isAxiosError(error)) {
|
|
774
|
+
const statusCode = error.response?.status;
|
|
775
|
+
const errorData = error.response?.data;
|
|
776
|
+
|
|
777
|
+
return {
|
|
778
|
+
success: false,
|
|
779
|
+
message:
|
|
780
|
+
errorData?.message ||
|
|
781
|
+
errorData?.detail ||
|
|
782
|
+
`HTTP ${statusCode}: Failed to set WebSocket URL`,
|
|
783
|
+
};
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
return {
|
|
787
|
+
success: false,
|
|
788
|
+
message: error instanceof Error ? error.message : 'Unknown error setting WebSocket URL',
|
|
789
|
+
};
|
|
790
|
+
}
|
|
791
|
+
}
|
|
792
|
+
|
|
793
|
+
// ==========================================
|
|
794
|
+
// CLI Test Definition Methods
|
|
795
|
+
// ==========================================
|
|
796
|
+
|
|
797
|
+
/**
|
|
798
|
+
* Run test definitions with SSE streaming progress
|
|
799
|
+
* @param options - Test execution options
|
|
800
|
+
* @param onEvent - Optional callback for SSE events
|
|
801
|
+
* @returns Promise resolving to test result
|
|
802
|
+
*/
|
|
803
|
+
async runCliTest(options: RunCliTestOptions, onEvent?: SSECallback): Promise<RunCliTestResult> {
|
|
804
|
+
try {
|
|
805
|
+
const response = await fetch(`${this.getApiUrl()}/vibe-qa/cli/run`, {
|
|
806
|
+
method: 'POST',
|
|
807
|
+
headers: {
|
|
808
|
+
Authorization: `Bearer ${this.apiKey}`,
|
|
809
|
+
'Content-Type': 'application/json',
|
|
810
|
+
Accept: 'text/event-stream',
|
|
811
|
+
},
|
|
812
|
+
body: JSON.stringify(options),
|
|
813
|
+
});
|
|
814
|
+
|
|
815
|
+
if (!response.ok) {
|
|
816
|
+
const errorData = (await response.json().catch(() => ({}))) as {
|
|
817
|
+
message?: string;
|
|
818
|
+
detail?: string;
|
|
819
|
+
};
|
|
820
|
+
throw new Error(
|
|
821
|
+
errorData?.message || errorData?.detail || `HTTP ${response.status}: Failed to run test`
|
|
822
|
+
);
|
|
823
|
+
}
|
|
824
|
+
|
|
825
|
+
let result: RunCliTestResult | null = null;
|
|
826
|
+
|
|
827
|
+
// Stream SSE events
|
|
828
|
+
for await (const event of streamSSE(response)) {
|
|
829
|
+
if (onEvent) onEvent(event);
|
|
830
|
+
|
|
831
|
+
// Capture final result
|
|
832
|
+
if (event.event === 'complete' || event.event === 'error') {
|
|
833
|
+
result = event.data;
|
|
834
|
+
}
|
|
835
|
+
}
|
|
836
|
+
|
|
837
|
+
if (!result) {
|
|
838
|
+
throw new Error('No result received from test execution');
|
|
839
|
+
}
|
|
840
|
+
|
|
841
|
+
return result;
|
|
842
|
+
} catch (error) {
|
|
843
|
+
if (error instanceof Error) {
|
|
844
|
+
throw error;
|
|
845
|
+
}
|
|
846
|
+
throw new Error('Unknown error running test');
|
|
847
|
+
}
|
|
848
|
+
}
|
|
849
|
+
|
|
850
|
+
/**
|
|
851
|
+
* Export a database test to YAML or JSON format
|
|
852
|
+
* @param testId - UUID of the test to export
|
|
853
|
+
* @param format - Output format ('yaml' or 'json')
|
|
854
|
+
* @param includeDeps - Whether to include dependencies
|
|
855
|
+
* @returns Promise resolving to YAML/JSON string
|
|
856
|
+
*/
|
|
857
|
+
async exportTest(
|
|
858
|
+
testId: string,
|
|
859
|
+
format: 'yaml' | 'json' = 'yaml',
|
|
860
|
+
includeDeps: boolean = true
|
|
861
|
+
): Promise<string> {
|
|
862
|
+
try {
|
|
863
|
+
const params = new URLSearchParams();
|
|
864
|
+
params.append('format', format);
|
|
865
|
+
if (!includeDeps) params.append('no_deps', 'true');
|
|
866
|
+
|
|
867
|
+
const response: AxiosResponse = await this.client.get(
|
|
868
|
+
`/vibe-qa/cli/export/${testId}?${params.toString()}`
|
|
869
|
+
);
|
|
870
|
+
|
|
871
|
+
return response.data;
|
|
872
|
+
} catch (error) {
|
|
873
|
+
if (axios.isAxiosError(error)) {
|
|
874
|
+
const statusCode = error.response?.status;
|
|
875
|
+
const errorData = error.response?.data;
|
|
876
|
+
|
|
877
|
+
if (statusCode === 404) {
|
|
878
|
+
throw new Error(`Test not found: ${testId}`);
|
|
879
|
+
}
|
|
880
|
+
|
|
881
|
+
throw new Error(
|
|
882
|
+
errorData?.message || errorData?.detail || `HTTP ${statusCode}: Failed to export test`
|
|
883
|
+
);
|
|
884
|
+
}
|
|
885
|
+
throw new Error(error instanceof Error ? error.message : 'Unknown error exporting test');
|
|
886
|
+
}
|
|
887
|
+
}
|
|
888
|
+
|
|
889
|
+
/**
|
|
890
|
+
* Validate test definitions without running them
|
|
891
|
+
* @param definitions - Array of TestDefinitions to validate
|
|
892
|
+
* @returns Promise resolving to validation result
|
|
893
|
+
*/
|
|
894
|
+
async validateTestDefinition(definitions: TestDefinition[]): Promise<ValidationResult> {
|
|
895
|
+
try {
|
|
896
|
+
const response: AxiosResponse = await this.client.post('/vibe-qa/cli/validate', {
|
|
897
|
+
test_definitions: definitions,
|
|
898
|
+
});
|
|
899
|
+
|
|
900
|
+
return response.data as ValidationResult;
|
|
901
|
+
} catch (error) {
|
|
902
|
+
if (axios.isAxiosError(error)) {
|
|
903
|
+
const statusCode = error.response?.status;
|
|
904
|
+
const errorData = error.response?.data;
|
|
905
|
+
|
|
906
|
+
throw new Error(
|
|
907
|
+
errorData?.message ||
|
|
908
|
+
errorData?.detail ||
|
|
909
|
+
`HTTP ${statusCode}: Failed to validate test definition`
|
|
910
|
+
);
|
|
911
|
+
}
|
|
912
|
+
throw new Error(
|
|
913
|
+
error instanceof Error ? error.message : 'Unknown error validating test definition'
|
|
914
|
+
);
|
|
915
|
+
}
|
|
916
|
+
}
|
|
917
|
+
|
|
918
|
+
/**
|
|
919
|
+
* Import (create/update) tests from definitions
|
|
920
|
+
* @param definitions - Array of TestDefinitions to import
|
|
921
|
+
* @param options - Import options (upsert, dry_run)
|
|
922
|
+
* @returns Promise resolving to import result
|
|
923
|
+
*/
|
|
924
|
+
async importTestDefinition(
|
|
925
|
+
definitions: TestDefinition[],
|
|
926
|
+
options: ImportOptions = {}
|
|
927
|
+
): Promise<ImportResult> {
|
|
928
|
+
try {
|
|
929
|
+
const response: AxiosResponse = await this.client.post('/vibe-qa/cli/import', {
|
|
930
|
+
test_definitions: definitions,
|
|
931
|
+
upsert: options.upsert ?? true,
|
|
932
|
+
dry_run: options.dry_run ?? false,
|
|
933
|
+
});
|
|
934
|
+
|
|
935
|
+
return response.data as ImportResult;
|
|
936
|
+
} catch (error) {
|
|
937
|
+
if (axios.isAxiosError(error)) {
|
|
938
|
+
const statusCode = error.response?.status;
|
|
939
|
+
const errorData = error.response?.data;
|
|
940
|
+
|
|
941
|
+
throw new Error(
|
|
942
|
+
errorData?.message ||
|
|
943
|
+
errorData?.detail ||
|
|
944
|
+
`HTTP ${statusCode}: Failed to import test definition`
|
|
945
|
+
);
|
|
946
|
+
}
|
|
947
|
+
throw new Error(
|
|
948
|
+
error instanceof Error ? error.message : 'Unknown error importing test definition'
|
|
949
|
+
);
|
|
950
|
+
}
|
|
951
|
+
}
|
|
952
|
+
|
|
953
|
+
/**
|
|
954
|
+
* Get JSON Schema for TestDefinition format
|
|
955
|
+
* @returns Promise resolving to JSON Schema object
|
|
956
|
+
*/
|
|
957
|
+
async getTestDefinitionSchema(): Promise<any> {
|
|
958
|
+
try {
|
|
959
|
+
const response: AxiosResponse = await this.client.get('/vibe-qa/cli/schema');
|
|
960
|
+
return response.data;
|
|
961
|
+
} catch (error) {
|
|
962
|
+
if (axios.isAxiosError(error)) {
|
|
963
|
+
const statusCode = error.response?.status;
|
|
964
|
+
const errorData = error.response?.data;
|
|
965
|
+
|
|
966
|
+
throw new Error(
|
|
967
|
+
errorData?.message ||
|
|
968
|
+
errorData?.detail ||
|
|
969
|
+
`HTTP ${statusCode}: Failed to fetch test definition schema`
|
|
970
|
+
);
|
|
971
|
+
}
|
|
972
|
+
throw new Error(
|
|
973
|
+
error instanceof Error ? error.message : 'Unknown error fetching test definition schema'
|
|
974
|
+
);
|
|
975
|
+
}
|
|
976
|
+
}
|
|
977
|
+
}
|