@desplega.ai/qa-use 2.4.1 → 2.5.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/dist/lib/api/browser.d.ts +2 -2
- package/dist/lib/api/browser.d.ts.map +1 -1
- package/dist/lib/api/browser.js +1 -1
- package/dist/lib/api/browser.js.map +1 -1
- package/dist/lib/api/index.d.ts +9 -5
- package/dist/lib/api/index.d.ts.map +1 -1
- package/dist/lib/api/index.js +11 -2
- package/dist/lib/api/index.js.map +1 -1
- package/dist/lib/browser/index.d.ts +1 -1
- package/dist/lib/browser/index.d.ts.map +1 -1
- package/dist/lib/browser/index.js +7 -8
- package/dist/lib/browser/index.js.map +1 -1
- package/dist/lib/env/index.js +4 -4
- package/dist/lib/env/index.js.map +1 -1
- package/dist/lib/tunnel/index.d.ts.map +1 -1
- package/dist/lib/tunnel/index.js +6 -6
- package/dist/lib/tunnel/index.js.map +1 -1
- package/dist/package.json +10 -14
- package/dist/src/cli/commands/browser/back.js +1 -1
- package/dist/src/cli/commands/browser/back.js.map +1 -1
- package/dist/src/cli/commands/browser/check.js +1 -1
- package/dist/src/cli/commands/browser/check.js.map +1 -1
- package/dist/src/cli/commands/browser/click.js +1 -1
- package/dist/src/cli/commands/browser/click.js.map +1 -1
- package/dist/src/cli/commands/browser/close.js +2 -2
- package/dist/src/cli/commands/browser/close.js.map +1 -1
- package/dist/src/cli/commands/browser/create.js +5 -5
- package/dist/src/cli/commands/browser/create.js.map +1 -1
- package/dist/src/cli/commands/browser/drag.js +1 -1
- package/dist/src/cli/commands/browser/drag.js.map +1 -1
- package/dist/src/cli/commands/browser/fill.js +2 -2
- package/dist/src/cli/commands/browser/fill.js.map +1 -1
- package/dist/src/cli/commands/browser/forward.js +1 -1
- package/dist/src/cli/commands/browser/forward.js.map +1 -1
- package/dist/src/cli/commands/browser/generate-test.js +2 -2
- package/dist/src/cli/commands/browser/generate-test.js.map +1 -1
- package/dist/src/cli/commands/browser/goto.js +2 -2
- package/dist/src/cli/commands/browser/goto.js.map +1 -1
- package/dist/src/cli/commands/browser/hover.js +1 -1
- package/dist/src/cli/commands/browser/hover.js.map +1 -1
- package/dist/src/cli/commands/browser/index.d.ts.map +1 -1
- package/dist/src/cli/commands/browser/index.js +24 -24
- package/dist/src/cli/commands/browser/index.js.map +1 -1
- package/dist/src/cli/commands/browser/list.js +2 -2
- package/dist/src/cli/commands/browser/list.js.map +1 -1
- package/dist/src/cli/commands/browser/mfa-totp.js +1 -1
- package/dist/src/cli/commands/browser/mfa-totp.js.map +1 -1
- package/dist/src/cli/commands/browser/press.js +1 -1
- package/dist/src/cli/commands/browser/press.js.map +1 -1
- package/dist/src/cli/commands/browser/reload.js +1 -1
- package/dist/src/cli/commands/browser/reload.js.map +1 -1
- package/dist/src/cli/commands/browser/run.d.ts.map +1 -1
- package/dist/src/cli/commands/browser/run.js +16 -16
- package/dist/src/cli/commands/browser/run.js.map +1 -1
- package/dist/src/cli/commands/browser/screenshot.d.ts.map +1 -1
- package/dist/src/cli/commands/browser/screenshot.js +3 -3
- package/dist/src/cli/commands/browser/screenshot.js.map +1 -1
- package/dist/src/cli/commands/browser/scroll-into-view.js +1 -1
- package/dist/src/cli/commands/browser/scroll-into-view.js.map +1 -1
- package/dist/src/cli/commands/browser/scroll.js +2 -2
- package/dist/src/cli/commands/browser/scroll.js.map +1 -1
- package/dist/src/cli/commands/browser/select.js +2 -2
- package/dist/src/cli/commands/browser/select.js.map +1 -1
- package/dist/src/cli/commands/browser/snapshot.js +2 -2
- package/dist/src/cli/commands/browser/snapshot.js.map +1 -1
- package/dist/src/cli/commands/browser/status.js +1 -1
- package/dist/src/cli/commands/browser/status.js.map +1 -1
- package/dist/src/cli/commands/browser/stream.js +1 -1
- package/dist/src/cli/commands/browser/stream.js.map +1 -1
- package/dist/src/cli/commands/browser/type.js +2 -2
- package/dist/src/cli/commands/browser/type.js.map +1 -1
- package/dist/src/cli/commands/browser/uncheck.js +1 -1
- package/dist/src/cli/commands/browser/uncheck.js.map +1 -1
- package/dist/src/cli/commands/browser/upload.d.ts.map +1 -1
- package/dist/src/cli/commands/browser/upload.js +3 -3
- package/dist/src/cli/commands/browser/upload.js.map +1 -1
- package/dist/src/cli/commands/browser/wait-for-load.js +1 -1
- package/dist/src/cli/commands/browser/wait-for-load.js.map +1 -1
- package/dist/src/cli/commands/browser/wait-for-selector.js +1 -1
- package/dist/src/cli/commands/browser/wait-for-selector.js.map +1 -1
- package/dist/src/cli/commands/browser/wait.js +2 -2
- package/dist/src/cli/commands/browser/wait.js.map +1 -1
- package/dist/src/cli/commands/info.js +4 -4
- package/dist/src/cli/commands/info.js.map +1 -1
- package/dist/src/cli/commands/install-deps.js +1 -1
- package/dist/src/cli/commands/install-deps.js.map +1 -1
- package/dist/src/cli/commands/setup.d.ts.map +1 -1
- package/dist/src/cli/commands/setup.js +4 -4
- package/dist/src/cli/commands/setup.js.map +1 -1
- package/dist/src/cli/commands/test/diff.d.ts +6 -0
- package/dist/src/cli/commands/test/diff.d.ts.map +1 -0
- package/dist/src/cli/commands/test/diff.js +187 -0
- package/dist/src/cli/commands/test/diff.js.map +1 -0
- package/dist/src/cli/commands/test/export.d.ts.map +1 -1
- package/dist/src/cli/commands/test/export.js +4 -4
- package/dist/src/cli/commands/test/export.js.map +1 -1
- package/dist/src/cli/commands/test/index.d.ts.map +1 -1
- package/dist/src/cli/commands/test/index.js +7 -5
- package/dist/src/cli/commands/test/index.js.map +1 -1
- package/dist/src/cli/commands/test/info.d.ts.map +1 -1
- package/dist/src/cli/commands/test/info.js +4 -4
- package/dist/src/cli/commands/test/info.js.map +1 -1
- package/dist/src/cli/commands/test/init.d.ts.map +1 -1
- package/dist/src/cli/commands/test/init.js +4 -4
- package/dist/src/cli/commands/test/init.js.map +1 -1
- package/dist/src/cli/commands/test/list.d.ts.map +1 -1
- package/dist/src/cli/commands/test/list.js +4 -4
- package/dist/src/cli/commands/test/list.js.map +1 -1
- package/dist/src/cli/commands/test/run.js +5 -5
- package/dist/src/cli/commands/test/run.js.map +1 -1
- package/dist/src/cli/commands/test/runs.js +5 -5
- package/dist/src/cli/commands/test/runs.js.map +1 -1
- package/dist/src/cli/commands/test/sync.d.ts +5 -0
- package/dist/src/cli/commands/test/sync.d.ts.map +1 -1
- package/dist/src/cli/commands/test/sync.js +68 -10
- package/dist/src/cli/commands/test/sync.js.map +1 -1
- package/dist/src/cli/commands/test/validate.js +2 -2
- package/dist/src/cli/commands/test/validate.js.map +1 -1
- package/dist/src/cli/commands/update.d.ts.map +1 -1
- package/dist/src/cli/commands/update.js +5 -5
- package/dist/src/cli/commands/update.js.map +1 -1
- package/dist/src/cli/index.js +5 -5
- package/dist/src/cli/index.js.map +1 -1
- package/dist/src/cli/lib/browser-sessions.js +4 -4
- package/dist/src/cli/lib/browser-sessions.js.map +1 -1
- package/dist/src/cli/lib/browser.d.ts +1 -1
- package/dist/src/cli/lib/browser.d.ts.map +1 -1
- package/dist/src/cli/lib/browser.js +2 -2
- package/dist/src/cli/lib/browser.js.map +1 -1
- package/dist/src/cli/lib/config.js +3 -3
- package/dist/src/cli/lib/config.js.map +1 -1
- package/dist/src/cli/lib/download.js +6 -6
- package/dist/src/cli/lib/download.js.map +1 -1
- package/dist/src/cli/lib/id-injector.js +1 -1
- package/dist/src/cli/lib/id-injector.js.map +1 -1
- package/dist/src/cli/lib/loader.js +2 -2
- package/dist/src/cli/lib/loader.js.map +1 -1
- package/dist/src/cli/lib/output.js +8 -8
- package/dist/src/cli/lib/output.js.map +1 -1
- package/dist/src/cli/lib/runner.d.ts +1 -1
- package/dist/src/cli/lib/runner.d.ts.map +1 -1
- package/dist/src/cli/lib/runner.js +0 -1
- package/dist/src/cli/lib/runner.js.map +1 -1
- package/dist/src/http-server.d.ts.map +1 -1
- package/dist/src/http-server.js +3 -4
- package/dist/src/http-server.js.map +1 -1
- package/dist/src/server.d.ts +0 -1
- package/dist/src/server.d.ts.map +1 -1
- package/dist/src/server.js +18 -86
- package/dist/src/server.js.map +1 -1
- package/dist/src/tunnel-mode.js +2 -2
- package/dist/src/tunnel-mode.js.map +1 -1
- package/dist/src/types/test-definition.d.ts +18 -6
- package/dist/src/types/test-definition.d.ts.map +1 -1
- package/dist/src/types/test-definition.js +2 -2
- package/dist/src/types.d.ts.map +1 -1
- package/dist/src/types.js +5 -12
- package/dist/src/types.js.map +1 -1
- package/dist/src/utils/package.js +4 -4
- package/dist/src/utils/package.js.map +1 -1
- package/dist/src/utils/summary.d.ts +1 -1
- package/dist/src/utils/summary.d.ts.map +1 -1
- package/dist/src/utils/summary.js.map +1 -1
- package/lib/api/browser.test.ts +2 -3
- package/lib/api/browser.ts +22 -22
- package/lib/api/index.test.ts +169 -0
- package/lib/api/index.ts +25 -11
- package/lib/browser/index.ts +8 -9
- package/lib/env/index.ts +4 -4
- package/lib/tunnel/index.test.ts +1 -1
- package/lib/tunnel/index.ts +6 -6
- package/lib/tunnel/integration.test.ts +3 -3
- package/package.json +10 -14
package/lib/api/browser.ts
CHANGED
|
@@ -2,27 +2,27 @@
|
|
|
2
2
|
* BrowserApiClient - Client for the desplega.ai Browser API (/browsers/v1/)
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
import axios from 'axios';
|
|
6
5
|
import type { AxiosInstance } from 'axios';
|
|
6
|
+
import axios from 'axios';
|
|
7
|
+
import type { ExtendedStep } from '../../src/types/test-definition.js';
|
|
7
8
|
import { getEnv } from '../env/index.js';
|
|
8
9
|
import type {
|
|
9
|
-
BrowserSession,
|
|
10
|
-
BrowserAction,
|
|
11
10
|
ActionResult,
|
|
12
|
-
SnapshotResult,
|
|
13
|
-
SnapshotOptions,
|
|
14
|
-
UrlResult,
|
|
15
11
|
BlocksResult,
|
|
16
|
-
|
|
12
|
+
BrowserAction,
|
|
13
|
+
BrowserSession,
|
|
17
14
|
BrowserSessionStatus,
|
|
18
|
-
GenerateTestOptions,
|
|
19
|
-
GenerateTestResult,
|
|
20
15
|
ConsoleLogsOptions,
|
|
21
16
|
ConsoleLogsResult,
|
|
17
|
+
CreateBrowserSessionOptions,
|
|
18
|
+
GenerateTestOptions,
|
|
19
|
+
GenerateTestResult,
|
|
22
20
|
NetworkLogsOptions,
|
|
23
21
|
NetworkLogsResult,
|
|
22
|
+
SnapshotOptions,
|
|
23
|
+
SnapshotResult,
|
|
24
|
+
UrlResult,
|
|
24
25
|
} from './browser-types.js';
|
|
25
|
-
import type { ExtendedStep } from '../../src/types/test-definition.js';
|
|
26
26
|
|
|
27
27
|
export class BrowserApiClient {
|
|
28
28
|
private readonly client: AxiosInstance;
|
|
@@ -49,7 +49,7 @@ export class BrowserApiClient {
|
|
|
49
49
|
|
|
50
50
|
setApiKey(apiKey: string): void {
|
|
51
51
|
this.apiKey = apiKey;
|
|
52
|
-
this.client.defaults.headers
|
|
52
|
+
this.client.defaults.headers.Authorization = `Bearer ${apiKey}`;
|
|
53
53
|
}
|
|
54
54
|
|
|
55
55
|
getApiKey(): string | null {
|
|
@@ -382,21 +382,21 @@ export class BrowserApiClient {
|
|
|
382
382
|
|
|
383
383
|
// Re-export types for convenience
|
|
384
384
|
export type {
|
|
385
|
-
BrowserSession,
|
|
386
|
-
BrowserAction,
|
|
387
385
|
ActionResult,
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
SnapshotFilterStats,
|
|
391
|
-
UrlResult,
|
|
392
|
-
CreateBrowserSessionOptions,
|
|
386
|
+
BrowserAction,
|
|
387
|
+
BrowserSession,
|
|
393
388
|
BrowserSessionStatus,
|
|
394
|
-
|
|
395
|
-
GenerateTestResult,
|
|
389
|
+
ConsoleLogEntry,
|
|
396
390
|
ConsoleLogsOptions,
|
|
397
391
|
ConsoleLogsResult,
|
|
398
|
-
|
|
392
|
+
CreateBrowserSessionOptions,
|
|
393
|
+
GenerateTestOptions,
|
|
394
|
+
GenerateTestResult,
|
|
395
|
+
NetworkLogEntry,
|
|
399
396
|
NetworkLogsOptions,
|
|
400
397
|
NetworkLogsResult,
|
|
401
|
-
|
|
398
|
+
SnapshotFilterStats,
|
|
399
|
+
SnapshotOptions,
|
|
400
|
+
SnapshotResult,
|
|
401
|
+
UrlResult,
|
|
402
402
|
} from './browser-types.js';
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ApiClient Unit Tests for importTestDefinition
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { beforeEach, describe, expect, it, mock } from 'bun:test';
|
|
6
|
+
import { ApiClient } from './index.js';
|
|
7
|
+
|
|
8
|
+
// Mock axios
|
|
9
|
+
const mockAxiosInstance = {
|
|
10
|
+
get: mock(() => Promise.resolve({ data: {} })),
|
|
11
|
+
post: mock(() => Promise.resolve({ data: {} })),
|
|
12
|
+
delete: mock(() => Promise.resolve({ data: {} })),
|
|
13
|
+
defaults: {
|
|
14
|
+
baseURL: 'https://api.desplega.ai',
|
|
15
|
+
headers: {} as Record<string, string>,
|
|
16
|
+
},
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
mock.module('axios', () => ({
|
|
20
|
+
default: {
|
|
21
|
+
create: () => mockAxiosInstance,
|
|
22
|
+
isAxiosError: (err: unknown) =>
|
|
23
|
+
typeof err === 'object' && err !== null && 'response' in err && 'isAxiosError' in err,
|
|
24
|
+
},
|
|
25
|
+
}));
|
|
26
|
+
|
|
27
|
+
describe('ApiClient', () => {
|
|
28
|
+
let client: ApiClient;
|
|
29
|
+
|
|
30
|
+
beforeEach(() => {
|
|
31
|
+
// Reset mocks
|
|
32
|
+
mockAxiosInstance.get.mockReset();
|
|
33
|
+
mockAxiosInstance.post.mockReset();
|
|
34
|
+
mockAxiosInstance.delete.mockReset();
|
|
35
|
+
|
|
36
|
+
// Clear environment variables
|
|
37
|
+
delete process.env.QA_USE_API_KEY;
|
|
38
|
+
delete process.env.QA_USE_API_URL;
|
|
39
|
+
|
|
40
|
+
client = new ApiClient();
|
|
41
|
+
client.setApiKey('test-key');
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
describe('importTestDefinition', () => {
|
|
45
|
+
const testDef = {
|
|
46
|
+
name: 'Test Definition',
|
|
47
|
+
steps: [{ goto: 'https://example.com' }],
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
it('should pass force option when provided', async () => {
|
|
51
|
+
const mockResult = {
|
|
52
|
+
success: true,
|
|
53
|
+
imported: [{ name: 'Test', id: '123', action: 'created', version_hash: 'abc123' }],
|
|
54
|
+
errors: [],
|
|
55
|
+
};
|
|
56
|
+
mockAxiosInstance.post.mockResolvedValueOnce({ data: mockResult });
|
|
57
|
+
|
|
58
|
+
await client.importTestDefinition([testDef], { upsert: true, force: true });
|
|
59
|
+
|
|
60
|
+
expect(mockAxiosInstance.post).toHaveBeenCalledWith('/vibe-qa/cli/import', {
|
|
61
|
+
test_definitions: [testDef],
|
|
62
|
+
upsert: true,
|
|
63
|
+
dry_run: false,
|
|
64
|
+
force: true,
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it('should default force to false', async () => {
|
|
69
|
+
const mockResult = {
|
|
70
|
+
success: true,
|
|
71
|
+
imported: [{ name: 'Test', id: '123', action: 'created' }],
|
|
72
|
+
errors: [],
|
|
73
|
+
};
|
|
74
|
+
mockAxiosInstance.post.mockResolvedValueOnce({ data: mockResult });
|
|
75
|
+
|
|
76
|
+
await client.importTestDefinition([testDef], { upsert: true });
|
|
77
|
+
|
|
78
|
+
expect(mockAxiosInstance.post).toHaveBeenCalledWith('/vibe-qa/cli/import', {
|
|
79
|
+
test_definitions: [testDef],
|
|
80
|
+
upsert: true,
|
|
81
|
+
dry_run: false,
|
|
82
|
+
force: false,
|
|
83
|
+
});
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
it('should handle conflict action in response', async () => {
|
|
87
|
+
const mockResult = {
|
|
88
|
+
success: true,
|
|
89
|
+
imported: [
|
|
90
|
+
{
|
|
91
|
+
name: 'Test',
|
|
92
|
+
id: '123',
|
|
93
|
+
action: 'conflict',
|
|
94
|
+
message: 'Remote modified since last sync',
|
|
95
|
+
prev_version_hash: 'old123',
|
|
96
|
+
version_hash: 'new456',
|
|
97
|
+
},
|
|
98
|
+
],
|
|
99
|
+
errors: [],
|
|
100
|
+
};
|
|
101
|
+
mockAxiosInstance.post.mockResolvedValueOnce({ data: mockResult });
|
|
102
|
+
|
|
103
|
+
const result = await client.importTestDefinition([testDef], { upsert: true });
|
|
104
|
+
|
|
105
|
+
expect(result.success).toBe(true);
|
|
106
|
+
expect(result.imported[0].action).toBe('conflict');
|
|
107
|
+
expect(result.imported[0].message).toBe('Remote modified since last sync');
|
|
108
|
+
expect(result.imported[0].prev_version_hash).toBe('old123');
|
|
109
|
+
expect(result.imported[0].version_hash).toBe('new456');
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
it('should handle unchanged action in response', async () => {
|
|
113
|
+
const mockResult = {
|
|
114
|
+
success: true,
|
|
115
|
+
imported: [
|
|
116
|
+
{
|
|
117
|
+
name: 'Test',
|
|
118
|
+
id: '123',
|
|
119
|
+
action: 'unchanged',
|
|
120
|
+
version_hash: 'same123',
|
|
121
|
+
},
|
|
122
|
+
],
|
|
123
|
+
errors: [],
|
|
124
|
+
};
|
|
125
|
+
mockAxiosInstance.post.mockResolvedValueOnce({ data: mockResult });
|
|
126
|
+
|
|
127
|
+
const result = await client.importTestDefinition([testDef], { upsert: true });
|
|
128
|
+
|
|
129
|
+
expect(result.success).toBe(true);
|
|
130
|
+
expect(result.imported[0].action).toBe('unchanged');
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
it('should pass dry_run option', async () => {
|
|
134
|
+
const mockResult = {
|
|
135
|
+
success: true,
|
|
136
|
+
imported: [{ name: 'Test', id: '123', action: 'created' }],
|
|
137
|
+
errors: [],
|
|
138
|
+
};
|
|
139
|
+
mockAxiosInstance.post.mockResolvedValueOnce({ data: mockResult });
|
|
140
|
+
|
|
141
|
+
await client.importTestDefinition([testDef], { dry_run: true });
|
|
142
|
+
|
|
143
|
+
expect(mockAxiosInstance.post).toHaveBeenCalledWith('/vibe-qa/cli/import', {
|
|
144
|
+
test_definitions: [testDef],
|
|
145
|
+
upsert: true,
|
|
146
|
+
dry_run: true,
|
|
147
|
+
force: false,
|
|
148
|
+
});
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
it('should use default options when none provided', async () => {
|
|
152
|
+
const mockResult = {
|
|
153
|
+
success: true,
|
|
154
|
+
imported: [],
|
|
155
|
+
errors: [],
|
|
156
|
+
};
|
|
157
|
+
mockAxiosInstance.post.mockResolvedValueOnce({ data: mockResult });
|
|
158
|
+
|
|
159
|
+
await client.importTestDefinition([testDef]);
|
|
160
|
+
|
|
161
|
+
expect(mockAxiosInstance.post).toHaveBeenCalledWith('/vibe-qa/cli/import', {
|
|
162
|
+
test_definitions: [testDef],
|
|
163
|
+
upsert: true,
|
|
164
|
+
dry_run: false,
|
|
165
|
+
force: false,
|
|
166
|
+
});
|
|
167
|
+
});
|
|
168
|
+
});
|
|
169
|
+
});
|
package/lib/api/index.ts
CHANGED
|
@@ -1,23 +1,23 @@
|
|
|
1
|
-
import axios from 'axios';
|
|
2
1
|
import type { AxiosInstance, AxiosResponse } from 'axios';
|
|
2
|
+
import axios from 'axios';
|
|
3
3
|
import 'dotenv/config';
|
|
4
|
-
import {
|
|
4
|
+
import type { TestDefinition } from '../../src/types/test-definition.js';
|
|
5
5
|
import type {
|
|
6
|
-
|
|
6
|
+
IssueReport,
|
|
7
7
|
IssueType,
|
|
8
8
|
Severity,
|
|
9
|
-
|
|
9
|
+
TestAgentV2Session,
|
|
10
10
|
TestCreatorDoneIntent,
|
|
11
11
|
} from '../../src/types.js';
|
|
12
|
+
import type { BlockSummary, EnhancedTestSummary } from '../../src/utils/summary.js';
|
|
12
13
|
import {
|
|
13
|
-
|
|
14
|
+
categorizeIssues,
|
|
14
15
|
formatEnhancedTestReport,
|
|
16
|
+
generateEnhancedTestSummary,
|
|
15
17
|
generateIssueStatistics,
|
|
16
|
-
categorizeIssues,
|
|
17
18
|
} from '../../src/utils/summary.js';
|
|
18
|
-
import
|
|
19
|
-
import {
|
|
20
|
-
import type { TestDefinition } from '../../src/types/test-definition.js';
|
|
19
|
+
import { getEnv } from '../env/index.js';
|
|
20
|
+
import { type SSEEvent, streamSSE } from './sse.js';
|
|
21
21
|
|
|
22
22
|
// Re-export new types for external consumers
|
|
23
23
|
export type { IssueType, Severity, IssueReport, TestCreatorDoneIntent };
|
|
@@ -285,12 +285,16 @@ export interface ValidationResult {
|
|
|
285
285
|
export interface ImportOptions {
|
|
286
286
|
upsert?: boolean;
|
|
287
287
|
dry_run?: boolean;
|
|
288
|
+
force?: boolean; // Override version conflicts
|
|
288
289
|
}
|
|
289
290
|
|
|
290
291
|
export interface ImportedTest {
|
|
291
292
|
name: string;
|
|
292
293
|
id: string;
|
|
293
|
-
action: 'created' | 'updated' | 'skipped';
|
|
294
|
+
action: 'created' | 'updated' | 'skipped' | 'unchanged' | 'conflict';
|
|
295
|
+
message?: string; // Warning/conflict details
|
|
296
|
+
prev_version_hash?: string; // Hash before operation
|
|
297
|
+
version_hash?: string; // Hash after operation
|
|
294
298
|
}
|
|
295
299
|
|
|
296
300
|
export interface ImportResult {
|
|
@@ -331,7 +335,7 @@ export class ApiClient {
|
|
|
331
335
|
|
|
332
336
|
setApiKey(apiKey: string): void {
|
|
333
337
|
this.apiKey = apiKey;
|
|
334
|
-
this.client.defaults.headers
|
|
338
|
+
this.client.defaults.headers.Authorization = `Bearer ${apiKey}`;
|
|
335
339
|
}
|
|
336
340
|
|
|
337
341
|
getApiKey(): string | null {
|
|
@@ -936,6 +940,7 @@ export class ApiClient {
|
|
|
936
940
|
test_definitions: definitions,
|
|
937
941
|
upsert: options.upsert ?? true,
|
|
938
942
|
dry_run: options.dry_run ?? false,
|
|
943
|
+
force: options.force ?? false,
|
|
939
944
|
});
|
|
940
945
|
|
|
941
946
|
return response.data as ImportResult;
|
|
@@ -944,6 +949,15 @@ export class ApiClient {
|
|
|
944
949
|
const statusCode = error.response?.status;
|
|
945
950
|
const errorData = error.response?.data;
|
|
946
951
|
|
|
952
|
+
// Handle validation errors (detail is an array of error objects)
|
|
953
|
+
if (Array.isArray(errorData?.detail)) {
|
|
954
|
+
const messages = errorData.detail.map((e: { msg?: string; loc?: string[] }) => {
|
|
955
|
+
const field = e.loc?.slice(2).join('.') || 'unknown';
|
|
956
|
+
return `${field}: ${e.msg || 'validation error'}`;
|
|
957
|
+
});
|
|
958
|
+
throw new Error(`Validation failed:\n ${messages.join('\n ')}`);
|
|
959
|
+
}
|
|
960
|
+
|
|
947
961
|
throw new Error(
|
|
948
962
|
errorData?.message ||
|
|
949
963
|
errorData?.detail ||
|
package/lib/browser/index.ts
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
|
+
import { fork } from 'node:child_process';
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import { createRequire } from 'node:module';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import type { Browser, BrowserServer } from 'playwright';
|
|
1
6
|
import { chromium } from 'playwright';
|
|
2
|
-
import type { BrowserServer, Browser } from 'playwright';
|
|
3
|
-
import { fork } from 'child_process';
|
|
4
|
-
import path from 'path';
|
|
5
|
-
import fs from 'fs';
|
|
6
|
-
import { createRequire } from 'module';
|
|
7
|
-
import { fileURLToPath } from 'url';
|
|
8
7
|
|
|
9
8
|
export interface BrowserInstallationStatus {
|
|
10
9
|
installed: boolean;
|
|
@@ -90,7 +89,7 @@ export class BrowserManager {
|
|
|
90
89
|
|
|
91
90
|
try {
|
|
92
91
|
await session.browserServer.close();
|
|
93
|
-
} catch
|
|
92
|
+
} catch {
|
|
94
93
|
// Silently handle cleanup errors
|
|
95
94
|
}
|
|
96
95
|
}
|
|
@@ -102,7 +101,7 @@ export class BrowserManager {
|
|
|
102
101
|
|
|
103
102
|
try {
|
|
104
103
|
return await chromium.connect(this.session.wsEndpoint);
|
|
105
|
-
} catch
|
|
104
|
+
} catch {
|
|
106
105
|
return null;
|
|
107
106
|
}
|
|
108
107
|
}
|
|
@@ -127,7 +126,7 @@ export class BrowserManager {
|
|
|
127
126
|
});
|
|
128
127
|
await browser.close();
|
|
129
128
|
return true;
|
|
130
|
-
} catch
|
|
129
|
+
} catch {
|
|
131
130
|
this.session.isActive = false;
|
|
132
131
|
return false;
|
|
133
132
|
}
|
package/lib/env/index.ts
CHANGED
|
@@ -6,9 +6,9 @@
|
|
|
6
6
|
* 2. Config file (~/.qa-use.json) with structure: { "env": { "VAR_NAME": "value" } }
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
9
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
10
|
+
import { homedir } from 'node:os';
|
|
11
|
+
import { join } from 'node:path';
|
|
12
12
|
|
|
13
13
|
interface QaUseConfig {
|
|
14
14
|
env?: Record<string, string>;
|
|
@@ -133,7 +133,7 @@ export function logConfigSources(): void {
|
|
|
133
133
|
const sources: string[] = [];
|
|
134
134
|
|
|
135
135
|
if (apiKey.value) {
|
|
136
|
-
const maskedKey = apiKey.value.slice(0, 8)
|
|
136
|
+
const maskedKey = `${apiKey.value.slice(0, 8)}...${apiKey.value.slice(-4)}`;
|
|
137
137
|
sources.push(` API Key: ${maskedKey} (from ${getSourceDescription(apiKey.source)})`);
|
|
138
138
|
}
|
|
139
139
|
|
package/lib/tunnel/index.test.ts
CHANGED
package/lib/tunnel/index.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
+
import crypto from 'node:crypto';
|
|
2
|
+
import https from 'node:https';
|
|
3
|
+
import { URL } from 'node:url';
|
|
1
4
|
import localtunnel from '@desplega.ai/localtunnel';
|
|
2
|
-
import { URL } from 'url';
|
|
3
|
-
import https from 'https';
|
|
4
|
-
import crypto from 'crypto';
|
|
5
5
|
import { getEnv } from '../env/index.js';
|
|
6
6
|
|
|
7
7
|
export interface TunnelSession {
|
|
@@ -124,7 +124,7 @@ export class TunnelManager {
|
|
|
124
124
|
|
|
125
125
|
try {
|
|
126
126
|
await session.tunnel.close();
|
|
127
|
-
} catch
|
|
127
|
+
} catch {
|
|
128
128
|
// Silently handle cleanup errors
|
|
129
129
|
}
|
|
130
130
|
}
|
|
@@ -155,7 +155,7 @@ export class TunnelManager {
|
|
|
155
155
|
clearTimeout(timeout);
|
|
156
156
|
// 426 = Upgrade Required (expected for WebSocket endpoint)
|
|
157
157
|
return response.ok || response.status === 426;
|
|
158
|
-
} catch
|
|
158
|
+
} catch {
|
|
159
159
|
this.session.isActive = false;
|
|
160
160
|
return false;
|
|
161
161
|
}
|
|
@@ -190,7 +190,7 @@ export class TunnelManager {
|
|
|
190
190
|
return (
|
|
191
191
|
this.session.publicUrl.replace('https://', 'wss://').replace('http://', 'ws://') + wsPath
|
|
192
192
|
);
|
|
193
|
-
} catch
|
|
193
|
+
} catch {
|
|
194
194
|
return null;
|
|
195
195
|
}
|
|
196
196
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, it } from 'bun:test';
|
|
2
|
+
import http from 'node:http';
|
|
2
3
|
import { TunnelManager } from './index';
|
|
3
|
-
import http from 'http';
|
|
4
4
|
|
|
5
5
|
describe('TunnelManager Integration Tests', () => {
|
|
6
6
|
let tunnelManager: TunnelManager;
|
|
@@ -12,7 +12,7 @@ describe('TunnelManager Integration Tests', () => {
|
|
|
12
12
|
|
|
13
13
|
// Create a simple test HTTP server
|
|
14
14
|
return new Promise<void>((resolve) => {
|
|
15
|
-
testServer = http.createServer((
|
|
15
|
+
testServer = http.createServer((_req, res) => {
|
|
16
16
|
res.writeHead(200, { 'Content-Type': 'text/plain' });
|
|
17
17
|
res.end('Hello from test server');
|
|
18
18
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@desplega.ai/qa-use",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.5.0",
|
|
4
4
|
"packageManager": "bun@^1.3.4",
|
|
5
5
|
"description": "QA automation tool for browser testing with MCP server support",
|
|
6
6
|
"type": "module",
|
|
@@ -26,10 +26,12 @@
|
|
|
26
26
|
"start": "bun run build && node dist/src/index.js",
|
|
27
27
|
"inspector": "bun run build && npx -y @modelcontextprotocol/inspector node dist/src/index.js",
|
|
28
28
|
"inspector:env": "bun run build && npx dotenv-cli -e .env -- npx -y @modelcontextprotocol/inspector node dist/src/index.js",
|
|
29
|
-
"format": "
|
|
30
|
-
"format:check": "
|
|
31
|
-
"lint": "
|
|
32
|
-
"lint:fix": "
|
|
29
|
+
"format": "biome format --write src lib",
|
|
30
|
+
"format:check": "biome format src lib",
|
|
31
|
+
"lint": "biome lint src lib && oxlint src lib",
|
|
32
|
+
"lint:fix": "biome lint --write src lib && oxlint src lib --fix",
|
|
33
|
+
"check": "biome check src lib",
|
|
34
|
+
"check:fix": "biome check --write src lib",
|
|
33
35
|
"typecheck": "tsc --noEmit",
|
|
34
36
|
"test": "bun test --preload ./test-setup.ts",
|
|
35
37
|
"test:unit": "node scripts/test-unit.js",
|
|
@@ -66,25 +68,19 @@
|
|
|
66
68
|
"access": "public"
|
|
67
69
|
},
|
|
68
70
|
"devDependencies": {
|
|
69
|
-
"@
|
|
71
|
+
"@biomejs/biome": "^2.3.13",
|
|
70
72
|
"@types/express": "^5.0.0",
|
|
71
73
|
"@types/node": "^24.5.2",
|
|
72
74
|
"@types/react": "^19.1.14",
|
|
73
75
|
"@types/ws": "^8.18.1",
|
|
74
|
-
"@typescript-eslint/eslint-plugin": "^8.44.1",
|
|
75
|
-
"@typescript-eslint/parser": "^8.44.1",
|
|
76
76
|
"@vercel/node": "^3.2.23",
|
|
77
77
|
"dotenv-cli": "^7.4.2",
|
|
78
|
-
"eslint": "^9.36.0",
|
|
79
|
-
"eslint-config-prettier": "^10.1.8",
|
|
80
|
-
"eslint-plugin-prettier": "^5.5.4",
|
|
81
78
|
"ignore": "^7.0.5",
|
|
82
79
|
"json-schema-to-typescript": "^15.0.4",
|
|
83
|
-
"
|
|
80
|
+
"oxlint": "^1.42.0",
|
|
84
81
|
"ts-node": "^10.9.2",
|
|
85
82
|
"tsx": "^4.20.6",
|
|
86
|
-
"typescript": "^5.9.2"
|
|
87
|
-
"typescript-eslint": "^8.44.1"
|
|
83
|
+
"typescript": "^5.9.2"
|
|
88
84
|
},
|
|
89
85
|
"dependencies": {
|
|
90
86
|
"@desplega.ai/localtunnel": "^2.2.0",
|