@desplega.ai/qa-use 2.1.7 → 2.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/lib/api/browser-types.d.ts +175 -0
- package/dist/lib/api/browser-types.d.ts.map +1 -0
- package/dist/lib/api/browser-types.js +5 -0
- package/dist/lib/api/browser-types.js.map +1 -0
- package/dist/lib/api/browser.d.ts +66 -0
- package/dist/lib/api/browser.d.ts.map +1 -0
- package/dist/lib/api/browser.js +223 -0
- package/dist/lib/api/browser.js.map +1 -0
- package/dist/package.json +2 -1
- package/dist/src/cli/commands/browser/back.d.ts +6 -0
- package/dist/src/cli/commands/browser/back.d.ts.map +1 -0
- package/dist/src/cli/commands/browser/back.js +42 -0
- package/dist/src/cli/commands/browser/back.js.map +1 -0
- package/dist/src/cli/commands/browser/check.d.ts +6 -0
- package/dist/src/cli/commands/browser/check.d.ts.map +1 -0
- package/dist/src/cli/commands/browser/check.js +62 -0
- package/dist/src/cli/commands/browser/check.js.map +1 -0
- package/dist/src/cli/commands/browser/click.d.ts +6 -0
- package/dist/src/cli/commands/browser/click.d.ts.map +1 -0
- package/dist/src/cli/commands/browser/click.js +63 -0
- package/dist/src/cli/commands/browser/click.js.map +1 -0
- package/dist/src/cli/commands/browser/close.d.ts +6 -0
- package/dist/src/cli/commands/browser/close.d.ts.map +1 -0
- package/dist/src/cli/commands/browser/close.js +44 -0
- package/dist/src/cli/commands/browser/close.js.map +1 -0
- package/dist/src/cli/commands/browser/create.d.ts +6 -0
- package/dist/src/cli/commands/browser/create.d.ts.map +1 -0
- package/dist/src/cli/commands/browser/create.js +281 -0
- package/dist/src/cli/commands/browser/create.js.map +1 -0
- package/dist/src/cli/commands/browser/fill.d.ts +6 -0
- package/dist/src/cli/commands/browser/fill.d.ts.map +1 -0
- package/dist/src/cli/commands/browser/fill.js +83 -0
- package/dist/src/cli/commands/browser/fill.js.map +1 -0
- package/dist/src/cli/commands/browser/forward.d.ts +6 -0
- package/dist/src/cli/commands/browser/forward.d.ts.map +1 -0
- package/dist/src/cli/commands/browser/forward.js +42 -0
- package/dist/src/cli/commands/browser/forward.js.map +1 -0
- package/dist/src/cli/commands/browser/get-blocks.d.ts +6 -0
- package/dist/src/cli/commands/browser/get-blocks.d.ts.map +1 -0
- package/dist/src/cli/commands/browser/get-blocks.js +35 -0
- package/dist/src/cli/commands/browser/get-blocks.js.map +1 -0
- package/dist/src/cli/commands/browser/goto.d.ts +6 -0
- package/dist/src/cli/commands/browser/goto.d.ts.map +1 -0
- package/dist/src/cli/commands/browser/goto.js +53 -0
- package/dist/src/cli/commands/browser/goto.js.map +1 -0
- package/dist/src/cli/commands/browser/hover.d.ts +6 -0
- package/dist/src/cli/commands/browser/hover.d.ts.map +1 -0
- package/dist/src/cli/commands/browser/hover.js +63 -0
- package/dist/src/cli/commands/browser/hover.js.map +1 -0
- package/dist/src/cli/commands/browser/index.d.ts +9 -0
- package/dist/src/cli/commands/browser/index.d.ts.map +1 -0
- package/dist/src/cli/commands/browser/index.js +71 -0
- package/dist/src/cli/commands/browser/index.js.map +1 -0
- package/dist/src/cli/commands/browser/list.d.ts +6 -0
- package/dist/src/cli/commands/browser/list.d.ts.map +1 -0
- package/dist/src/cli/commands/browser/list.js +85 -0
- package/dist/src/cli/commands/browser/list.js.map +1 -0
- package/dist/src/cli/commands/browser/press.d.ts +6 -0
- package/dist/src/cli/commands/browser/press.d.ts.map +1 -0
- package/dist/src/cli/commands/browser/press.js +67 -0
- package/dist/src/cli/commands/browser/press.js.map +1 -0
- package/dist/src/cli/commands/browser/reload.d.ts +6 -0
- package/dist/src/cli/commands/browser/reload.d.ts.map +1 -0
- package/dist/src/cli/commands/browser/reload.js +42 -0
- package/dist/src/cli/commands/browser/reload.js.map +1 -0
- package/dist/src/cli/commands/browser/run.d.ts +6 -0
- package/dist/src/cli/commands/browser/run.d.ts.map +1 -0
- package/dist/src/cli/commands/browser/run.js +618 -0
- package/dist/src/cli/commands/browser/run.js.map +1 -0
- package/dist/src/cli/commands/browser/screenshot.d.ts +6 -0
- package/dist/src/cli/commands/browser/screenshot.d.ts.map +1 -0
- package/dist/src/cli/commands/browser/screenshot.js +72 -0
- package/dist/src/cli/commands/browser/screenshot.js.map +1 -0
- package/dist/src/cli/commands/browser/scroll-into-view.d.ts +6 -0
- package/dist/src/cli/commands/browser/scroll-into-view.d.ts.map +1 -0
- package/dist/src/cli/commands/browser/scroll-into-view.js +64 -0
- package/dist/src/cli/commands/browser/scroll-into-view.js.map +1 -0
- package/dist/src/cli/commands/browser/scroll.d.ts +6 -0
- package/dist/src/cli/commands/browser/scroll.d.ts.map +1 -0
- package/dist/src/cli/commands/browser/scroll.js +63 -0
- package/dist/src/cli/commands/browser/scroll.js.map +1 -0
- package/dist/src/cli/commands/browser/select.d.ts +6 -0
- package/dist/src/cli/commands/browser/select.d.ts.map +1 -0
- package/dist/src/cli/commands/browser/select.js +83 -0
- package/dist/src/cli/commands/browser/select.js.map +1 -0
- package/dist/src/cli/commands/browser/snapshot.d.ts +6 -0
- package/dist/src/cli/commands/browser/snapshot.d.ts.map +1 -0
- package/dist/src/cli/commands/browser/snapshot.js +72 -0
- package/dist/src/cli/commands/browser/snapshot.js.map +1 -0
- package/dist/src/cli/commands/browser/status.d.ts +6 -0
- package/dist/src/cli/commands/browser/status.d.ts.map +1 -0
- package/dist/src/cli/commands/browser/status.js +91 -0
- package/dist/src/cli/commands/browser/status.js.map +1 -0
- package/dist/src/cli/commands/browser/stream.d.ts +6 -0
- package/dist/src/cli/commands/browser/stream.d.ts.map +1 -0
- package/dist/src/cli/commands/browser/stream.js +135 -0
- package/dist/src/cli/commands/browser/stream.js.map +1 -0
- package/dist/src/cli/commands/browser/tunnel.d.ts +13 -0
- package/dist/src/cli/commands/browser/tunnel.d.ts.map +1 -0
- package/dist/src/cli/commands/browser/tunnel.js +225 -0
- package/dist/src/cli/commands/browser/tunnel.js.map +1 -0
- package/dist/src/cli/commands/browser/type.d.ts +6 -0
- package/dist/src/cli/commands/browser/type.d.ts.map +1 -0
- package/dist/src/cli/commands/browser/type.js +61 -0
- package/dist/src/cli/commands/browser/type.js.map +1 -0
- package/dist/src/cli/commands/browser/uncheck.d.ts +6 -0
- package/dist/src/cli/commands/browser/uncheck.d.ts.map +1 -0
- package/dist/src/cli/commands/browser/uncheck.js +62 -0
- package/dist/src/cli/commands/browser/uncheck.js.map +1 -0
- package/dist/src/cli/commands/browser/url.d.ts +6 -0
- package/dist/src/cli/commands/browser/url.d.ts.map +1 -0
- package/dist/src/cli/commands/browser/url.js +40 -0
- package/dist/src/cli/commands/browser/url.js.map +1 -0
- package/dist/src/cli/commands/browser/wait-for-load.d.ts +6 -0
- package/dist/src/cli/commands/browser/wait-for-load.d.ts.map +1 -0
- package/dist/src/cli/commands/browser/wait-for-load.js +50 -0
- package/dist/src/cli/commands/browser/wait-for-load.js.map +1 -0
- package/dist/src/cli/commands/browser/wait-for-selector.d.ts +6 -0
- package/dist/src/cli/commands/browser/wait-for-selector.d.ts.map +1 -0
- package/dist/src/cli/commands/browser/wait-for-selector.js +52 -0
- package/dist/src/cli/commands/browser/wait-for-selector.js.map +1 -0
- package/dist/src/cli/commands/browser/wait.d.ts +6 -0
- package/dist/src/cli/commands/browser/wait.d.ts.map +1 -0
- package/dist/src/cli/commands/browser/wait.js +60 -0
- package/dist/src/cli/commands/browser/wait.js.map +1 -0
- package/dist/src/cli/commands/test/run.d.ts.map +1 -1
- package/dist/src/cli/commands/test/run.js +38 -7
- package/dist/src/cli/commands/test/run.js.map +1 -1
- package/dist/src/cli/index.js +2 -0
- package/dist/src/cli/index.js.map +1 -1
- package/dist/src/cli/lib/browser-sessions.d.ts +72 -0
- package/dist/src/cli/lib/browser-sessions.d.ts.map +1 -0
- package/dist/src/cli/lib/browser-sessions.js +184 -0
- package/dist/src/cli/lib/browser-sessions.js.map +1 -0
- package/lib/api/browser-types.ts +278 -0
- package/lib/api/browser.test.ts +378 -0
- package/lib/api/browser.ts +279 -0
- package/package.json +2 -1
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* BrowserApiClient - Client for the desplega.ai Browser API (/browsers/v1/)
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import axios from 'axios';
|
|
6
|
+
import type { AxiosInstance } from 'axios';
|
|
7
|
+
import { getEnv } from '../env/index.js';
|
|
8
|
+
import type {
|
|
9
|
+
BrowserSession,
|
|
10
|
+
BrowserAction,
|
|
11
|
+
ActionResult,
|
|
12
|
+
SnapshotResult,
|
|
13
|
+
UrlResult,
|
|
14
|
+
BlocksResult,
|
|
15
|
+
CreateBrowserSessionOptions,
|
|
16
|
+
BrowserSessionStatus,
|
|
17
|
+
} from './browser-types.js';
|
|
18
|
+
import type { ExtendedStep } from '../../src/types/test-definition.js';
|
|
19
|
+
|
|
20
|
+
export class BrowserApiClient {
|
|
21
|
+
private readonly client: AxiosInstance;
|
|
22
|
+
private apiKey: string | null = null;
|
|
23
|
+
|
|
24
|
+
constructor(baseUrl?: string) {
|
|
25
|
+
// Use environment variable if available, otherwise use provided baseUrl, finally fall back to production
|
|
26
|
+
const apiUrl = getEnv('QA_USE_API_URL') || baseUrl || 'https://api.desplega.ai';
|
|
27
|
+
|
|
28
|
+
this.client = axios.create({
|
|
29
|
+
baseURL: `${apiUrl}/browsers/v1`,
|
|
30
|
+
timeout: 60000, // Longer timeout for browser operations
|
|
31
|
+
headers: {
|
|
32
|
+
'Content-Type': 'application/json',
|
|
33
|
+
},
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
// Auto-load API key from environment or config file if available
|
|
37
|
+
const envApiKey = getEnv('QA_USE_API_KEY');
|
|
38
|
+
if (envApiKey) {
|
|
39
|
+
this.setApiKey(envApiKey);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
setApiKey(apiKey: string): void {
|
|
44
|
+
this.apiKey = apiKey;
|
|
45
|
+
this.client.defaults.headers['Authorization'] = `Bearer ${apiKey}`;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
getApiKey(): string | null {
|
|
49
|
+
return this.apiKey;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
getBaseUrl(): string {
|
|
53
|
+
return this.client.defaults.baseURL || 'https://api.desplega.ai/browsers/v1';
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// ==========================================
|
|
57
|
+
// Session Management
|
|
58
|
+
// ==========================================
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Create a new browser session
|
|
62
|
+
*/
|
|
63
|
+
async createSession(options: CreateBrowserSessionOptions = {}): Promise<BrowserSession> {
|
|
64
|
+
try {
|
|
65
|
+
const response = await this.client.post('/sessions', {
|
|
66
|
+
headless: options.headless ?? true,
|
|
67
|
+
viewport: options.viewport ?? 'desktop',
|
|
68
|
+
timeout: options.timeout ?? 300,
|
|
69
|
+
...(options.ws_url && { ws_url: options.ws_url }),
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
return response.data as BrowserSession;
|
|
73
|
+
} catch (error) {
|
|
74
|
+
throw this.handleError(error, 'create session');
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* List all sessions for the organization
|
|
80
|
+
*/
|
|
81
|
+
async listSessions(): Promise<BrowserSession[]> {
|
|
82
|
+
try {
|
|
83
|
+
const response = await this.client.get('/sessions');
|
|
84
|
+
// API may return { sessions: [...] } or just [...]
|
|
85
|
+
return Array.isArray(response.data) ? response.data : response.data.sessions || [];
|
|
86
|
+
} catch (error) {
|
|
87
|
+
throw this.handleError(error, 'list sessions');
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Get a specific session by ID
|
|
93
|
+
*/
|
|
94
|
+
async getSession(sessionId: string): Promise<BrowserSession> {
|
|
95
|
+
try {
|
|
96
|
+
const response = await this.client.get(`/sessions/${sessionId}`);
|
|
97
|
+
return response.data as BrowserSession;
|
|
98
|
+
} catch (error) {
|
|
99
|
+
throw this.handleError(error, 'get session');
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Delete/close a session
|
|
105
|
+
*/
|
|
106
|
+
async deleteSession(sessionId: string): Promise<void> {
|
|
107
|
+
try {
|
|
108
|
+
await this.client.delete(`/sessions/${sessionId}`);
|
|
109
|
+
} catch (error) {
|
|
110
|
+
throw this.handleError(error, 'delete session');
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Wait for a session to reach a specific status
|
|
116
|
+
* @param sessionId - The session ID to wait for
|
|
117
|
+
* @param targetStatus - The status to wait for (default: 'active')
|
|
118
|
+
* @param timeoutMs - Maximum time to wait in milliseconds (default: 30000)
|
|
119
|
+
* @param pollIntervalMs - Polling interval in milliseconds (default: 1000)
|
|
120
|
+
*/
|
|
121
|
+
async waitForStatus(
|
|
122
|
+
sessionId: string,
|
|
123
|
+
targetStatus: BrowserSessionStatus = 'active',
|
|
124
|
+
timeoutMs: number = 30000,
|
|
125
|
+
pollIntervalMs: number = 1000
|
|
126
|
+
): Promise<BrowserSession> {
|
|
127
|
+
const startTime = Date.now();
|
|
128
|
+
|
|
129
|
+
while (Date.now() - startTime < timeoutMs) {
|
|
130
|
+
const session = await this.getSession(sessionId);
|
|
131
|
+
|
|
132
|
+
if (session.status === targetStatus) {
|
|
133
|
+
return session;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// If session is closed or failed, throw error
|
|
137
|
+
if (session.status === 'closed') {
|
|
138
|
+
throw new Error(`Session ${sessionId} is closed`);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Wait before polling again
|
|
142
|
+
await new Promise((resolve) => setTimeout(resolve, pollIntervalMs));
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
throw new Error(`Timeout waiting for session ${sessionId} to reach status "${targetStatus}"`);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// ==========================================
|
|
149
|
+
// Actions
|
|
150
|
+
// ==========================================
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Execute a browser action
|
|
154
|
+
*/
|
|
155
|
+
async executeAction(sessionId: string, action: BrowserAction): Promise<ActionResult> {
|
|
156
|
+
try {
|
|
157
|
+
const response = await this.client.post(`/sessions/${sessionId}/action`, action);
|
|
158
|
+
return response.data as ActionResult;
|
|
159
|
+
} catch (error) {
|
|
160
|
+
throw this.handleError(error, `execute action "${action.type}"`);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// ==========================================
|
|
165
|
+
// Inspection
|
|
166
|
+
// ==========================================
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Get the ARIA accessibility tree snapshot
|
|
170
|
+
*/
|
|
171
|
+
async getSnapshot(sessionId: string): Promise<SnapshotResult> {
|
|
172
|
+
try {
|
|
173
|
+
const response = await this.client.get(`/sessions/${sessionId}/snapshot`);
|
|
174
|
+
return response.data as SnapshotResult;
|
|
175
|
+
} catch (error) {
|
|
176
|
+
throw this.handleError(error, 'get snapshot');
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Get a screenshot of the current page
|
|
182
|
+
* @returns Buffer containing PNG image data
|
|
183
|
+
*/
|
|
184
|
+
async getScreenshot(sessionId: string): Promise<Buffer> {
|
|
185
|
+
try {
|
|
186
|
+
const response = await this.client.get(`/sessions/${sessionId}/screenshot`, {
|
|
187
|
+
responseType: 'arraybuffer',
|
|
188
|
+
});
|
|
189
|
+
return Buffer.from(response.data);
|
|
190
|
+
} catch (error) {
|
|
191
|
+
throw this.handleError(error, 'get screenshot');
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Get the current page URL
|
|
197
|
+
*/
|
|
198
|
+
async getUrl(sessionId: string): Promise<string> {
|
|
199
|
+
try {
|
|
200
|
+
const response = await this.client.get(`/sessions/${sessionId}/url`);
|
|
201
|
+
const result = response.data as UrlResult;
|
|
202
|
+
return result.url;
|
|
203
|
+
} catch (error) {
|
|
204
|
+
throw this.handleError(error, 'get URL');
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* Get recorded blocks (test steps) from the session
|
|
210
|
+
* @returns Array of ExtendedStep objects
|
|
211
|
+
*/
|
|
212
|
+
async getBlocks(sessionId: string): Promise<ExtendedStep[]> {
|
|
213
|
+
try {
|
|
214
|
+
const response = await this.client.get(`/sessions/${sessionId}/blocks`);
|
|
215
|
+
const result = response.data as BlocksResult;
|
|
216
|
+
return (result.blocks || []) as ExtendedStep[];
|
|
217
|
+
} catch (error) {
|
|
218
|
+
throw this.handleError(error, 'get blocks');
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// ==========================================
|
|
223
|
+
// WebSocket Streaming
|
|
224
|
+
// ==========================================
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* Get the WebSocket URL for streaming events
|
|
228
|
+
*/
|
|
229
|
+
getStreamUrl(sessionId: string): string {
|
|
230
|
+
// Convert HTTP URL to WebSocket URL
|
|
231
|
+
const baseUrl = this.getBaseUrl();
|
|
232
|
+
const wsUrl = baseUrl.replace(/^http/, 'ws');
|
|
233
|
+
return `${wsUrl}/sessions/${sessionId}/stream`;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// ==========================================
|
|
237
|
+
// Error Handling
|
|
238
|
+
// ==========================================
|
|
239
|
+
|
|
240
|
+
private handleError(error: unknown, operation: string): Error {
|
|
241
|
+
if (axios.isAxiosError(error)) {
|
|
242
|
+
const statusCode = error.response?.status;
|
|
243
|
+
const errorData = error.response?.data as { message?: string; detail?: string } | undefined;
|
|
244
|
+
|
|
245
|
+
if (statusCode === 404) {
|
|
246
|
+
return new Error(`Session not found`);
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
if (statusCode === 401) {
|
|
250
|
+
return new Error(`Unauthorized. Please check your API key.`);
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
if (statusCode === 403) {
|
|
254
|
+
return new Error(`Forbidden. You don't have permission for this operation.`);
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
const message =
|
|
258
|
+
errorData?.message || errorData?.detail || `HTTP ${statusCode}: Failed to ${operation}`;
|
|
259
|
+
return new Error(message);
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
if (error instanceof Error) {
|
|
263
|
+
return error;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
return new Error(`Unknown error during ${operation}`);
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
// Re-export types for convenience
|
|
271
|
+
export type {
|
|
272
|
+
BrowserSession,
|
|
273
|
+
BrowserAction,
|
|
274
|
+
ActionResult,
|
|
275
|
+
SnapshotResult,
|
|
276
|
+
UrlResult,
|
|
277
|
+
CreateBrowserSessionOptions,
|
|
278
|
+
BrowserSessionStatus,
|
|
279
|
+
} from './browser-types.js';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@desplega.ai/qa-use",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.2.2",
|
|
4
4
|
"packageManager": "bun@^1.3.4",
|
|
5
5
|
"description": "QA automation tool for browser testing with MCP server support",
|
|
6
6
|
"type": "module",
|
|
@@ -95,6 +95,7 @@
|
|
|
95
95
|
"express": "^4.21.2",
|
|
96
96
|
"glob": "^13.0.0",
|
|
97
97
|
"playwright": "1.55.0",
|
|
98
|
+
"ws": "^8.19.0",
|
|
98
99
|
"yaml": "^2.8.2"
|
|
99
100
|
}
|
|
100
101
|
}
|