@mobilewright/driver-mobilenext 0.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/dist/driver.d.ts +47 -0
- package/dist/driver.d.ts.map +1 -0
- package/dist/driver.js +355 -0
- package/dist/driver.js.map +1 -0
- package/dist/example.d.ts +2 -0
- package/dist/example.d.ts.map +1 -0
- package/dist/example.js +39 -0
- package/dist/example.js.map +1 -0
- package/dist/git-info.d.ts +10 -0
- package/dist/git-info.d.ts.map +1 -0
- package/dist/git-info.js +142 -0
- package/dist/git-info.js.map +1 -0
- package/dist/git-info.test.d.ts +2 -0
- package/dist/git-info.test.d.ts.map +1 -0
- package/dist/git-info.test.js +107 -0
- package/dist/git-info.test.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -0
- package/dist/rpc-client.d.ts +36 -0
- package/dist/rpc-client.d.ts.map +1 -0
- package/dist/rpc-client.js +152 -0
- package/dist/rpc-client.js.map +1 -0
- package/dist/upload-client.d.ts +13 -0
- package/dist/upload-client.d.ts.map +1 -0
- package/dist/upload-client.js +110 -0
- package/dist/upload-client.js.map +1 -0
- package/dist/upload-client.test.d.ts +2 -0
- package/dist/upload-client.test.d.ts.map +1 -0
- package/dist/upload-client.test.js +233 -0
- package/dist/upload-client.test.js.map +1 -0
- package/package.json +40 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"git-info.test.d.ts","sourceRoot":"","sources":["../src/git-info.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { test, expect } from '@playwright/test';
|
|
2
|
+
import { getGitInfo, normalizeRepoUrl } from './git-info.js';
|
|
3
|
+
const ALL_CI_KEYS = [
|
|
4
|
+
'GITHUB_ACTIONS', 'GITLAB_CI', 'JENKINS_URL',
|
|
5
|
+
'CIRCLECI', 'TRAVIS', 'TF_BUILD', 'BITBUCKET_PIPELINE_UUID',
|
|
6
|
+
];
|
|
7
|
+
function withCIEnv(vars, fn) {
|
|
8
|
+
const saved = {};
|
|
9
|
+
const keysToManage = [...new Set([...ALL_CI_KEYS, ...Object.keys(vars)])];
|
|
10
|
+
for (const key of keysToManage) {
|
|
11
|
+
saved[key] = process.env[key];
|
|
12
|
+
delete process.env[key];
|
|
13
|
+
}
|
|
14
|
+
for (const [key, value] of Object.entries(vars)) {
|
|
15
|
+
process.env[key] = value;
|
|
16
|
+
}
|
|
17
|
+
try {
|
|
18
|
+
fn();
|
|
19
|
+
}
|
|
20
|
+
finally {
|
|
21
|
+
for (const key of keysToManage) {
|
|
22
|
+
if (saved[key] === undefined) {
|
|
23
|
+
delete process.env[key];
|
|
24
|
+
}
|
|
25
|
+
else {
|
|
26
|
+
process.env[key] = saved[key];
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
test('normalizeRepoUrl converts SSH git@ URL to HTTPS', () => {
|
|
32
|
+
expect(normalizeRepoUrl('git@github.com:org/repo.git')).toBe('https://github.com/org/repo');
|
|
33
|
+
});
|
|
34
|
+
test('normalizeRepoUrl converts ssh:// URL to HTTPS', () => {
|
|
35
|
+
expect(normalizeRepoUrl('ssh://git@github.com/org/repo.git')).toBe('https://github.com/org/repo');
|
|
36
|
+
});
|
|
37
|
+
test('normalizeRepoUrl strips .git suffix from HTTPS URL', () => {
|
|
38
|
+
expect(normalizeRepoUrl('https://github.com/org/repo.git')).toBe('https://github.com/org/repo');
|
|
39
|
+
});
|
|
40
|
+
test('normalizeRepoUrl leaves plain HTTPS URL unchanged', () => {
|
|
41
|
+
expect(normalizeRepoUrl('https://github.com/org/repo')).toBe('https://github.com/org/repo');
|
|
42
|
+
});
|
|
43
|
+
test('getGitInfo reads GitHub Actions environment variables', () => {
|
|
44
|
+
withCIEnv({
|
|
45
|
+
GITHUB_ACTIONS: 'true',
|
|
46
|
+
GITHUB_REPOSITORY: 'myorg/myrepo',
|
|
47
|
+
GITHUB_SHA: 'abc123def456',
|
|
48
|
+
GITHUB_REF_NAME: 'main',
|
|
49
|
+
GITHUB_ACTOR: 'octocat',
|
|
50
|
+
GITHUB_COMMIT_MESSAGE: 'feat: add feature',
|
|
51
|
+
}, () => {
|
|
52
|
+
const info = getGitInfo();
|
|
53
|
+
expect(info.repoUrl).toBe('https://github.com/myorg/myrepo');
|
|
54
|
+
expect(info.commitSha).toBe('abc123def456');
|
|
55
|
+
expect(info.branch).toBe('main');
|
|
56
|
+
expect(info.authorName).toBe('octocat');
|
|
57
|
+
expect(info.commitMessage).toBe('feat: add feature');
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
test('getGitInfo reads GitLab CI environment variables', () => {
|
|
61
|
+
withCIEnv({
|
|
62
|
+
GITLAB_CI: 'true',
|
|
63
|
+
CI_PROJECT_URL: 'https://gitlab.com/myorg/myrepo',
|
|
64
|
+
CI_COMMIT_SHA: 'deadbeef',
|
|
65
|
+
CI_COMMIT_REF_NAME: 'feature-branch',
|
|
66
|
+
GITLAB_USER_NAME: 'alice',
|
|
67
|
+
CI_COMMIT_MESSAGE: 'fix: bug',
|
|
68
|
+
}, () => {
|
|
69
|
+
const info = getGitInfo();
|
|
70
|
+
expect(info.repoUrl).toBe('https://gitlab.com/myorg/myrepo');
|
|
71
|
+
expect(info.commitSha).toBe('deadbeef');
|
|
72
|
+
expect(info.branch).toBe('feature-branch');
|
|
73
|
+
expect(info.authorName).toBe('alice');
|
|
74
|
+
expect(info.commitMessage).toBe('fix: bug');
|
|
75
|
+
});
|
|
76
|
+
});
|
|
77
|
+
test('getGitInfo reads Azure DevOps environment variables and strips refs/heads/ prefix', () => {
|
|
78
|
+
withCIEnv({
|
|
79
|
+
TF_BUILD: 'true',
|
|
80
|
+
BUILD_REPOSITORY_URI: 'https://dev.azure.com/org/project/_git/repo',
|
|
81
|
+
BUILD_SOURCEBRANCH: 'refs/heads/main',
|
|
82
|
+
BUILD_SOURCEVERSION: 'abc123',
|
|
83
|
+
BUILD_REQUESTEDFOR: 'Bob',
|
|
84
|
+
BUILD_SOURCEVERSIONMESSAGE: 'chore: update deps',
|
|
85
|
+
}, () => {
|
|
86
|
+
const info = getGitInfo();
|
|
87
|
+
expect(info.branch).toBe('main');
|
|
88
|
+
expect(info.repoUrl).toBe('https://dev.azure.com/org/project/_git/repo');
|
|
89
|
+
expect(info.authorName).toBe('Bob');
|
|
90
|
+
});
|
|
91
|
+
});
|
|
92
|
+
test('getGitInfo falls back to local git when no CI env vars are set', () => {
|
|
93
|
+
// This test runs inside the mobilewright git repo, so local git should work.
|
|
94
|
+
withCIEnv({}, () => {
|
|
95
|
+
const info = getGitInfo();
|
|
96
|
+
expect(typeof info.branch).toBe('string');
|
|
97
|
+
expect(typeof info.commitSha).toBe('string');
|
|
98
|
+
expect(info.commitSha).toHaveLength(40);
|
|
99
|
+
});
|
|
100
|
+
});
|
|
101
|
+
test('getGitInfo returns empty object when not in a git repo and no CI env vars', () => {
|
|
102
|
+
// Simulate a non-git environment by checking we get an object (may be empty)
|
|
103
|
+
const info = getGitInfo();
|
|
104
|
+
expect(typeof info).toBe('object');
|
|
105
|
+
expect(info).not.toBeNull();
|
|
106
|
+
});
|
|
107
|
+
//# sourceMappingURL=git-info.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"git-info.test.js","sourceRoot":"","sources":["../src/git-info.test.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,WAAW,EAAE,KAAK,uBAAuB,EAAE,KAAK,oBAAoB,EAAE,MAAM,aAAa,CAAC;AACrH,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,WAAW,EAA2D,MAAM,aAAa,CAAC;AACrH,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
export declare class RpcError extends Error {
|
|
2
|
+
readonly code: number;
|
|
3
|
+
readonly data?: unknown | undefined;
|
|
4
|
+
constructor(message: string, code: number, data?: unknown | undefined);
|
|
5
|
+
}
|
|
6
|
+
export interface JsonRpcRequest {
|
|
7
|
+
jsonrpc: '2.0';
|
|
8
|
+
id: number;
|
|
9
|
+
method: string;
|
|
10
|
+
params?: Record<string, unknown>;
|
|
11
|
+
}
|
|
12
|
+
export interface JsonRpcResponse {
|
|
13
|
+
jsonrpc: '2.0';
|
|
14
|
+
id: number;
|
|
15
|
+
result?: unknown;
|
|
16
|
+
error?: {
|
|
17
|
+
code: number;
|
|
18
|
+
message: string;
|
|
19
|
+
data?: unknown;
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
export declare class RpcClient {
|
|
23
|
+
private url;
|
|
24
|
+
private requestTimeout;
|
|
25
|
+
private ws;
|
|
26
|
+
private nextId;
|
|
27
|
+
private pending;
|
|
28
|
+
private connectionPromise;
|
|
29
|
+
constructor(url: string, requestTimeout?: number);
|
|
30
|
+
connect(): Promise<void>;
|
|
31
|
+
call<T = unknown>(method: string, params?: Record<string, unknown>): Promise<T>;
|
|
32
|
+
disconnect(): Promise<void>;
|
|
33
|
+
get isConnected(): boolean;
|
|
34
|
+
private handleMessage;
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=rpc-client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rpc-client.d.ts","sourceRoot":"","sources":["../src/rpc-client.ts"],"names":[],"mappings":"AAOA,qBAAa,QAAS,SAAQ,KAAK;IAG/B,QAAQ,CAAC,IAAI,EAAE,MAAM;IACrB,QAAQ,CAAC,IAAI,CAAC,EAAE,OAAO;gBAFvB,OAAO,EAAE,MAAM,EACN,IAAI,EAAE,MAAM,EACZ,IAAI,CAAC,EAAE,OAAO,YAAA;CAK1B;AAED,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,KAAK,CAAC;IACf,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAClC;AAED,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,KAAK,CAAC;IACf,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC;CAC3D;AAQD,qBAAa,SAAS;IAOlB,OAAO,CAAC,GAAG;IACX,OAAO,CAAC,cAAc;IAPxB,OAAO,CAAC,EAAE,CAA0B;IACpC,OAAO,CAAC,MAAM,CAAK;IACnB,OAAO,CAAC,OAAO,CAAqC;IACpD,OAAO,CAAC,iBAAiB,CAA8B;gBAG7C,GAAG,EAAE,MAAM,EACX,cAAc,SAAS;IAG3B,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAmExB,IAAI,CAAC,CAAC,GAAG,OAAO,EACpB,MAAM,EAAE,MAAM,EACd,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC/B,OAAO,CAAC,CAAC,CAAC;IAmCP,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAYjC,IAAI,WAAW,IAAI,OAAO,CAEzB;IAED,OAAO,CAAC,aAAa;CA4BtB"}
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import createDebug from 'debug';
|
|
2
|
+
import WebSocket from 'ws';
|
|
3
|
+
const debug = createDebug('mw:driver-mobilenext');
|
|
4
|
+
const WS_CLOSE_NORMAL = 1000;
|
|
5
|
+
export class RpcError extends Error {
|
|
6
|
+
constructor(message, code, data) {
|
|
7
|
+
super(message);
|
|
8
|
+
this.code = code;
|
|
9
|
+
this.data = data;
|
|
10
|
+
this.name = 'RpcError';
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
export class RpcClient {
|
|
14
|
+
constructor(url, requestTimeout = 30_000) {
|
|
15
|
+
this.url = url;
|
|
16
|
+
this.requestTimeout = requestTimeout;
|
|
17
|
+
this.ws = null;
|
|
18
|
+
this.nextId = 1;
|
|
19
|
+
this.pending = new Map();
|
|
20
|
+
this.connectionPromise = null;
|
|
21
|
+
}
|
|
22
|
+
async connect() {
|
|
23
|
+
if (this.ws?.readyState === WebSocket.OPEN) {
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
if (this.connectionPromise) {
|
|
27
|
+
return this.connectionPromise;
|
|
28
|
+
}
|
|
29
|
+
this.connectionPromise = new Promise((resolve, reject) => {
|
|
30
|
+
let settled = false;
|
|
31
|
+
const ws = new WebSocket(this.url);
|
|
32
|
+
const timeout = setTimeout(() => {
|
|
33
|
+
ws.terminate();
|
|
34
|
+
if (!settled) {
|
|
35
|
+
settled = true;
|
|
36
|
+
reject(new Error(`Connection to ${this.url} timed out`));
|
|
37
|
+
}
|
|
38
|
+
}, this.requestTimeout);
|
|
39
|
+
ws.on('open', () => {
|
|
40
|
+
clearTimeout(timeout);
|
|
41
|
+
this.ws = ws;
|
|
42
|
+
this.connectionPromise = null;
|
|
43
|
+
settled = true;
|
|
44
|
+
resolve();
|
|
45
|
+
});
|
|
46
|
+
ws.on('error', (err) => {
|
|
47
|
+
clearTimeout(timeout);
|
|
48
|
+
this.connectionPromise = null;
|
|
49
|
+
if (!settled) {
|
|
50
|
+
settled = true;
|
|
51
|
+
// Node may wrap connection failures in AggregateError (e.g. IPv4+IPv6).
|
|
52
|
+
// Unwrap to surface the actual error message.
|
|
53
|
+
if (err instanceof AggregateError && err.errors.length > 0) {
|
|
54
|
+
reject(new Error(`Failed to connect to ${this.url}: ${err.errors.map((e) => e.message).join('; ')}`));
|
|
55
|
+
}
|
|
56
|
+
else {
|
|
57
|
+
reject(new Error(`Failed to connect to ${this.url}: ${err.message}`));
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
ws.on('message', (data) => {
|
|
62
|
+
this.handleMessage(data);
|
|
63
|
+
});
|
|
64
|
+
ws.on('close', (code, reason) => {
|
|
65
|
+
this.ws = null;
|
|
66
|
+
const reasonStr = reason.toString() || 'no reason';
|
|
67
|
+
const msg = `WebSocket connection closed (code=${code}, reason=${reasonStr})`;
|
|
68
|
+
if (!settled) {
|
|
69
|
+
clearTimeout(timeout);
|
|
70
|
+
this.connectionPromise = null;
|
|
71
|
+
settled = true;
|
|
72
|
+
reject(new Error(msg));
|
|
73
|
+
}
|
|
74
|
+
for (const [id, req] of this.pending) {
|
|
75
|
+
clearTimeout(req.timer);
|
|
76
|
+
req.reject(new Error(msg));
|
|
77
|
+
this.pending.delete(id);
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
});
|
|
81
|
+
return this.connectionPromise;
|
|
82
|
+
}
|
|
83
|
+
async call(method, params) {
|
|
84
|
+
await this.connect();
|
|
85
|
+
const ws = this.ws;
|
|
86
|
+
if (!ws || ws.readyState !== WebSocket.OPEN) {
|
|
87
|
+
throw new Error('WebSocket is not connected');
|
|
88
|
+
}
|
|
89
|
+
const id = this.nextId++;
|
|
90
|
+
const request = {
|
|
91
|
+
jsonrpc: '2.0',
|
|
92
|
+
id,
|
|
93
|
+
method,
|
|
94
|
+
...(params !== undefined && { params }),
|
|
95
|
+
};
|
|
96
|
+
return new Promise((resolve, reject) => {
|
|
97
|
+
const timer = setTimeout(() => {
|
|
98
|
+
this.pending.delete(id);
|
|
99
|
+
reject(new Error(`RPC call "${method}" timed out after ${this.requestTimeout}ms`));
|
|
100
|
+
}, this.requestTimeout);
|
|
101
|
+
this.pending.set(id, {
|
|
102
|
+
resolve: resolve,
|
|
103
|
+
reject,
|
|
104
|
+
timer,
|
|
105
|
+
});
|
|
106
|
+
const payload = JSON.stringify(request);
|
|
107
|
+
debug('rpc send %s %o', method, request.params);
|
|
108
|
+
ws.send(payload);
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
async disconnect() {
|
|
112
|
+
const ws = this.ws;
|
|
113
|
+
if (!ws) {
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
this.ws = null;
|
|
117
|
+
return new Promise((resolve) => {
|
|
118
|
+
ws.once('close', () => resolve());
|
|
119
|
+
ws.close(WS_CLOSE_NORMAL);
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
get isConnected() {
|
|
123
|
+
return this.ws?.readyState === WebSocket.OPEN;
|
|
124
|
+
}
|
|
125
|
+
handleMessage(data) {
|
|
126
|
+
let response;
|
|
127
|
+
try {
|
|
128
|
+
response = JSON.parse(data.toString());
|
|
129
|
+
}
|
|
130
|
+
catch {
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
const pending = this.pending.get(response.id);
|
|
134
|
+
if (!pending) {
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
this.pending.delete(response.id);
|
|
138
|
+
clearTimeout(pending.timer);
|
|
139
|
+
if (response.error) {
|
|
140
|
+
debug('rpc recv error id=%d code=%d %s', response.id, response.error.code, response.error.message);
|
|
141
|
+
const detail = typeof response.error.data === 'string'
|
|
142
|
+
? response.error.data
|
|
143
|
+
: response.error.message;
|
|
144
|
+
pending.reject(new RpcError(detail, response.error.code, response.error.data));
|
|
145
|
+
}
|
|
146
|
+
else {
|
|
147
|
+
debug('rpc recv result id=%d %o', response.id, response.result);
|
|
148
|
+
pending.resolve(response.result);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
//# sourceMappingURL=rpc-client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rpc-client.js","sourceRoot":"","sources":["../src/rpc-client.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export interface UploadTestResultParams {
|
|
2
|
+
apiKey: string;
|
|
3
|
+
jsonResultsPath: string;
|
|
4
|
+
outputDir: string;
|
|
5
|
+
name?: string;
|
|
6
|
+
tags?: string[];
|
|
7
|
+
environment?: string;
|
|
8
|
+
_fetchFn?: typeof fetch;
|
|
9
|
+
}
|
|
10
|
+
export declare function uploadTestResult(params: UploadTestResultParams): Promise<{
|
|
11
|
+
url: string;
|
|
12
|
+
}>;
|
|
13
|
+
//# sourceMappingURL=upload-client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"upload-client.d.ts","sourceRoot":"","sources":["../src/upload-client.ts"],"names":[],"mappings":"AAYA,MAAM,WAAW,sBAAsB;IACrC,MAAM,EAAE,MAAM,CAAC;IACf,eAAe,EAAE,MAAM,CAAC;IACxB,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,OAAO,KAAK,CAAC;CACzB;AA6ED,wBAAsB,gBAAgB,CAAC,MAAM,EAAE,sBAAsB,GAAG,OAAO,CAAC;IAAE,GAAG,EAAE,MAAM,CAAA;CAAE,CAAC,CA4D/F"}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { readFileSync } from 'node:fs';
|
|
2
|
+
import { createRequire } from 'node:module';
|
|
3
|
+
import { randomUUID } from 'node:crypto';
|
|
4
|
+
import createDebug from 'debug';
|
|
5
|
+
import { getGitInfo } from './git-info.js';
|
|
6
|
+
const _require = createRequire(import.meta.url);
|
|
7
|
+
const debug = createDebug('mw:reporter:upload');
|
|
8
|
+
const BASE_URL = 'https://api.mobilenext.ai';
|
|
9
|
+
const DASHBOARD_BASE_URL = 'https://app.mobilenext.ai';
|
|
10
|
+
function extensionForContentType(contentType) {
|
|
11
|
+
const extensions = {
|
|
12
|
+
'image/png': 'png',
|
|
13
|
+
'image/jpeg': 'jpg',
|
|
14
|
+
'image/webp': 'webp',
|
|
15
|
+
'image/gif': 'gif',
|
|
16
|
+
};
|
|
17
|
+
return extensions[contentType] ?? 'bin';
|
|
18
|
+
}
|
|
19
|
+
async function uploadAttachmentBodies(obj, testResultId, apiKey, fetchFn) {
|
|
20
|
+
if (!obj || typeof obj !== 'object') {
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
if (Array.isArray(obj)) {
|
|
24
|
+
for (const item of obj) {
|
|
25
|
+
await uploadAttachmentBodies(item, testResultId, apiKey, fetchFn);
|
|
26
|
+
}
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
const record = obj;
|
|
30
|
+
if (Array.isArray(record['attachments'])) {
|
|
31
|
+
for (const att of record['attachments']) {
|
|
32
|
+
if (typeof att['body'] === 'string') {
|
|
33
|
+
const contentType = typeof att['contentType'] === 'string' ? att['contentType'] : 'application/octet-stream';
|
|
34
|
+
const ext = extensionForContentType(contentType);
|
|
35
|
+
const assetName = `${randomUUID()}.${ext}`;
|
|
36
|
+
const buffer = Buffer.from(att['body'], 'base64');
|
|
37
|
+
const sizeKB = (buffer.length / 1024).toFixed(1);
|
|
38
|
+
debug('uploading attachment name=%s contentType=%s size=%skB as %s', att['name'], contentType, sizeKB, assetName);
|
|
39
|
+
const form = new FormData();
|
|
40
|
+
form.append('name', assetName);
|
|
41
|
+
form.append('file', new Blob([buffer], { type: contentType }), assetName);
|
|
42
|
+
const res = await fetchFn(`${BASE_URL}/api/v1/test-results/${testResultId}/assets`, {
|
|
43
|
+
method: 'POST',
|
|
44
|
+
headers: { 'Authorization': `Bearer ${apiKey}` },
|
|
45
|
+
body: form,
|
|
46
|
+
});
|
|
47
|
+
if (!res.ok) {
|
|
48
|
+
throw new Error(`Failed to upload attachment "${att['name']}": ${res.status}`);
|
|
49
|
+
}
|
|
50
|
+
const asset = await res.json();
|
|
51
|
+
delete att['body'];
|
|
52
|
+
att['assetId'] = asset.id;
|
|
53
|
+
debug('attachment uploaded assetId=%s', asset.id);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
for (const value of Object.values(record)) {
|
|
58
|
+
await uploadAttachmentBodies(value, testResultId, apiKey, fetchFn);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
export async function uploadTestResult(params) {
|
|
62
|
+
const fetchFn = params._fetchFn ?? fetch;
|
|
63
|
+
const pkg = _require('../package.json');
|
|
64
|
+
const userAgent = `mobilewright/${pkg.version}`;
|
|
65
|
+
const gitInfo = getGitInfo();
|
|
66
|
+
const hasGitInfo = Object.values(gitInfo).some(v => v !== undefined);
|
|
67
|
+
debug('creating test result name=%s userAgent=%s', params.name ?? 'Test Run', userAgent);
|
|
68
|
+
const createRes = await fetchFn(`${BASE_URL}/api/v1/test-results`, {
|
|
69
|
+
method: 'POST',
|
|
70
|
+
headers: {
|
|
71
|
+
'Authorization': `Bearer ${params.apiKey}`,
|
|
72
|
+
'Content-Type': 'application/json',
|
|
73
|
+
},
|
|
74
|
+
body: JSON.stringify({
|
|
75
|
+
name: params.name ?? 'Test Run',
|
|
76
|
+
userAgent,
|
|
77
|
+
...(hasGitInfo ? { git: gitInfo } : {}),
|
|
78
|
+
}),
|
|
79
|
+
});
|
|
80
|
+
if (!createRes.ok) {
|
|
81
|
+
throw new Error(`Failed to create test result: ${createRes.status}`);
|
|
82
|
+
}
|
|
83
|
+
const testResult = await createRes.json();
|
|
84
|
+
debug('test result created id=%s', testResult.id);
|
|
85
|
+
// Parse into a fresh in-memory copy — original file on disk is never modified
|
|
86
|
+
const rawJson = readFileSync(params.jsonResultsPath);
|
|
87
|
+
const report = JSON.parse(rawJson.toString());
|
|
88
|
+
await uploadAttachmentBodies(report, testResult.id, params.apiKey, fetchFn);
|
|
89
|
+
const modifiedJson = JSON.stringify(report);
|
|
90
|
+
const modifiedBuffer = Buffer.from(modifiedJson);
|
|
91
|
+
const fileSizeKB = (modifiedBuffer.length / 1024).toFixed(1);
|
|
92
|
+
debug('uploading report.json size=%skB path=%s', fileSizeKB, params.jsonResultsPath);
|
|
93
|
+
const form = new FormData();
|
|
94
|
+
form.append('name', 'report.json');
|
|
95
|
+
form.append('file', new Blob([modifiedBuffer], { type: 'application/json' }), 'report.json');
|
|
96
|
+
const progressTimer = setInterval(() => {
|
|
97
|
+
debug('still uploading report.json...');
|
|
98
|
+
}, 10_000);
|
|
99
|
+
const uploadRes = await fetchFn(`${BASE_URL}/api/v1/test-results/${testResult.id}/assets`, {
|
|
100
|
+
method: 'POST',
|
|
101
|
+
headers: { 'Authorization': `Bearer ${params.apiKey}` },
|
|
102
|
+
body: form,
|
|
103
|
+
}).finally(() => clearInterval(progressTimer));
|
|
104
|
+
if (!uploadRes.ok) {
|
|
105
|
+
throw new Error(`Failed to upload report.json: ${uploadRes.status}`);
|
|
106
|
+
}
|
|
107
|
+
debug('upload complete url=%s', `${DASHBOARD_BASE_URL}/dashboard/test-results/${testResult.id}`);
|
|
108
|
+
return { url: `${DASHBOARD_BASE_URL}/dashboard/test-results/${testResult.id}` };
|
|
109
|
+
}
|
|
110
|
+
//# sourceMappingURL=upload-client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"upload-client.js","sourceRoot":"","sources":["../src/upload-client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,WAAW,MAAM,OAAO,CAAC;AAChC,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAE3C,MAAM,QAAQ,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAChD,MAAM,KAAK,GAAG,WAAW,CAAC,oBAAoB,CAAC,CAAC;AAEhD,MAAM,QAAQ,GAAG,2BAA2B,CAAC;AAC7C,MAAM,kBAAkB,GAAG,2BAA2B,CAAC;AA2BvD,SAAS,uBAAuB,CAAC,WAAmB;IAClD,MAAM,UAAU,GAA2B;QACzC,WAAW,EAAE,KAAK;QAClB,YAAY,EAAE,KAAK;QACnB,YAAY,EAAE,MAAM;QACpB,WAAW,EAAE,KAAK;KACnB,CAAC;IACF,OAAO,UAAU,CAAC,WAAW,CAAC,IAAI,KAAK,CAAC;AAC1C,CAAC;AAED,KAAK,UAAU,sBAAsB,CACnC,GAAY,EACZ,YAAoB,EACpB,MAAc,EACd,OAAqB;IAErB,IAAI,CAAC,GAAG,IAAI,OAAO,GAAG,KAAK,QAAQ,EAAE,CAAC;QAAC,OAAO;IAAC,CAAC;IAChD,IAAI,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;QACvB,KAAK,MAAM,IAAI,IAAI,GAAG,EAAE,CAAC;YACvB,MAAM,sBAAsB,CAAC,IAAI,EAAE,YAAY,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC;QACpE,CAAC;QACD,OAAO;IACT,CAAC;IACD,MAAM,MAAM,GAAG,GAA8B,CAAC;IAC9C,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC,EAAE,CAAC;QACzC,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,aAAa,CAA8B,EAAE,CAAC;YACrE,IAAI,OAAO,GAAG,CAAC,MAAM,CAAC,KAAK,QAAQ,EAAE,CAAC;gBACpC,MAAM,WAAW,GAAG,OAAO,GAAG,CAAC,aAAa,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,0BAA0B,CAAC;gBAC7G,MAAM,GAAG,GAAG,uBAAuB,CAAC,WAAW,CAAC,CAAC;gBACjD,MAAM,SAAS,GAAG,GAAG,UAAU,EAAE,IAAI,GAAG,EAAE,CAAC;gBAC3C,MAAM,MAAM,GAAG,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,QAAQ,CAAC,CAAC;gBAClD,MAAM,MAAM,GAAG,CAAC,MAAM,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;gBACjD,KAAK,CAAC,6DAA6D,EAAE,GAAG,CAAC,MAAM,CAAC,EAAE,WAAW,EAAE,MAAM,EAAE,SAAS,CAAC,CAAC;gBAElH,MAAM,IAAI,GAAG,IAAI,QAAQ,EAAE,CAAC;gBAC5B,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;gBAC/B,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,IAAI,IAAI,CAAC,CAAC,MAAM,CAAC,EAAE,EAAE,IAAI,EAAE,WAAW,EAAE,CAAC,EAAE,SAAS,CAAC,CAAC;gBAE1E,MAAM,GAAG,GAAG,MAAM,OAAO,CAAC,GAAG,QAAQ,wBAAwB,YAAY,SAAS,EAAE;oBAClF,MAAM,EAAE,MAAM;oBACd,OAAO,EAAE,EAAE,eAAe,EAAE,UAAU,MAAM,EAAE,EAAE;oBAChD,IAAI,EAAE,IAAI;iBACX,CAAC,CAAC;gBAEH,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;oBACZ,MAAM,IAAI,KAAK,CAAC,gCAAgC,GAAG,CAAC,MAAM,CAAW,MAAM,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC;gBAC3F,CAAC;gBAED,MAAM,KAAK,GAAG,MAAM,GAAG,CAAC,IAAI,EAAmB,CAAC;gBAChD,OAAO,GAAG,CAAC,MAAM,CAAC,CAAC;gBACnB,GAAG,CAAC,SAAS,CAAC,GAAG,KAAK,CAAC,EAAE,CAAC;gBAC1B,KAAK,CAAC,gCAAgC,EAAE,KAAK,CAAC,EAAE,CAAC,CAAC;YACpD,CAAC;QACH,CAAC;IACH,CAAC;IACD,KAAK,MAAM,KAAK,IAAI,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC;QAC1C,MAAM,sBAAsB,CAAC,KAAK,EAAE,YAAY,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC;IACrE,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,MAA8B;IACnE,MAAM,OAAO,GAAG,MAAM,CAAC,QAAQ,IAAI,KAAK,CAAC;IACzC,MAAM,GAAG,GAAG,QAAQ,CAAC,iBAAiB,CAAwB,CAAC;IAC/D,MAAM,SAAS,GAAG,gBAAgB,GAAG,CAAC,OAAO,EAAE,CAAC;IAEhD,MAAM,OAAO,GAAG,UAAU,EAAE,CAAC;IAC7B,MAAM,UAAU,GAAG,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,KAAK,SAAS,CAAC,CAAC;IAErE,KAAK,CAAC,2CAA2C,EAAE,MAAM,CAAC,IAAI,IAAI,UAAU,EAAE,SAAS,CAAC,CAAC;IACzF,MAAM,SAAS,GAAG,MAAM,OAAO,CAAC,GAAG,QAAQ,sBAAsB,EAAE;QACjE,MAAM,EAAE,MAAM;QACd,OAAO,EAAE;YACP,eAAe,EAAE,UAAU,MAAM,CAAC,MAAM,EAAE;YAC1C,cAAc,EAAE,kBAAkB;SACnC;QACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;YACnB,IAAI,EAAE,MAAM,CAAC,IAAI,IAAI,UAAU;YAC/B,SAAS;YACT,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SACxC,CAAC;KACH,CAAC,CAAC;IAEH,IAAI,CAAC,SAAS,CAAC,EAAE,EAAE,CAAC;QAClB,MAAM,IAAI,KAAK,CAAC,iCAAiC,SAAS,CAAC,MAAM,EAAE,CAAC,CAAC;IACvE,CAAC;IAED,MAAM,UAAU,GAAG,MAAM,SAAS,CAAC,IAAI,EAAwB,CAAC;IAChE,KAAK,CAAC,2BAA2B,EAAE,UAAU,CAAC,EAAE,CAAC,CAAC;IAElD,8EAA8E;IAC9E,MAAM,OAAO,GAAG,YAAY,CAAC,MAAM,CAAC,eAAe,CAAC,CAAC;IACrD,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,QAAQ,EAAE,CAA4B,CAAC;IAEzE,MAAM,sBAAsB,CAAC,MAAM,EAAE,UAAU,CAAC,EAAE,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAE5E,MAAM,YAAY,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;IAC5C,MAAM,cAAc,GAAG,MAAM,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;IACjD,MAAM,UAAU,GAAG,CAAC,cAAc,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;IAC7D,KAAK,CAAC,yCAAyC,EAAE,UAAU,EAAE,MAAM,CAAC,eAAe,CAAC,CAAC;IAErF,MAAM,IAAI,GAAG,IAAI,QAAQ,EAAE,CAAC;IAC5B,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;IACnC,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,IAAI,IAAI,CAAC,CAAC,cAAc,CAAC,EAAE,EAAE,IAAI,EAAE,kBAAkB,EAAE,CAAC,EAAE,aAAa,CAAC,CAAC;IAE7F,MAAM,aAAa,GAAG,WAAW,CAAC,GAAG,EAAE;QACrC,KAAK,CAAC,gCAAgC,CAAC,CAAC;IAC1C,CAAC,EAAE,MAAM,CAAC,CAAC;IAEX,MAAM,SAAS,GAAG,MAAM,OAAO,CAAC,GAAG,QAAQ,wBAAwB,UAAU,CAAC,EAAE,SAAS,EAAE;QACzF,MAAM,EAAE,MAAM;QACd,OAAO,EAAE,EAAE,eAAe,EAAE,UAAU,MAAM,CAAC,MAAM,EAAE,EAAE;QACvD,IAAI,EAAE,IAAI;KACX,CAAC,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,aAAa,CAAC,aAAa,CAAC,CAAC,CAAC;IAE/C,IAAI,CAAC,SAAS,CAAC,EAAE,EAAE,CAAC;QAClB,MAAM,IAAI,KAAK,CAAC,iCAAiC,SAAS,CAAC,MAAM,EAAE,CAAC,CAAC;IACvE,CAAC;IAED,KAAK,CAAC,wBAAwB,EAAE,GAAG,kBAAkB,2BAA2B,UAAU,CAAC,EAAE,EAAE,CAAC,CAAC;IACjG,OAAO,EAAE,GAAG,EAAE,GAAG,kBAAkB,2BAA2B,UAAU,CAAC,EAAE,EAAE,EAAE,CAAC;AAClF,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"upload-client.test.d.ts","sourceRoot":"","sources":["../src/upload-client.test.ts"],"names":[],"mappings":""}
|