@muuktest/amikoo-reporter 1.1.1 → 1.2.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/amikooArtifacts.d.ts +36 -0
- package/dist/amikooArtifacts.d.ts.map +1 -1
- package/dist/amikooArtifacts.js +124 -6
- package/dist/amikooArtifacts.js.map +1 -1
- package/dist/controlHubReporter.d.ts +1 -1
- package/dist/controlHubReporter.d.ts.map +1 -1
- package/dist/controlHubReporter.js +19 -12
- package/dist/controlHubReporter.js.map +1 -1
- package/dist/s3Upload.d.ts +33 -0
- package/dist/s3Upload.d.ts.map +1 -1
- package/dist/s3Upload.js +43 -2
- package/dist/s3Upload.js.map +1 -1
- package/dist/videoUtils.d.ts +44 -3
- package/dist/videoUtils.d.ts.map +1 -1
- package/dist/videoUtils.js +111 -25
- package/dist/videoUtils.js.map +1 -1
- package/package.json +1 -1
|
@@ -11,12 +11,48 @@ interface UploadOptions {
|
|
|
11
11
|
signedUrlEndpoint?: string;
|
|
12
12
|
repoRoot: string;
|
|
13
13
|
}
|
|
14
|
+
/**
|
|
15
|
+
* Scans a Playwright TestResult for attachments tagged as amikoo artifacts
|
|
16
|
+
* and converts them into `AmikooArtifact` records the uploader understands.
|
|
17
|
+
*
|
|
18
|
+
* Convention: any attachment whose `name` starts with `amikoo-` is treated as
|
|
19
|
+
* a failure-data artifact. The `amikoo-` prefix is stripped from the name so
|
|
20
|
+
* the file lands in the zip with its "real" name (e.g. `amikoo-trace.json`
|
|
21
|
+
* becomes `failure-data/trace.json`).
|
|
22
|
+
*
|
|
23
|
+
* @param result - Playwright TestResult with `attachments`.
|
|
24
|
+
* @param testId - Hash identifying the test (used as the grouping key when
|
|
25
|
+
* multiple artifacts belong to the same test).
|
|
26
|
+
* @param fullTitle - Full test title including parent describe blocks; used
|
|
27
|
+
* to derive a human-readable slug for the S3 path.
|
|
28
|
+
* @param specFile - Absolute path to the .spec file; included so the
|
|
29
|
+
* uploader can also zip the test source + its imports.
|
|
30
|
+
* @returns Zero or more artifact records ready to be queued for upload.
|
|
31
|
+
*/
|
|
14
32
|
export declare function collectAmikooArtifacts(result: {
|
|
15
33
|
attachments?: Array<{
|
|
16
34
|
name?: string;
|
|
17
35
|
path?: string;
|
|
18
36
|
}>;
|
|
19
37
|
}, testId: string, fullTitle: string, specFile: string): AmikooArtifact[];
|
|
38
|
+
/**
|
|
39
|
+
* Top-level entry point: groups artifacts by test, then uploads each group
|
|
40
|
+
* to S3 in parallel (bounded by `ARTIFACT_UPLOAD_CONCURRENCY`).
|
|
41
|
+
*
|
|
42
|
+
* Flow:
|
|
43
|
+
* 1. Bail out early if there's nothing to upload or no `AMIKOO_KEY`.
|
|
44
|
+
* 2. Group artifacts by `hash` so all files for one test land in a single
|
|
45
|
+
* zip (one S3 object per test, not per attachment).
|
|
46
|
+
* 3. Spawn N worker promises that pull from a shared cursor; each worker
|
|
47
|
+
* processes one test's group end-to-end (zip → signed URL → upload →
|
|
48
|
+
* upsert backend record).
|
|
49
|
+
* 4. Collect per-test results and print a single summary line. Individual
|
|
50
|
+
* failures are logged but do not abort the batch.
|
|
51
|
+
*
|
|
52
|
+
* @param files - All artifacts collected across the whole run.
|
|
53
|
+
* @param opts - Backend token, amikoo key, optional endpoint override, and
|
|
54
|
+
* the repo root used when zipping test source files.
|
|
55
|
+
*/
|
|
20
56
|
export declare function uploadAmikooArtifacts(files: AmikooArtifact[], opts: UploadOptions): Promise<void>;
|
|
21
57
|
export {};
|
|
22
58
|
//# sourceMappingURL=amikooArtifacts.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"amikooArtifacts.d.ts","sourceRoot":"","sources":["../controlHub/amikooArtifacts.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"amikooArtifacts.d.ts","sourceRoot":"","sources":["../controlHub/amikooArtifacts.ts"],"names":[],"mappings":"AA2BA,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,MAAM,CAAC;IACvB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,UAAU,aAAa;IACrB,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,EAAE,MAAM,CAAC;CAClB;AA2BD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,sBAAsB,CACpC,MAAM,EAAE;IAAE,WAAW,CAAC,EAAE,KAAK,CAAC;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;CAAE,EACjE,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,MAAM,GACf,cAAc,EAAE,CAclB;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAsB,qBAAqB,CACzC,KAAK,EAAE,cAAc,EAAE,EACvB,IAAI,EAAE,aAAa,GAClB,OAAO,CAAC,IAAI,CAAC,CA4Cf"}
|
package/dist/amikooArtifacts.js
CHANGED
|
@@ -11,10 +11,39 @@ const os_1 = __importDefault(require("os"));
|
|
|
11
11
|
const apiUtils_1 = require("./apiUtils");
|
|
12
12
|
const zipUtils_1 = require("./zipUtils");
|
|
13
13
|
const s3Upload_1 = require("./s3Upload");
|
|
14
|
+
// How many artifact zips are built + uploaded to S3 in parallel.
|
|
15
|
+
//
|
|
16
|
+
// Each "worker" does: build zip on disk → request signed URL → PUT stream to S3.
|
|
17
|
+
// Higher values finish faster on fast networks but hold more open sockets,
|
|
18
|
+
// more `archiver` deflate state, and more disk I/O at once.
|
|
19
|
+
// Lower values (1–2) are gentler on memory and network — useful on
|
|
20
|
+
// constrained CI runners.
|
|
21
|
+
//
|
|
22
|
+
// Trade-off (per concurrent worker, approx):
|
|
23
|
+
// - 1 open HTTPS socket to S3 (TLS state ~1–2 MB)
|
|
24
|
+
// - 1 active `archiver` zip stream (small, but allocates deflate buffers)
|
|
25
|
+
// - 1 temp zip file open on disk (streamed, not buffered)
|
|
26
|
+
//
|
|
27
|
+
// Default: 10 (good balance for typical CI). Override with
|
|
28
|
+
// AMIKOO_ARTIFACT_UPLOAD_CONCURRENCY=<n>.
|
|
29
|
+
const ARTIFACT_UPLOAD_CONCURRENCY = Math.max(1, parseInt(process.env.AMIKOO_ARTIFACT_UPLOAD_CONCURRENCY || '', 10) || 10);
|
|
14
30
|
const SIGNED_URL_ENDPOINTS = {
|
|
15
31
|
prod: 'https://prgm4fborb.execute-api.us-east-2.amazonaws.com/prod/mcp/getSignedUrl',
|
|
16
32
|
staging: 'https://prgm4fborb.execute-api.us-east-2.amazonaws.com/staging/mcp/getSignedUrl',
|
|
17
33
|
};
|
|
34
|
+
/**
|
|
35
|
+
* Resolves the endpoint that hands out presigned S3 URLs for artifact uploads.
|
|
36
|
+
*
|
|
37
|
+
* Resolution order:
|
|
38
|
+
* 1. `AMIKOO_SIGNED_URL_ENDPOINT` env var, if set. Use this to point the
|
|
39
|
+
* reporter at a localhost endpoint (e.g. `http://localhost:3000/mcp/getSignedUrl`)
|
|
40
|
+
* or any custom deployment during development.
|
|
41
|
+
* 2. Otherwise, picks prod vs staging automatically based on which Control
|
|
42
|
+
* Hub URL the reporter is talking to (`getControlHubUrl()`).
|
|
43
|
+
*
|
|
44
|
+
* @returns Fully-qualified endpoint that returns a presigned S3 URL for
|
|
45
|
+
* uploading an artifact zip. May be localhost, staging, or prod.
|
|
46
|
+
*/
|
|
18
47
|
function resolveSignedUrlEndpoint() {
|
|
19
48
|
if (process.env.AMIKOO_SIGNED_URL_ENDPOINT)
|
|
20
49
|
return process.env.AMIKOO_SIGNED_URL_ENDPOINT;
|
|
@@ -22,6 +51,24 @@ function resolveSignedUrlEndpoint() {
|
|
|
22
51
|
? SIGNED_URL_ENDPOINTS.prod
|
|
23
52
|
: SIGNED_URL_ENDPOINTS.staging;
|
|
24
53
|
}
|
|
54
|
+
/**
|
|
55
|
+
* Scans a Playwright TestResult for attachments tagged as amikoo artifacts
|
|
56
|
+
* and converts them into `AmikooArtifact` records the uploader understands.
|
|
57
|
+
*
|
|
58
|
+
* Convention: any attachment whose `name` starts with `amikoo-` is treated as
|
|
59
|
+
* a failure-data artifact. The `amikoo-` prefix is stripped from the name so
|
|
60
|
+
* the file lands in the zip with its "real" name (e.g. `amikoo-trace.json`
|
|
61
|
+
* becomes `failure-data/trace.json`).
|
|
62
|
+
*
|
|
63
|
+
* @param result - Playwright TestResult with `attachments`.
|
|
64
|
+
* @param testId - Hash identifying the test (used as the grouping key when
|
|
65
|
+
* multiple artifacts belong to the same test).
|
|
66
|
+
* @param fullTitle - Full test title including parent describe blocks; used
|
|
67
|
+
* to derive a human-readable slug for the S3 path.
|
|
68
|
+
* @param specFile - Absolute path to the .spec file; included so the
|
|
69
|
+
* uploader can also zip the test source + its imports.
|
|
70
|
+
* @returns Zero or more artifact records ready to be queued for upload.
|
|
71
|
+
*/
|
|
25
72
|
function collectAmikooArtifacts(result, testId, fullTitle, specFile) {
|
|
26
73
|
const out = [];
|
|
27
74
|
for (const att of result.attachments || []) {
|
|
@@ -37,11 +84,29 @@ function collectAmikooArtifacts(result, testId, fullTitle, specFile) {
|
|
|
37
84
|
}
|
|
38
85
|
return out;
|
|
39
86
|
}
|
|
87
|
+
/**
|
|
88
|
+
* Top-level entry point: groups artifacts by test, then uploads each group
|
|
89
|
+
* to S3 in parallel (bounded by `ARTIFACT_UPLOAD_CONCURRENCY`).
|
|
90
|
+
*
|
|
91
|
+
* Flow:
|
|
92
|
+
* 1. Bail out early if there's nothing to upload or no `AMIKOO_KEY`.
|
|
93
|
+
* 2. Group artifacts by `hash` so all files for one test land in a single
|
|
94
|
+
* zip (one S3 object per test, not per attachment).
|
|
95
|
+
* 3. Spawn N worker promises that pull from a shared cursor; each worker
|
|
96
|
+
* processes one test's group end-to-end (zip → signed URL → upload →
|
|
97
|
+
* upsert backend record).
|
|
98
|
+
* 4. Collect per-test results and print a single summary line. Individual
|
|
99
|
+
* failures are logged but do not abort the batch.
|
|
100
|
+
*
|
|
101
|
+
* @param files - All artifacts collected across the whole run.
|
|
102
|
+
* @param opts - Backend token, amikoo key, optional endpoint override, and
|
|
103
|
+
* the repo root used when zipping test source files.
|
|
104
|
+
*/
|
|
40
105
|
async function uploadAmikooArtifacts(files, opts) {
|
|
41
106
|
if (!files.length)
|
|
42
107
|
return;
|
|
43
108
|
if (!opts.amikooKey) {
|
|
44
|
-
console.warn('
|
|
109
|
+
console.warn('⚠ AMIKOO_KEY missing; skipping artifact upload');
|
|
45
110
|
return;
|
|
46
111
|
}
|
|
47
112
|
const groups = new Map();
|
|
@@ -50,12 +115,54 @@ async function uploadAmikooArtifacts(files, opts) {
|
|
|
50
115
|
bucket.push(f);
|
|
51
116
|
groups.set(f.hash, bucket);
|
|
52
117
|
}
|
|
53
|
-
console.log(
|
|
54
|
-
const
|
|
118
|
+
console.log(`📁 Uploading ${groups.size} artifact(s) to S3 (concurrency=${ARTIFACT_UPLOAD_CONCURRENCY})...`);
|
|
119
|
+
const entries = Array.from(groups.entries());
|
|
120
|
+
const results = [];
|
|
121
|
+
let cursor = 0;
|
|
122
|
+
const worker = async () => {
|
|
123
|
+
while (cursor < entries.length) {
|
|
124
|
+
const index = cursor++;
|
|
125
|
+
const [hash, group] = entries[index];
|
|
126
|
+
try {
|
|
127
|
+
await uploadSingleTest(hash, group, opts);
|
|
128
|
+
results.push({ status: 'fulfilled', value: undefined });
|
|
129
|
+
}
|
|
130
|
+
catch (reason) {
|
|
131
|
+
results.push({ status: 'rejected', reason });
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
};
|
|
135
|
+
const workerCount = Math.min(ARTIFACT_UPLOAD_CONCURRENCY, entries.length);
|
|
136
|
+
await Promise.all(Array.from({ length: workerCount }, () => worker()));
|
|
55
137
|
const succeeded = results.filter(r => r.status === 'fulfilled').length;
|
|
56
138
|
const failed = results.length - succeeded;
|
|
57
|
-
|
|
139
|
+
if (failed) {
|
|
140
|
+
console.warn(`⚠ Uploaded ${succeeded}/${results.length} artifact(s) to S3 (${failed} failed)`);
|
|
141
|
+
}
|
|
142
|
+
else {
|
|
143
|
+
console.log(`✔ Uploaded ${succeeded}/${results.length} artifact(s) to S3`);
|
|
144
|
+
}
|
|
58
145
|
}
|
|
146
|
+
/**
|
|
147
|
+
* Builds a zip for a single test's artifacts, uploads it to S3, and records
|
|
148
|
+
* the resulting S3 path on the backend via `/test_execution_data/upsert`.
|
|
149
|
+
*
|
|
150
|
+
* Steps:
|
|
151
|
+
* 1. Build a zip in the OS temp dir containing:
|
|
152
|
+
* - `failure-data/*` → the amikoo artifact files
|
|
153
|
+
* - `test-files/*` → the spec file and its local imports
|
|
154
|
+
* 2. Request a fresh presigned S3 URL scoped to this test slug.
|
|
155
|
+
* 3. Stream the zip to S3 (see `uploadZipToS3` — streaming, not buffered).
|
|
156
|
+
* 4. Tell the backend where the zip landed so the UI can link to it.
|
|
157
|
+
* 5. Always delete the temp zip in `finally`, even on failure.
|
|
158
|
+
*
|
|
159
|
+
* Errors are logged with a clear per-test message and re-thrown so the
|
|
160
|
+
* parent batcher records the failure in its summary.
|
|
161
|
+
*
|
|
162
|
+
* @param hash - Test hash, used as the grouping key and S3 key fragment.
|
|
163
|
+
* @param group - All artifacts belonging to this test.
|
|
164
|
+
* @param opts - Upload options (token, amikoo key, repo root, endpoint).
|
|
165
|
+
*/
|
|
59
166
|
async function uploadSingleTest(hash, group, opts) {
|
|
60
167
|
const testTitle = group[0].testTitle;
|
|
61
168
|
const specFile = group[0].specFile;
|
|
@@ -73,11 +180,10 @@ async function uploadSingleTest(hash, group, opts) {
|
|
|
73
180
|
if (!upsertResponse.success) {
|
|
74
181
|
throw new Error(`upsert failed: ${upsertResponse.error}`);
|
|
75
182
|
}
|
|
76
|
-
console.log(`[amikoo-upload] ${slug} uploaded to ${sourcePath}`);
|
|
77
183
|
}
|
|
78
184
|
catch (error) {
|
|
79
185
|
const msg = error instanceof Error ? error.message : String(error);
|
|
80
|
-
console.error(`
|
|
186
|
+
console.error(` ✖ Artifact upload failed for ${slug}: ${msg}`);
|
|
81
187
|
throw error;
|
|
82
188
|
}
|
|
83
189
|
finally {
|
|
@@ -87,6 +193,18 @@ async function uploadSingleTest(hash, group, opts) {
|
|
|
87
193
|
catch { /* ignore */ }
|
|
88
194
|
}
|
|
89
195
|
}
|
|
196
|
+
/**
|
|
197
|
+
* Converts a free-form test title into an S3-safe slug.
|
|
198
|
+
*
|
|
199
|
+
* Rules:
|
|
200
|
+
* - lowercase
|
|
201
|
+
* - spaces → underscores
|
|
202
|
+
* - anything that isn't `[a-z0-9_-]` → underscore
|
|
203
|
+
* - collapse runs of underscores and trim leading/trailing ones
|
|
204
|
+
*
|
|
205
|
+
* Used as a path segment in the S3 key so artifacts are easy to spot when
|
|
206
|
+
* browsing the bucket.
|
|
207
|
+
*/
|
|
90
208
|
function sanitizeSlug(title) {
|
|
91
209
|
return title
|
|
92
210
|
.toLowerCase()
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"amikooArtifacts.js","sourceRoot":"","sources":["../controlHub/amikooArtifacts.ts"],"names":[],"mappings":";;;;;
|
|
1
|
+
{"version":3,"file":"amikooArtifacts.js","sourceRoot":"","sources":["../controlHub/amikooArtifacts.ts"],"names":[],"mappings":";;;;;AAqFA,wDAmBC;AAoBD,sDA+CC;AA3KD,4CAAoB;AACpB,gDAAwB;AACxB,4CAAoB;AACpB,yCAAmD;AACnD,yCAA6C;AAC7C,yCAA6D;AAE7D,iEAAiE;AACjE,EAAE;AACF,iFAAiF;AACjF,2EAA2E;AAC3E,4DAA4D;AAC5D,mEAAmE;AACnE,0BAA0B;AAC1B,EAAE;AACF,6CAA6C;AAC7C,oDAAoD;AACpD,4EAA4E;AAC5E,4DAA4D;AAC5D,EAAE;AACF,2DAA2D;AAC3D,0CAA0C;AAC1C,MAAM,2BAA2B,GAAG,IAAI,CAAC,GAAG,CAC1C,CAAC,EACD,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,kCAAkC,IAAI,EAAE,EAAE,EAAE,CAAC,IAAI,EAAE,CACzE,CAAC;AAiBF,MAAM,oBAAoB,GAAG;IAC3B,IAAI,EAAE,8EAA8E;IACpF,OAAO,EAAE,iFAAiF;CAC3F,CAAC;AAEF;;;;;;;;;;;;GAYG;AACH,SAAS,wBAAwB;IAC/B,IAAI,OAAO,CAAC,GAAG,CAAC,0BAA0B;QAAE,OAAO,OAAO,CAAC,GAAG,CAAC,0BAA0B,CAAC;IAC1F,OAAO,IAAA,2BAAgB,GAAE,KAAK,uBAAuB;QACnD,CAAC,CAAC,oBAAoB,CAAC,IAAI;QAC3B,CAAC,CAAC,oBAAoB,CAAC,OAAO,CAAC;AACnC,CAAC;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,SAAgB,sBAAsB,CACpC,MAAiE,EACjE,MAAc,EACd,SAAiB,EACjB,QAAgB;IAEhB,MAAM,GAAG,GAAqB,EAAE,CAAC;IACjC,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,WAAW,IAAI,EAAE,EAAE,CAAC;QAC3C,IAAI,OAAO,GAAG,EAAE,IAAI,KAAK,QAAQ,IAAI,GAAG,CAAC,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC,IAAI,GAAG,CAAC,IAAI,EAAE,CAAC;YAChF,GAAG,CAAC,IAAI,CAAC;gBACP,IAAI,EAAE,MAAM;gBACZ,SAAS,EAAE,SAAS;gBACpB,SAAS,EAAE,GAAG,CAAC,IAAI;gBACnB,cAAc,EAAE,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,MAAM,CAAC;gBAChD,QAAQ;aACT,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED;;;;;;;;;;;;;;;;;GAiBG;AACI,KAAK,UAAU,qBAAqB,CACzC,KAAuB,EACvB,IAAmB;IAEnB,IAAI,CAAC,KAAK,CAAC,MAAM;QAAE,OAAO;IAE1B,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC;QACpB,OAAO,CAAC,IAAI,CAAC,gDAAgD,CAAC,CAAC;QAC/D,OAAO;IACT,CAAC;IAED,MAAM,MAAM,GAAG,IAAI,GAAG,EAA4B,CAAC;IACnD,KAAK,MAAM,CAAC,IAAI,KAAK,EAAE,CAAC;QACtB,MAAM,MAAM,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;QACxC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACf,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IAC7B,CAAC;IAED,OAAO,CAAC,GAAG,CAAC,gBAAgB,MAAM,CAAC,IAAI,mCAAmC,2BAA2B,MAAM,CAAC,CAAC;IAE7G,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC,CAAC;IAC7C,MAAM,OAAO,GAAiC,EAAE,CAAC;IACjD,IAAI,MAAM,GAAG,CAAC,CAAC;IACf,MAAM,MAAM,GAAG,KAAK,IAAI,EAAE;QACxB,OAAO,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;YAC/B,MAAM,KAAK,GAAG,MAAM,EAAE,CAAC;YACvB,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC;YACrC,IAAI,CAAC;gBACH,MAAM,gBAAgB,CAAC,IAAI,EAAE,KAAK,EAAE,IAAI,CAAC,CAAC;gBAC1C,OAAO,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,WAAW,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC,CAAC;YAC1D,CAAC;YACD,OAAO,MAAM,EAAE,CAAC;gBACd,OAAO,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,CAAC,CAAC;YAC/C,CAAC;QACH,CAAC;IACH,CAAC,CAAC;IAEF,MAAM,WAAW,GAAG,IAAI,CAAC,GAAG,CAAC,2BAA2B,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC;IAC1E,MAAM,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,WAAW,EAAE,EAAE,GAAG,EAAE,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;IAEvE,MAAM,SAAS,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,WAAW,CAAC,CAAC,MAAM,CAAC;IACvE,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC;IAC1C,IAAI,MAAM,EAAE,CAAC;QACX,OAAO,CAAC,IAAI,CAAC,cAAc,SAAS,IAAI,OAAO,CAAC,MAAM,uBAAuB,MAAM,UAAU,CAAC,CAAC;IACjG,CAAC;SAAM,CAAC;QACN,OAAO,CAAC,GAAG,CAAC,cAAc,SAAS,IAAI,OAAO,CAAC,MAAM,oBAAoB,CAAC,CAAC;IAC7E,CAAC;AACH,CAAC;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,KAAK,UAAU,gBAAgB,CAC7B,IAAY,EACZ,KAAuB,EACvB,IAAmB;IAEnB,MAAM,SAAS,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IACrC,MAAM,QAAQ,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC;IACnC,MAAM,IAAI,GAAG,YAAY,CAAC,SAAS,CAAC,CAAC;IAErC,MAAM,OAAO,GAAG,cAAI,CAAC,IAAI,CAAC,YAAE,CAAC,MAAM,EAAE,EAAE,UAAU,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,IAAI,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;IACvF,IAAI,CAAC;QACH,MAAM,IAAA,0BAAe,EAAC,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC;QAE/D,MAAM,QAAQ,GAAG,IAAI,CAAC,iBAAiB,IAAI,wBAAwB,EAAE,CAAC;QACtE,MAAM,EAAE,UAAU,EAAE,SAAS,EAAE,GAAG,MAAM,IAAA,2BAAgB,EACtD,QAAQ,EACR,IAAI,CAAC,SAAS,EACd,IAAI,CACL,CAAC;QAEF,MAAM,IAAA,wBAAa,EAAC,OAAO,EAAE,SAAS,CAAC,CAAC;QAExC,MAAM,cAAc,GAAG,MAAM,cAAG,CAAC,IAAI,CAAC,6BAA6B,EAAE,IAAI,CAAC,KAAK,EAAE;YAC/E,IAAI;YACJ,oBAAoB,EAAE,UAAU;SACjC,CAAC,CAAC;QAEH,IAAI,CAAC,cAAc,CAAC,OAAO,EAAE,CAAC;YAC5B,MAAM,IAAI,KAAK,CAAC,kBAAkB,cAAc,CAAC,KAAK,EAAE,CAAC,CAAC;QAC5D,CAAC;IACH,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,GAAG,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QACnE,OAAO,CAAC,KAAK,CAAC,kCAAkC,IAAI,KAAK,GAAG,EAAE,CAAC,CAAC;QAChE,MAAM,KAAK,CAAC;IACd,CAAC;YAAS,CAAC;QACT,IAAI,CAAC;YAAC,YAAE,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC;QAAC,CAAC;QAAC,MAAM,CAAC,CAAC,YAAY,CAAC,CAAC;IACxD,CAAC;AACH,CAAC;AAED;;;;;;;;;;;GAWG;AACH,SAAS,YAAY,CAAC,KAAa;IACjC,OAAO,KAAK;SACT,WAAW,EAAE;SACb,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC;SACpB,OAAO,CAAC,eAAe,EAAE,GAAG,CAAC;SAC7B,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC;SACnB,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC;AAC7B,CAAC"}
|
|
@@ -21,7 +21,7 @@ declare class MyReporter implements Reporter {
|
|
|
21
21
|
private owner;
|
|
22
22
|
private key;
|
|
23
23
|
private testsInfo;
|
|
24
|
-
private
|
|
24
|
+
private initPromise;
|
|
25
25
|
onBegin(config: any, suite: any): Promise<void>;
|
|
26
26
|
onTestBegin(test: any): Promise<void>;
|
|
27
27
|
onError(error: any): Promise<void>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"controlHubReporter.d.ts","sourceRoot":"","sources":["../controlHub/controlHubReporter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,2BAA2B,CAAC;AAKrD,OAAO,EAAyB,cAAc,EAA0B,MAAM,mBAAmB,CAAC;AAKlG,cAAM,UAAW,YAAW,QAAQ;IAElC,iBAAiB,EAAE,GAAG,EAAE,CAAM;IAC9B,MAAM,EAAE,GAAG,EAAE,CAAM;IACnB,WAAW,EAAE,cAAc,EAAE,CAAM;IACnC,QAAQ,EAAE,GAAG,CAAC;IACd,cAAc,EAAE,MAAM,CAAM;IAC5B,eAAe,EAAE,MAAM,CAAK;IAC5B,OAAO,EAAE,GAAG,EAAE,CAAM;IACpB,kBAAkB,EAAE,MAAM,CAAK;IAC/B,OAAO,EAAE,MAAM,CAAgB;IAC/B,gBAAgB,EAAE,OAAO,CAAS;IAClC,yBAAyB,EAAE,GAAG,EAAE,CAAM;IACtC,GAAG,EAAE,GAAG,CAAC;IACT,eAAe,EAAE,GAAG,EAAE,CAAM;IAE5B,OAAO,CAAC,YAAY,CAAU;IAC9B,OAAO,CAAC,MAAM,CAAU;IACxB,OAAO,CAAC,cAAc,CAAU;IAChC,OAAO,CAAC,YAAY,CAAU;IAC9B,OAAO,CAAC,KAAK,CAAU;IACvB,OAAO,CAAC,GAAG,CAAU;IACrB,OAAO,CAAC,SAAS,CAAa;IAC9B,OAAO,CAAC,
|
|
1
|
+
{"version":3,"file":"controlHubReporter.d.ts","sourceRoot":"","sources":["../controlHub/controlHubReporter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,2BAA2B,CAAC;AAKrD,OAAO,EAAyB,cAAc,EAA0B,MAAM,mBAAmB,CAAC;AAKlG,cAAM,UAAW,YAAW,QAAQ;IAElC,iBAAiB,EAAE,GAAG,EAAE,CAAM;IAC9B,MAAM,EAAE,GAAG,EAAE,CAAM;IACnB,WAAW,EAAE,cAAc,EAAE,CAAM;IACnC,QAAQ,EAAE,GAAG,CAAC;IACd,cAAc,EAAE,MAAM,CAAM;IAC5B,eAAe,EAAE,MAAM,CAAK;IAC5B,OAAO,EAAE,GAAG,EAAE,CAAM;IACpB,kBAAkB,EAAE,MAAM,CAAK;IAC/B,OAAO,EAAE,MAAM,CAAgB;IAC/B,gBAAgB,EAAE,OAAO,CAAS;IAClC,yBAAyB,EAAE,GAAG,EAAE,CAAM;IACtC,GAAG,EAAE,GAAG,CAAC;IACT,eAAe,EAAE,GAAG,EAAE,CAAM;IAE5B,OAAO,CAAC,YAAY,CAAU;IAC9B,OAAO,CAAC,MAAM,CAAU;IACxB,OAAO,CAAC,cAAc,CAAU;IAChC,OAAO,CAAC,YAAY,CAAU;IAC9B,OAAO,CAAC,KAAK,CAAU;IACvB,OAAO,CAAC,GAAG,CAAU;IACrB,OAAO,CAAC,SAAS,CAAa;IAC9B,OAAO,CAAC,WAAW,CAA8B;IAE3C,OAAO,CAAC,MAAM,EAAE,GAAG,EAAE,KAAK,EAAE,GAAG;IA6C/B,WAAW,CAAC,IAAI,EAAE,GAAG;IAIrB,OAAO,CAAC,KAAK,EAAE,GAAG;IAalB,SAAS,CAAC,IAAI,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG;IAgFhC,KAAK,CAAC,MAAM,EAAE,GAAG;CAqFxB;AACD,eAAe,UAAU,CAAC"}
|
|
@@ -25,12 +25,12 @@ class MyReporter {
|
|
|
25
25
|
this.filesWithCompilationError = [];
|
|
26
26
|
this.testEndPromises = [];
|
|
27
27
|
this.testsInfo = [];
|
|
28
|
-
this.
|
|
28
|
+
this.initPromise = null;
|
|
29
29
|
}
|
|
30
30
|
async onBegin(config, suite) {
|
|
31
31
|
(0, reporterOrder_1.checkReporterOrder)(config);
|
|
32
|
-
// Store initialization work as a promise that onTestEnd will await
|
|
33
|
-
|
|
32
|
+
// Store initialization work as a single promise that onTestEnd will await
|
|
33
|
+
this.initPromise = (async () => {
|
|
34
34
|
try {
|
|
35
35
|
// We need to obtain a token from control hub so we can send API requests.
|
|
36
36
|
this.key = process.env.AMIKOO_KEY || '';
|
|
@@ -67,7 +67,6 @@ class MyReporter {
|
|
|
67
67
|
console.error('\n✖ Error during reporter initialization:', error);
|
|
68
68
|
}
|
|
69
69
|
})();
|
|
70
|
-
this.onBeginDonePromises.push(initPromise);
|
|
71
70
|
}
|
|
72
71
|
async onTestBegin(test) {
|
|
73
72
|
console.log(` ▶ Starting execution of test : ${test.title}`);
|
|
@@ -86,11 +85,13 @@ class MyReporter {
|
|
|
86
85
|
}
|
|
87
86
|
async onTestEnd(test, result) {
|
|
88
87
|
// Wait for onBegin to complete before processing test results
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
88
|
+
if (this.initPromise) {
|
|
89
|
+
try {
|
|
90
|
+
await this.initPromise;
|
|
91
|
+
}
|
|
92
|
+
catch (error) {
|
|
93
|
+
console.error(' ✖ Error waiting for initialization:', error);
|
|
94
|
+
}
|
|
94
95
|
}
|
|
95
96
|
const statusIcon = result.status === 'passed' ? '✔' : '✖';
|
|
96
97
|
const statusText = result.status === 'passed' ? 'Passed' : 'Failed';
|
|
@@ -134,8 +135,8 @@ class MyReporter {
|
|
|
134
135
|
executionAt: new Date().toISOString(),
|
|
135
136
|
result: result.status === "passed" ? true : false,
|
|
136
137
|
error: errorData,
|
|
137
|
-
systemOutput:
|
|
138
|
-
systemError:
|
|
138
|
+
systemOutput: '',
|
|
139
|
+
systemError: '',
|
|
139
140
|
};
|
|
140
141
|
this.testExecutionData.push(payload);
|
|
141
142
|
// Collect video for later batch processing
|
|
@@ -155,12 +156,18 @@ class MyReporter {
|
|
|
155
156
|
async onEnd(result) {
|
|
156
157
|
console.log('\n⏳ Waiting for all tests to complete...');
|
|
157
158
|
try {
|
|
158
|
-
|
|
159
|
+
const pending = [...this.testEndPromises];
|
|
160
|
+
if (this.initPromise)
|
|
161
|
+
pending.push(this.initPromise);
|
|
162
|
+
await Promise.all(pending);
|
|
159
163
|
}
|
|
160
164
|
catch (error) {
|
|
161
165
|
console.error('✖ Error waiting for test promises:', error);
|
|
162
166
|
}
|
|
163
167
|
console.log('✔ All tests completed\n');
|
|
168
|
+
// Release references to large per-test data structures we no longer need
|
|
169
|
+
this.testEndPromises = [];
|
|
170
|
+
this.testsInfo = [];
|
|
164
171
|
if (this.executionNumber) {
|
|
165
172
|
// Process videos and get presigned URLs
|
|
166
173
|
const videoResult = await (0, videoUtils_1.processVideos)(this.videos, this.organizationId, this.executionNumber, this.access_token);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"controlHubReporter.js","sourceRoot":"","sources":["../controlHub/controlHubReporter.ts"],"names":[],"mappings":";;;;;AACA,yCAA2C;AAC3C,yCAAiC;AACjC,6CAA6E;AAC7E,mCAA0H;AAC1H,uDAAkG;AAClG,mDAAqD;AACrD,gDAAwB;AACxB,oDAA4B;AAE5B,MAAM,UAAU;IAAhB;QAEE,sBAAiB,GAAU,EAAE,CAAC;QAC9B,WAAM,GAAU,EAAE,CAAC;QACnB,gBAAW,GAAqB,EAAE,CAAC;QAEnC,mBAAc,GAAW,EAAE,CAAC;QAC5B,oBAAe,GAAW,CAAC,CAAC;QAC5B,YAAO,GAAU,EAAE,CAAC;QACpB,uBAAkB,GAAW,CAAC,CAAC;QAC/B,YAAO,GAAW,YAAY,CAAC;QAC/B,qBAAgB,GAAY,KAAK,CAAC;QAClC,8BAAyB,GAAU,EAAE,CAAC;QAEtC,oBAAe,GAAU,EAAE,CAAC;QAQpB,cAAS,GAAU,EAAE,CAAC;QACtB,
|
|
1
|
+
{"version":3,"file":"controlHubReporter.js","sourceRoot":"","sources":["../controlHub/controlHubReporter.ts"],"names":[],"mappings":"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"}
|
package/dist/s3Upload.d.ts
CHANGED
|
@@ -1,6 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Requests a presigned S3 URL from the amikoo signed-URL service so we can
|
|
3
|
+
* PUT an artifact zip directly to S3 without going through the backend.
|
|
4
|
+
*
|
|
5
|
+
* The service decides the final S3 key from `test_name` (slug) and returns
|
|
6
|
+
* both the destination path we should record in our DB (`sourcePath`) and
|
|
7
|
+
* the time-limited PUT URL.
|
|
8
|
+
*
|
|
9
|
+
* @param endpoint - Full HTTPS URL of the signed-URL service
|
|
10
|
+
* (prod/staging differ — see `resolveSignedUrlEndpoint`).
|
|
11
|
+
* @param amikooKey - User's amikoo key, used by the service to scope the
|
|
12
|
+
* upload to the right organization.
|
|
13
|
+
* @param testSlug - URL-safe test name; becomes a path segment in S3.
|
|
14
|
+
* @returns `{ sourcePath, signedUrl }` — throws on HTTP error or if the
|
|
15
|
+
* response is missing either field.
|
|
16
|
+
*/
|
|
1
17
|
export declare function requestSignedUrl(endpoint: string, amikooKey: string, testSlug: string): Promise<{
|
|
2
18
|
sourcePath: string;
|
|
3
19
|
signedUrl: string;
|
|
4
20
|
}>;
|
|
21
|
+
/**
|
|
22
|
+
* Streams a local zip file to S3 via a presigned PUT URL.
|
|
23
|
+
*
|
|
24
|
+
* Memory note: we deliberately do **not** call `fs.readFileSync(zipPath)`
|
|
25
|
+
* here. With concurrent uploads, buffering each zip would multiply RAM
|
|
26
|
+
* usage by the concurrency factor. Instead we open a Node read stream,
|
|
27
|
+
* convert it to a Web ReadableStream, and let undici/fetch send it in
|
|
28
|
+
* chunks (~64 KB at a time). `Content-Length` is set explicitly so S3 can
|
|
29
|
+
* validate the request and doesn't have to fall back to chunked encoding.
|
|
30
|
+
*
|
|
31
|
+
* The `duplex: 'half'` option is required by undici whenever the request
|
|
32
|
+
* body is a stream.
|
|
33
|
+
*
|
|
34
|
+
* @param zipPath - Absolute path to the zip on disk.
|
|
35
|
+
* @param signedUrl - Presigned PUT URL from `requestSignedUrl`.
|
|
36
|
+
* @throws If S3 responds with a non-2xx status.
|
|
37
|
+
*/
|
|
5
38
|
export declare function uploadZipToS3(zipPath: string, signedUrl: string): Promise<void>;
|
|
6
39
|
//# sourceMappingURL=s3Upload.d.ts.map
|
package/dist/s3Upload.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"s3Upload.d.ts","sourceRoot":"","sources":["../controlHub/s3Upload.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"s3Upload.d.ts","sourceRoot":"","sources":["../controlHub/s3Upload.ts"],"names":[],"mappings":"AAGA;;;;;;;;;;;;;;;GAeG;AACH,wBAAsB,gBAAgB,CACpC,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC;IAAE,UAAU,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,CAAC,CAiBpD;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAsB,aAAa,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAuBrF"}
|
package/dist/s3Upload.js
CHANGED
|
@@ -6,6 +6,23 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
6
|
exports.requestSignedUrl = requestSignedUrl;
|
|
7
7
|
exports.uploadZipToS3 = uploadZipToS3;
|
|
8
8
|
const fs_1 = __importDefault(require("fs"));
|
|
9
|
+
const stream_1 = require("stream");
|
|
10
|
+
/**
|
|
11
|
+
* Requests a presigned S3 URL from the amikoo signed-URL service so we can
|
|
12
|
+
* PUT an artifact zip directly to S3 without going through the backend.
|
|
13
|
+
*
|
|
14
|
+
* The service decides the final S3 key from `test_name` (slug) and returns
|
|
15
|
+
* both the destination path we should record in our DB (`sourcePath`) and
|
|
16
|
+
* the time-limited PUT URL.
|
|
17
|
+
*
|
|
18
|
+
* @param endpoint - Full HTTPS URL of the signed-URL service
|
|
19
|
+
* (prod/staging differ — see `resolveSignedUrlEndpoint`).
|
|
20
|
+
* @param amikooKey - User's amikoo key, used by the service to scope the
|
|
21
|
+
* upload to the right organization.
|
|
22
|
+
* @param testSlug - URL-safe test name; becomes a path segment in S3.
|
|
23
|
+
* @returns `{ sourcePath, signedUrl }` — throws on HTTP error or if the
|
|
24
|
+
* response is missing either field.
|
|
25
|
+
*/
|
|
9
26
|
async function requestSignedUrl(endpoint, amikooKey, testSlug) {
|
|
10
27
|
const response = await fetch(endpoint, {
|
|
11
28
|
method: 'POST',
|
|
@@ -21,16 +38,40 @@ async function requestSignedUrl(endpoint, amikooKey, testSlug) {
|
|
|
21
38
|
}
|
|
22
39
|
return { sourcePath: data.sourcePath, signedUrl: data.signedUrl };
|
|
23
40
|
}
|
|
41
|
+
/**
|
|
42
|
+
* Streams a local zip file to S3 via a presigned PUT URL.
|
|
43
|
+
*
|
|
44
|
+
* Memory note: we deliberately do **not** call `fs.readFileSync(zipPath)`
|
|
45
|
+
* here. With concurrent uploads, buffering each zip would multiply RAM
|
|
46
|
+
* usage by the concurrency factor. Instead we open a Node read stream,
|
|
47
|
+
* convert it to a Web ReadableStream, and let undici/fetch send it in
|
|
48
|
+
* chunks (~64 KB at a time). `Content-Length` is set explicitly so S3 can
|
|
49
|
+
* validate the request and doesn't have to fall back to chunked encoding.
|
|
50
|
+
*
|
|
51
|
+
* The `duplex: 'half'` option is required by undici whenever the request
|
|
52
|
+
* body is a stream.
|
|
53
|
+
*
|
|
54
|
+
* @param zipPath - Absolute path to the zip on disk.
|
|
55
|
+
* @param signedUrl - Presigned PUT URL from `requestSignedUrl`.
|
|
56
|
+
* @throws If S3 responds with a non-2xx status.
|
|
57
|
+
*/
|
|
24
58
|
async function uploadZipToS3(zipPath, signedUrl) {
|
|
25
|
-
|
|
59
|
+
// Stream the zip from disk instead of buffering it in memory. With multiple
|
|
60
|
+
// concurrent uploads this keeps RAM flat even for large zips.
|
|
61
|
+
const stat = fs_1.default.statSync(zipPath);
|
|
62
|
+
const nodeStream = fs_1.default.createReadStream(zipPath);
|
|
63
|
+
const body = stream_1.Readable.toWeb(nodeStream);
|
|
26
64
|
const response = await fetch(signedUrl, {
|
|
27
65
|
method: 'PUT',
|
|
28
|
-
body
|
|
66
|
+
body,
|
|
29
67
|
headers: {
|
|
30
68
|
'Content-Type': 'application/zip',
|
|
69
|
+
'Content-Length': String(stat.size),
|
|
31
70
|
'x-amz-server-side-encryption': 'AES256',
|
|
32
71
|
'x-amz-meta-purpose': 'muukmcp',
|
|
33
72
|
},
|
|
73
|
+
// duplex is required by undici when the request body is a stream.
|
|
74
|
+
duplex: 'half',
|
|
34
75
|
});
|
|
35
76
|
if (!response.ok) {
|
|
36
77
|
throw new Error(`S3 upload failed: ${response.status} ${response.statusText}`);
|
package/dist/s3Upload.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"s3Upload.js","sourceRoot":"","sources":["../controlHub/s3Upload.ts"],"names":[],"mappings":";;;;;
|
|
1
|
+
{"version":3,"file":"s3Upload.js","sourceRoot":"","sources":["../controlHub/s3Upload.ts"],"names":[],"mappings":";;;;;AAmBA,4CAqBC;AAmBD,sCAuBC;AAlFD,4CAAoB;AACpB,mCAAkC;AAElC;;;;;;;;;;;;;;;GAeG;AACI,KAAK,UAAU,gBAAgB,CACpC,QAAgB,EAChB,SAAiB,EACjB,QAAgB;IAEhB,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,QAAQ,EAAE;QACrC,MAAM,EAAE,MAAM;QACd,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;QAC/C,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,GAAG,EAAE,SAAS,EAAE,SAAS,EAAE,QAAQ,EAAE,CAAC;KAC9D,CAAC,CAAC;IAEH,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;QACjB,MAAM,IAAI,KAAK,CAAC,8BAA8B,QAAQ,CAAC,MAAM,IAAI,QAAQ,CAAC,UAAU,EAAE,CAAC,CAAC;IAC1F,CAAC;IAED,MAAM,IAAI,GAAQ,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;IACxC,IAAI,CAAC,IAAI,EAAE,UAAU,IAAI,CAAC,IAAI,EAAE,SAAS,EAAE,CAAC;QAC1C,MAAM,IAAI,KAAK,CAAC,qDAAqD,CAAC,CAAC;IACzE,CAAC;IAED,OAAO,EAAE,UAAU,EAAE,IAAI,CAAC,UAAU,EAAE,SAAS,EAAE,IAAI,CAAC,SAAS,EAAE,CAAC;AACpE,CAAC;AAED;;;;;;;;;;;;;;;;GAgBG;AACI,KAAK,UAAU,aAAa,CAAC,OAAe,EAAE,SAAiB;IACpE,4EAA4E;IAC5E,8DAA8D;IAC9D,MAAM,IAAI,GAAG,YAAE,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;IAClC,MAAM,UAAU,GAAG,YAAE,CAAC,gBAAgB,CAAC,OAAO,CAAC,CAAC;IAChD,MAAM,IAAI,GAAG,iBAAQ,CAAC,KAAK,CAAC,UAAU,CAA0C,CAAC;IAEjF,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,SAAS,EAAE;QACtC,MAAM,EAAE,KAAK;QACb,IAAI;QACJ,OAAO,EAAE;YACP,cAAc,EAAE,iBAAiB;YACjC,gBAAgB,EAAE,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC;YACnC,8BAA8B,EAAE,QAAQ;YACxC,oBAAoB,EAAE,SAAS;SAChC;QACD,kEAAkE;QAClE,MAAM,EAAE,MAAM;KACR,CAAC,CAAC;IAEV,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;QACjB,MAAM,IAAI,KAAK,CAAC,qBAAqB,QAAQ,CAAC,MAAM,IAAI,QAAQ,CAAC,UAAU,EAAE,CAAC,CAAC;IACjF,CAAC;AACH,CAAC"}
|
package/dist/videoUtils.d.ts
CHANGED
|
@@ -1,16 +1,57 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Extracts the local filesystem path of the video recorded by Playwright
|
|
3
|
+
* for a given test result.
|
|
4
|
+
*
|
|
5
|
+
* Playwright stores videos as attachments on the TestResult. We look for an
|
|
6
|
+
* attachment explicitly named 'video', and fall back to any attachment whose
|
|
7
|
+
* path ends in `.webm` (older Playwright versions or custom setups).
|
|
8
|
+
*
|
|
9
|
+
* @param result - Playwright TestResult object (typed as `any` to avoid a hard
|
|
10
|
+
* dependency on Playwright's reporter types here).
|
|
11
|
+
* @returns Absolute path to the video file, or an empty string if no video
|
|
12
|
+
* attachment exists for this test.
|
|
3
13
|
*/
|
|
4
14
|
export declare function getVideoPath(result: any): string;
|
|
5
15
|
/**
|
|
6
|
-
*
|
|
16
|
+
* Prepares the video list for upload and asks the backend for presigned S3
|
|
17
|
+
* upload URLs in a single batch request.
|
|
18
|
+
*
|
|
19
|
+
* Steps:
|
|
20
|
+
* 1. Filter out videos whose local file no longer exists on disk.
|
|
21
|
+
* 2. Compute a deterministic S3 filename per video:
|
|
22
|
+
* `<executionNumber>_<testId>.webm`
|
|
23
|
+
* 3. Call `/execution/batch_upload_urls` once for all videos (single API
|
|
24
|
+
* round-trip instead of one per file).
|
|
25
|
+
*
|
|
26
|
+
* @param videos - Raw entries collected during the run, each with
|
|
27
|
+
* `{ path, testId }`.
|
|
28
|
+
* @param organizationId - Used as the S3 folder prefix: `videos/<orgId>`.
|
|
29
|
+
* @param executionNumber - Current run's execution number (prefix in S3 key).
|
|
30
|
+
* @param token - Amikoo API access token.
|
|
31
|
+
* @returns An object with the processed video metadata (`localPath` +
|
|
32
|
+
* `s3FileName`) and the matching presigned upload URLs. `uploadUrls`
|
|
33
|
+
* is `null` if the backend request failed or no videos were eligible.
|
|
7
34
|
*/
|
|
8
35
|
export declare function processVideos(videos: any[], organizationId: string, executionNumber: number, token: string): Promise<{
|
|
9
36
|
videos: any[];
|
|
10
37
|
uploadUrls: any[] | null;
|
|
11
38
|
}>;
|
|
12
39
|
/**
|
|
13
|
-
* Uploads
|
|
40
|
+
* Uploads every processed video to S3 in parallel, bounded by
|
|
41
|
+
* `VIDEO_UPLOAD_CONCURRENCY`.
|
|
42
|
+
*
|
|
43
|
+
* Implementation:
|
|
44
|
+
* - Builds a `Map<s3FileName, presignedUrl>` for O(1) lookup.
|
|
45
|
+
* - Spawns up to N "worker" promises that pull from a shared cursor; as
|
|
46
|
+
* soon as a worker finishes one upload, it grabs the next pending video.
|
|
47
|
+
* This keeps the connection pipeline saturated without ever holding more
|
|
48
|
+
* than N file streams open at once.
|
|
49
|
+
* - Failures are recorded per-video; one failed upload does not abort the
|
|
50
|
+
* rest. A summary line is printed at the end.
|
|
51
|
+
*
|
|
52
|
+
* @param videos - Processed video entries from `processVideos`.
|
|
53
|
+
* @param uploadUrls - Presigned URLs from the backend, keyed by `fileName`.
|
|
54
|
+
* @returns Per-video results: `{ fileName, success, error? }`.
|
|
14
55
|
*/
|
|
15
56
|
export declare function uploadVideosToS3(videos: any[], uploadUrls: any[]): Promise<any[]>;
|
|
16
57
|
//# sourceMappingURL=videoUtils.d.ts.map
|
package/dist/videoUtils.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"videoUtils.d.ts","sourceRoot":"","sources":["../controlHub/videoUtils.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"videoUtils.d.ts","sourceRoot":"","sources":["../controlHub/videoUtils.ts"],"names":[],"mappings":"AAuBA;;;;;;;;;;;;GAYG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,GAAG,GAAG,MAAM,CAGhD;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAsB,aAAa,CACjC,MAAM,EAAE,GAAG,EAAE,EACb,cAAc,EAAE,MAAM,EACtB,eAAe,EAAE,MAAM,EACvB,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC;IAAE,MAAM,EAAE,GAAG,EAAE,CAAC;IAAC,UAAU,EAAE,GAAG,EAAE,GAAG,IAAI,CAAA;CAAE,CAAC,CA0BtD;AAsDD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAsB,gBAAgB,CAAC,MAAM,EAAE,GAAG,EAAE,EAAE,UAAU,EAAE,GAAG,EAAE,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC,CAqCvF"}
|
package/dist/videoUtils.js
CHANGED
|
@@ -7,16 +7,59 @@ exports.getVideoPath = getVideoPath;
|
|
|
7
7
|
exports.processVideos = processVideos;
|
|
8
8
|
exports.uploadVideosToS3 = uploadVideosToS3;
|
|
9
9
|
const fs_1 = __importDefault(require("fs"));
|
|
10
|
+
const stream_1 = require("stream");
|
|
10
11
|
const apiUtils_1 = require("./apiUtils");
|
|
12
|
+
// How many videos are uploaded to S3 in parallel.
|
|
13
|
+
//
|
|
14
|
+
// Each "worker" streams one .webm file to S3 via a presigned URL using
|
|
15
|
+
// fs.createReadStream → fetch (no buffering). Higher values finish faster
|
|
16
|
+
// on fast networks but hold more open sockets and file descriptors at once.
|
|
17
|
+
// Lower values (1–2) are gentler on memory and network — useful on
|
|
18
|
+
// constrained CI runners.
|
|
19
|
+
//
|
|
20
|
+
// Trade-off (per concurrent worker, approx):
|
|
21
|
+
// - 1 open HTTPS socket to S3 (TLS state ~1–2 MB)
|
|
22
|
+
// - 1 open file descriptor + small read-stream buffer (~64 KB)
|
|
23
|
+
//
|
|
24
|
+
// Default: 10 (good balance for typical CI). Override with
|
|
25
|
+
// AMIKOO_VIDEO_UPLOAD_CONCURRENCY=<n>.
|
|
26
|
+
const VIDEO_UPLOAD_CONCURRENCY = Math.max(1, parseInt(process.env.AMIKOO_VIDEO_UPLOAD_CONCURRENCY || '', 10) || 10);
|
|
11
27
|
/**
|
|
12
|
-
*
|
|
28
|
+
* Extracts the local filesystem path of the video recorded by Playwright
|
|
29
|
+
* for a given test result.
|
|
30
|
+
*
|
|
31
|
+
* Playwright stores videos as attachments on the TestResult. We look for an
|
|
32
|
+
* attachment explicitly named 'video', and fall back to any attachment whose
|
|
33
|
+
* path ends in `.webm` (older Playwright versions or custom setups).
|
|
34
|
+
*
|
|
35
|
+
* @param result - Playwright TestResult object (typed as `any` to avoid a hard
|
|
36
|
+
* dependency on Playwright's reporter types here).
|
|
37
|
+
* @returns Absolute path to the video file, or an empty string if no video
|
|
38
|
+
* attachment exists for this test.
|
|
13
39
|
*/
|
|
14
40
|
function getVideoPath(result) {
|
|
15
41
|
const attachment = result.attachments?.find((a) => a.name === 'video' || a.path?.endsWith('.webm'));
|
|
16
42
|
return attachment?.path || '';
|
|
17
43
|
}
|
|
18
44
|
/**
|
|
19
|
-
*
|
|
45
|
+
* Prepares the video list for upload and asks the backend for presigned S3
|
|
46
|
+
* upload URLs in a single batch request.
|
|
47
|
+
*
|
|
48
|
+
* Steps:
|
|
49
|
+
* 1. Filter out videos whose local file no longer exists on disk.
|
|
50
|
+
* 2. Compute a deterministic S3 filename per video:
|
|
51
|
+
* `<executionNumber>_<testId>.webm`
|
|
52
|
+
* 3. Call `/execution/batch_upload_urls` once for all videos (single API
|
|
53
|
+
* round-trip instead of one per file).
|
|
54
|
+
*
|
|
55
|
+
* @param videos - Raw entries collected during the run, each with
|
|
56
|
+
* `{ path, testId }`.
|
|
57
|
+
* @param organizationId - Used as the S3 folder prefix: `videos/<orgId>`.
|
|
58
|
+
* @param executionNumber - Current run's execution number (prefix in S3 key).
|
|
59
|
+
* @param token - Amikoo API access token.
|
|
60
|
+
* @returns An object with the processed video metadata (`localPath` +
|
|
61
|
+
* `s3FileName`) and the matching presigned upload URLs. `uploadUrls`
|
|
62
|
+
* is `null` if the backend request failed or no videos were eligible.
|
|
20
63
|
*/
|
|
21
64
|
async function processVideos(videos, organizationId, executionNumber, token) {
|
|
22
65
|
let processed = [];
|
|
@@ -45,25 +88,48 @@ async function processVideos(videos, organizationId, executionNumber, token) {
|
|
|
45
88
|
return { videos: processed, uploadUrls };
|
|
46
89
|
}
|
|
47
90
|
/**
|
|
48
|
-
* Uploads a single video file to S3 using a presigned URL
|
|
91
|
+
* Uploads a single video file to S3 using a presigned URL.
|
|
92
|
+
*
|
|
93
|
+
* Memory note: the file is **streamed** from disk via `fs.createReadStream`
|
|
94
|
+
* and converted to a Web ReadableStream for `fetch`. This keeps RAM usage
|
|
95
|
+
* flat (a small ~64 KB internal buffer) regardless of file size — important
|
|
96
|
+
* because `.webm` recordings can be tens of MB each and we may have hundreds
|
|
97
|
+
* of them in a single run.
|
|
98
|
+
*
|
|
99
|
+
* The `duplex: 'half'` option is required by Node's undici-based fetch
|
|
100
|
+
* implementation whenever the request body is a stream.
|
|
101
|
+
*
|
|
102
|
+
* @param filePath - Absolute path to the local video file.
|
|
103
|
+
* @param uploadUrl - Presigned S3 PUT URL returned by `processVideos`.
|
|
104
|
+
* @returns `true` if S3 responded with a 2xx status, `false` otherwise (the
|
|
105
|
+
* caller logs and continues — one failed upload does not abort the
|
|
106
|
+
* batch).
|
|
49
107
|
*/
|
|
50
108
|
async function uploadVideoToS3(filePath, uploadUrl) {
|
|
51
109
|
let success = false;
|
|
52
110
|
if (fs_1.default.existsSync(filePath)) {
|
|
53
|
-
//
|
|
54
|
-
|
|
111
|
+
// Stream the file instead of buffering it in memory. This keeps RAM flat
|
|
112
|
+
// even when there are hundreds of large .webm files.
|
|
113
|
+
const stat = fs_1.default.statSync(filePath);
|
|
114
|
+
const nodeStream = fs_1.default.createReadStream(filePath);
|
|
115
|
+
// undici/fetch requires a Web ReadableStream for streamed bodies and
|
|
116
|
+
// `duplex: 'half'` when the body is a stream.
|
|
117
|
+
const body = stream_1.Readable.toWeb(nodeStream);
|
|
55
118
|
const response = await fetch(uploadUrl, {
|
|
56
119
|
method: 'PUT',
|
|
57
|
-
body
|
|
58
|
-
headers: {
|
|
120
|
+
body,
|
|
121
|
+
headers: {
|
|
122
|
+
'Content-Type': 'video/webm',
|
|
123
|
+
'Content-Length': String(stat.size),
|
|
124
|
+
},
|
|
125
|
+
// duplex is required by undici when the request body is a stream.
|
|
126
|
+
// Cast to any because not all TS lib versions include this field yet.
|
|
127
|
+
duplex: 'half',
|
|
59
128
|
});
|
|
60
129
|
success = response.ok;
|
|
61
130
|
if (!success) {
|
|
62
131
|
console.log(` ✖ Upload failed with status: ${response.status}`);
|
|
63
132
|
}
|
|
64
|
-
/* } catch (error) {
|
|
65
|
-
console.log('Error uploading video:', error);
|
|
66
|
-
}*/
|
|
67
133
|
}
|
|
68
134
|
else {
|
|
69
135
|
console.log(` ✖ Video file not found: ${filePath}`);
|
|
@@ -71,28 +137,48 @@ async function uploadVideoToS3(filePath, uploadUrl) {
|
|
|
71
137
|
return success;
|
|
72
138
|
}
|
|
73
139
|
/**
|
|
74
|
-
* Uploads
|
|
140
|
+
* Uploads every processed video to S3 in parallel, bounded by
|
|
141
|
+
* `VIDEO_UPLOAD_CONCURRENCY`.
|
|
142
|
+
*
|
|
143
|
+
* Implementation:
|
|
144
|
+
* - Builds a `Map<s3FileName, presignedUrl>` for O(1) lookup.
|
|
145
|
+
* - Spawns up to N "worker" promises that pull from a shared cursor; as
|
|
146
|
+
* soon as a worker finishes one upload, it grabs the next pending video.
|
|
147
|
+
* This keeps the connection pipeline saturated without ever holding more
|
|
148
|
+
* than N file streams open at once.
|
|
149
|
+
* - Failures are recorded per-video; one failed upload does not abort the
|
|
150
|
+
* rest. A summary line is printed at the end.
|
|
151
|
+
*
|
|
152
|
+
* @param videos - Processed video entries from `processVideos`.
|
|
153
|
+
* @param uploadUrls - Presigned URLs from the backend, keyed by `fileName`.
|
|
154
|
+
* @returns Per-video results: `{ fileName, success, error? }`.
|
|
75
155
|
*/
|
|
76
156
|
async function uploadVideosToS3(videos, uploadUrls) {
|
|
77
157
|
const results = [];
|
|
78
|
-
console.log(`📹 Uploading ${videos.length} video(s) to S3...`);
|
|
158
|
+
console.log(`📹 Uploading ${videos.length} video(s) to S3 (concurrency=${VIDEO_UPLOAD_CONCURRENCY})...`);
|
|
79
159
|
if (videos.length && uploadUrls?.length) {
|
|
80
160
|
// Create a map for quick lookup by S3 filename
|
|
81
161
|
const urlMap = new Map(uploadUrls.map(u => [u.fileName, u.uploadUrl]));
|
|
82
|
-
// Upload
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
162
|
+
// Upload in bounded-concurrency batches so we never hold more than
|
|
163
|
+
// VIDEO_UPLOAD_CONCURRENCY file streams open at once.
|
|
164
|
+
let cursor = 0;
|
|
165
|
+
const worker = async () => {
|
|
166
|
+
while (cursor < videos.length) {
|
|
167
|
+
const index = cursor++;
|
|
168
|
+
const video = videos[index];
|
|
169
|
+
const uploadUrl = urlMap.get(video.s3FileName);
|
|
170
|
+
if (uploadUrl) {
|
|
171
|
+
const success = await uploadVideoToS3(video.localPath, uploadUrl);
|
|
172
|
+
results.push({ fileName: video.s3FileName, success });
|
|
173
|
+
}
|
|
174
|
+
else {
|
|
175
|
+
console.log(` ✖ No upload URL found for: ${video.s3FileName}`);
|
|
176
|
+
results.push({ fileName: video.s3FileName, success: false, error: 'No upload URL' });
|
|
177
|
+
}
|
|
88
178
|
}
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
}
|
|
93
|
-
});
|
|
94
|
-
const uploadResults = await Promise.all(uploadPromises);
|
|
95
|
-
results.push(...uploadResults);
|
|
179
|
+
};
|
|
180
|
+
const workerCount = Math.min(VIDEO_UPLOAD_CONCURRENCY, videos.length);
|
|
181
|
+
await Promise.all(Array.from({ length: workerCount }, () => worker()));
|
|
96
182
|
const successCount = results.filter(r => r.success).length;
|
|
97
183
|
console.log(`✔ Uploaded ${successCount}/${results.length} video(s) to S3`);
|
|
98
184
|
}
|
package/dist/videoUtils.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"videoUtils.js","sourceRoot":"","sources":["../controlHub/videoUtils.ts"],"names":[],"mappings":";;;;;
|
|
1
|
+
{"version":3,"file":"videoUtils.js","sourceRoot":"","sources":["../controlHub/videoUtils.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@muuktest/amikoo-reporter",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"description": "Playwright reporter for Amikoo - automatically installs and configures test reporting to Amikoo AI",
|
|
5
5
|
"main": "dist/controlHubReporter.js",
|
|
6
6
|
"types": "dist/controlHubReporter.d.ts",
|