@flakiness/sdk 0.150.1 → 0.151.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.
@@ -1,113 +0,0 @@
1
- import stableObjectHash from "stable-hash";
2
- class Multimap {
3
- _map = /* @__PURE__ */ new Map();
4
- set(key, value) {
5
- const set = this._map.get(key) ?? /* @__PURE__ */ new Set();
6
- this._map.set(key, set);
7
- set.add(value);
8
- }
9
- getAll(key) {
10
- return Array.from(this._map.get(key) ?? []);
11
- }
12
- }
13
- function normalizeReport(report) {
14
- const gEnvs = /* @__PURE__ */ new Map();
15
- const gSuites = /* @__PURE__ */ new Map();
16
- const gTests = new Multimap();
17
- const gSuiteIds = /* @__PURE__ */ new Map();
18
- const gTestIds = /* @__PURE__ */ new Map();
19
- const gEnvIds = /* @__PURE__ */ new Map();
20
- const gSuiteChildren = new Multimap();
21
- const gSuiteTests = new Multimap();
22
- for (const env of report.environments) {
23
- const envId = computeEnvId(env);
24
- gEnvs.set(envId, env);
25
- gEnvIds.set(env, envId);
26
- }
27
- const usedEnvIds = /* @__PURE__ */ new Set();
28
- function visitTests(tests, suiteId) {
29
- for (const test of tests ?? []) {
30
- const testId = computeTestId(test, suiteId);
31
- gTests.set(testId, test);
32
- gTestIds.set(test, testId);
33
- gSuiteTests.set(suiteId, test);
34
- for (const attempt of test.attempts) {
35
- const env = report.environments[attempt.environmentIdx];
36
- const envId = gEnvIds.get(env);
37
- usedEnvIds.add(envId);
38
- }
39
- }
40
- }
41
- function visitSuite(suite, parentSuiteId) {
42
- const suiteId = computeSuiteId(suite, parentSuiteId);
43
- gSuites.set(suiteId, suite);
44
- gSuiteIds.set(suite, suiteId);
45
- for (const childSuite of suite.suites ?? []) {
46
- visitSuite(childSuite, suiteId);
47
- gSuiteChildren.set(suiteId, childSuite);
48
- }
49
- visitTests(suite.tests ?? [], suiteId);
50
- }
51
- function transformTests(tests) {
52
- const testIds = new Set(tests.map((test) => gTestIds.get(test)));
53
- return [...testIds].map((testId) => {
54
- const tests2 = gTests.getAll(testId);
55
- const tags = tests2.map((test) => test.tags ?? []).flat();
56
- return {
57
- location: tests2[0].location,
58
- title: tests2[0].title,
59
- tags: tags.length ? tags : void 0,
60
- attempts: tests2.map((t) => t.attempts).flat().map((attempt) => ({
61
- ...attempt,
62
- environmentIdx: envIdToIndex.get(gEnvIds.get(report.environments[attempt.environmentIdx]))
63
- }))
64
- };
65
- });
66
- }
67
- function transformSuites(suites) {
68
- const suiteIds = new Set(suites.map((suite) => gSuiteIds.get(suite)));
69
- return [...suiteIds].map((suiteId) => {
70
- const suite = gSuites.get(suiteId);
71
- return {
72
- location: suite.location,
73
- title: suite.title,
74
- type: suite.type,
75
- suites: transformSuites(gSuiteChildren.getAll(suiteId)),
76
- tests: transformTests(gSuiteTests.getAll(suiteId))
77
- };
78
- });
79
- }
80
- visitTests(report.tests ?? [], "suiteless");
81
- for (const suite of report.suites)
82
- visitSuite(suite);
83
- const newEnvironments = [...usedEnvIds];
84
- const envIdToIndex = new Map(newEnvironments.map((envId, index) => [envId, index]));
85
- return {
86
- ...report,
87
- environments: newEnvironments.map((envId) => gEnvs.get(envId)),
88
- suites: transformSuites(report.suites),
89
- tests: transformTests(report.tests ?? [])
90
- };
91
- }
92
- function computeEnvId(env) {
93
- return stableObjectHash(env);
94
- }
95
- function computeSuiteId(suite, parentSuiteId) {
96
- return stableObjectHash({
97
- parentSuiteId: parentSuiteId ?? "",
98
- type: suite.type,
99
- file: suite.location?.file ?? "",
100
- title: suite.title
101
- });
102
- }
103
- function computeTestId(test, suiteId) {
104
- return stableObjectHash({
105
- suiteId,
106
- file: test.location?.file ?? "",
107
- title: test.title
108
- });
109
- }
110
- export {
111
- normalizeReport
112
- };
113
- //# sourceMappingURL=normalizeReport.js.map
@@ -1,19 +0,0 @@
1
- import { createEnvironment } from "./createEnvironment.js";
2
- import { createTestStepSnippetsInplace } from "./createTestStepSnippets.js";
3
- import { normalizeReport } from "./normalizeReport.js";
4
- import { stripAnsi } from "./stripAnsi.js";
5
- import {
6
- createDataAttachment,
7
- createFileAttachment
8
- } from "./uploadReport.js";
9
- import { visitTests } from "./visitTests.js";
10
- export {
11
- createDataAttachment,
12
- createEnvironment,
13
- createFileAttachment,
14
- createTestStepSnippetsInplace,
15
- normalizeReport,
16
- stripAnsi,
17
- visitTests
18
- };
19
- //# sourceMappingURL=reportUtils.js.map
@@ -1,9 +0,0 @@
1
- import { normalizeReport } from "./normalizeReport.js";
2
- import { stripAnsi } from "./stripAnsi.js";
3
- import { visitTests } from "./visitTests.js";
4
- export {
5
- normalizeReport,
6
- stripAnsi,
7
- visitTests
8
- };
9
- //# sourceMappingURL=reportUtilsBrowser.js.map
package/lib/showReport.js DELETED
@@ -1,28 +0,0 @@
1
- import chalk from "chalk";
2
- import open from "open";
3
- import { randomUUIDBase62 } from "./_internalUtils.js";
4
- import { FlakinessProjectConfig } from "./flakinessProjectConfig.js";
5
- import { StaticServer } from "./staticServer.js";
6
- async function showReport(reportFolder) {
7
- const config = await FlakinessProjectConfig.load();
8
- const projectPublicId = config.projectPublicId();
9
- const reportViewerEndpoint = config.reportViewerUrl();
10
- const token = randomUUIDBase62();
11
- const server = new StaticServer(token, reportFolder, reportViewerEndpoint);
12
- await server.start(9373, "127.0.0.1");
13
- const url = new URL(reportViewerEndpoint);
14
- url.searchParams.set("port", String(server.port()));
15
- url.searchParams.set("token", token);
16
- if (projectPublicId)
17
- url.searchParams.set("ppid", projectPublicId);
18
- console.log(chalk.cyan(`
19
- Serving Flakiness report at ${url.toString()}
20
- Press Ctrl+C to quit.`));
21
- await open(url.toString());
22
- await new Promise(() => {
23
- });
24
- }
25
- export {
26
- showReport
27
- };
28
- //# sourceMappingURL=showReport.js.map
@@ -1,148 +0,0 @@
1
- import debug from "debug";
2
- import * as fs from "fs";
3
- import * as http from "http";
4
- import * as path from "path";
5
- const log = debug("fk:static_server");
6
- class StaticServer {
7
- _server;
8
- _absoluteFolderPath;
9
- _pathPrefix;
10
- _cors;
11
- _mimeTypes = {
12
- ".html": "text/html",
13
- ".js": "text/javascript",
14
- ".css": "text/css",
15
- ".json": "application/json",
16
- ".png": "image/png",
17
- ".jpg": "image/jpeg",
18
- ".gif": "image/gif",
19
- ".svg": "image/svg+xml",
20
- ".ico": "image/x-icon",
21
- ".txt": "text/plain"
22
- };
23
- constructor(pathPrefix, folderPath, cors) {
24
- this._pathPrefix = "/" + pathPrefix.replace(/^\//, "").replace(/\/$/, "");
25
- this._absoluteFolderPath = path.resolve(folderPath);
26
- this._cors = cors;
27
- this._server = http.createServer((req, res) => this._handleRequest(req, res));
28
- }
29
- port() {
30
- const address = this._server.address();
31
- if (!address)
32
- return void 0;
33
- return address.port;
34
- }
35
- address() {
36
- const address = this._server.address();
37
- if (!address)
38
- return void 0;
39
- const displayHost = address.address.includes(":") ? `[${address.address}]` : address.address;
40
- return `http://${displayHost}:${address.port}${this._pathPrefix}`;
41
- }
42
- async _startServer(port, host) {
43
- let okListener;
44
- let errListener;
45
- const result = new Promise((resolve, reject) => {
46
- okListener = resolve;
47
- errListener = reject;
48
- }).finally(() => {
49
- this._server.removeListener("listening", okListener);
50
- this._server.removeListener("error", errListener);
51
- });
52
- this._server.once("listening", okListener);
53
- this._server.once("error", errListener);
54
- this._server.listen(port, host);
55
- await result;
56
- log('Serving "%s" on "%s"', this._absoluteFolderPath, this.address());
57
- }
58
- async start(port, host = "127.0.0.1") {
59
- if (port === 0) {
60
- await this._startServer(port, host);
61
- return this.address();
62
- }
63
- for (let i = 0; i < 20; ++i) {
64
- const err = await this._startServer(port, host).then(() => void 0).catch((e) => e);
65
- if (!err)
66
- return this.address();
67
- if (err.code !== "EADDRINUSE")
68
- throw err;
69
- log("Port %d is busy (EADDRINUSE). Trying next port...", port);
70
- port = port + 1;
71
- if (port > 65535)
72
- port = 4e3;
73
- }
74
- log("All sequential ports busy. Falling back to random port.");
75
- await this._startServer(0, host);
76
- return this.address();
77
- }
78
- stop() {
79
- return new Promise((resolve, reject) => {
80
- this._server.close((err) => {
81
- if (err) {
82
- log("Error stopping server: %o", err);
83
- reject(err);
84
- } else {
85
- log("Server stopped.");
86
- resolve();
87
- }
88
- });
89
- });
90
- }
91
- _errorResponse(req, res, code, text) {
92
- res.writeHead(code, { "Content-Type": "text/plain" });
93
- res.end(text);
94
- log(`[${code}] ${req.method} ${req.url}`);
95
- }
96
- _handleRequest(req, res) {
97
- const { url, method } = req;
98
- if (this._cors) {
99
- res.setHeader("Access-Control-Allow-Headers", "*");
100
- res.setHeader("Access-Control-Allow-Origin", this._cors);
101
- res.setHeader("Access-Control-Allow-Methods", "*");
102
- if (req.method === "OPTIONS") {
103
- res.writeHead(204);
104
- res.end();
105
- return;
106
- }
107
- }
108
- if (method !== "GET") {
109
- this._errorResponse(req, res, 405, "Method Not Allowed");
110
- return;
111
- }
112
- req.on("aborted", () => log(`ABORTED ${req.method} ${req.url}`));
113
- res.on("close", () => {
114
- if (!res.headersSent) log(`CLOSED BEFORE SEND ${req.method} ${req.url}`);
115
- });
116
- if (!url || !url.startsWith(this._pathPrefix)) {
117
- this._errorResponse(req, res, 404, "Not Found");
118
- return;
119
- }
120
- const relativePath = url.slice(this._pathPrefix.length);
121
- const safeSuffix = path.normalize(decodeURIComponent(relativePath)).replace(/^(\.\.[\/\\])+/, "");
122
- const filePath = path.join(this._absoluteFolderPath, safeSuffix);
123
- if (!filePath.startsWith(this._absoluteFolderPath)) {
124
- this._errorResponse(req, res, 403, "Forbidden");
125
- return;
126
- }
127
- fs.stat(filePath, (err, stats) => {
128
- if (err || !stats.isFile()) {
129
- this._errorResponse(req, res, 404, "File Not Found");
130
- return;
131
- }
132
- const ext = path.extname(filePath).toLowerCase();
133
- const contentType = this._mimeTypes[ext] || "application/octet-stream";
134
- res.writeHead(200, { "Content-Type": contentType });
135
- log(`[200] ${req.method} ${req.url} -> ${filePath}`);
136
- const readStream = fs.createReadStream(filePath);
137
- readStream.pipe(res);
138
- readStream.on("error", (err2) => {
139
- log("Stream error: %o", err2);
140
- res.end();
141
- });
142
- });
143
- }
144
- }
145
- export {
146
- StaticServer
147
- };
148
- //# sourceMappingURL=staticServer.js.map
package/lib/stripAnsi.js DELETED
@@ -1,8 +0,0 @@
1
- const ansiRegex = new RegExp("[\\u001B\\u009B][[\\]()#;?]*(?:(?:(?:[a-zA-Z\\d]*(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]*)*)?\\u0007)|(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PR-TZcf-ntqry=><~]))", "g");
2
- function stripAnsi(str) {
3
- return str.replace(ansiRegex, "");
4
- }
5
- export {
6
- stripAnsi
7
- };
8
- //# sourceMappingURL=stripAnsi.js.map
@@ -1,91 +0,0 @@
1
- import { spawnSync } from "child_process";
2
- import os from "os";
3
- function getAvailableMemMacOS() {
4
- const lines = spawnSync("vm_stat", { encoding: "utf8" }).stdout.trim().split("\n");
5
- const pageSize = parseInt(lines[0].match(/page size of (\d+) bytes/)[1], 10);
6
- if (isNaN(pageSize)) {
7
- console.warn("[flakiness.io] Error detecting macos page size");
8
- return 0;
9
- }
10
- let totalFree = 0;
11
- for (const line of lines) {
12
- if (/Pages (free|inactive|speculative):/.test(line)) {
13
- const match = line.match(/\d+/);
14
- if (match)
15
- totalFree += parseInt(match[0], 10);
16
- }
17
- }
18
- return totalFree * pageSize;
19
- }
20
- function getSystemUtilization() {
21
- let idleTicks = 0;
22
- let totalTicks = 0;
23
- for (const cpu of os.cpus()) {
24
- totalTicks += cpu.times.user + cpu.times.nice + cpu.times.sys + cpu.times.irq + cpu.times.idle;
25
- idleTicks += cpu.times.idle;
26
- }
27
- return {
28
- idleTicks,
29
- totalTicks,
30
- timestamp: Date.now(),
31
- freeBytes: os.platform() === "darwin" ? getAvailableMemMacOS() : os.freemem()
32
- };
33
- }
34
- function toFKUtilization(sample, previous) {
35
- const idleTicks = sample.idleTicks - previous.idleTicks;
36
- const totalTicks = sample.totalTicks - previous.totalTicks;
37
- const cpuUtilization = Math.floor((1 - idleTicks / totalTicks) * 1e4) / 100;
38
- const memoryUtilization = Math.floor((1 - sample.freeBytes / os.totalmem()) * 1e4) / 100;
39
- return {
40
- cpuUtilization,
41
- memoryUtilization,
42
- dts: sample.timestamp - previous.timestamp
43
- };
44
- }
45
- class SystemUtilizationSampler {
46
- /**
47
- * The accumulated system utilization data.
48
- *
49
- * This object is populated as samples are collected and can be directly included in
50
- * Flakiness reports. It contains:
51
- * - `samples` - Array of utilization samples with CPU/memory percentages and durations
52
- * - `startTimestamp` - Timestamp when sampling began
53
- * - `totalMemoryBytes` - Total system memory in bytes
54
- */
55
- result;
56
- _lastSample = getSystemUtilization();
57
- _timer;
58
- /**
59
- * Creates a new SystemUtilizationSampler and starts sampling immediately.
60
- *
61
- * The first sample is collected after 50ms, and subsequent samples are collected
62
- * every 1000ms. Call `dispose()` to stop sampling and clean up resources.
63
- */
64
- constructor() {
65
- this.result = {
66
- samples: [],
67
- startTimestamp: this._lastSample.timestamp,
68
- totalMemoryBytes: os.totalmem()
69
- };
70
- this._timer = setTimeout(this._addSample.bind(this), 50);
71
- }
72
- _addSample() {
73
- const sample = getSystemUtilization();
74
- this.result.samples.push(toFKUtilization(sample, this._lastSample));
75
- this._lastSample = sample;
76
- this._timer = setTimeout(this._addSample.bind(this), 1e3);
77
- }
78
- /**
79
- * Stops sampling and cleans up resources.
80
- *
81
- * Call this method when you're done collecting utilization data to stop the sampling
82
- * timer and prevent memory leaks. The `result` object remains accessible after disposal.
83
- */
84
- dispose() {
85
- clearTimeout(this._timer);
86
- }
87
- }
88
- export {
89
- SystemUtilizationSampler
90
- };
91
- //# sourceMappingURL=systemUtilizationSampler.js.map
@@ -1,175 +0,0 @@
1
- import assert from "assert";
2
- import fs from "fs";
3
- import { URL } from "url";
4
- import { compressTextAsync, retryWithBackoff, sha1File, sha1Text } from "./_internalUtils.js";
5
- async function createFileAttachment(contentType, filePath) {
6
- return {
7
- type: "file",
8
- contentType,
9
- id: await sha1File(filePath),
10
- path: filePath
11
- };
12
- }
13
- async function createDataAttachment(contentType, data) {
14
- return {
15
- type: "buffer",
16
- contentType,
17
- id: sha1Text(data),
18
- body: data
19
- };
20
- }
21
- async function uploadReport(report, attachments, options) {
22
- const flakinessAccessToken = options?.flakinessAccessToken ?? process.env["FLAKINESS_ACCESS_TOKEN"];
23
- const flakinessEndpoint = options?.flakinessEndpoint ?? process.env["FLAKINESS_ENDPOINT"] ?? "https://flakiness.io";
24
- const logger = options?.logger ?? console;
25
- if (!flakinessAccessToken) {
26
- const reason = "No FLAKINESS_ACCESS_TOKEN found";
27
- if (process.env.CI)
28
- logger.warn(`[flakiness.io] \u26A0 Skipping upload: ${reason}`);
29
- return { status: "skipped", reason };
30
- }
31
- try {
32
- const upload = new ReportUpload(report, attachments, { flakinessAccessToken, flakinessEndpoint });
33
- const uploadResult = await upload.upload();
34
- if (!uploadResult.success) {
35
- const errorMessage = uploadResult.message || "Unknown upload error";
36
- logger.error(`[flakiness.io] \u2715 Failed to upload: ${errorMessage}`);
37
- if (options?.throwOnFailure)
38
- throw new Error(`Flakiness upload failed: ${errorMessage}`);
39
- return { status: "failed", error: errorMessage };
40
- }
41
- logger.log(`[flakiness.io] \u2713 Uploaded to ${uploadResult.reportUrl}`);
42
- return { status: "success", reportUrl: uploadResult.reportUrl };
43
- } catch (e) {
44
- const errorMessage = e.message || String(e);
45
- logger.error(`[flakiness.io] \u2715 Unexpected error during upload: ${errorMessage}`);
46
- if (options?.throwOnFailure)
47
- throw e;
48
- return { status: "failed", error: errorMessage };
49
- }
50
- }
51
- const HTTP_BACKOFF = [100, 500, 1e3, 1e3, 1e3, 1e3];
52
- class ReportUpload {
53
- _report;
54
- _attachments;
55
- _options;
56
- constructor(report, attachments, options) {
57
- this._options = options;
58
- this._report = report;
59
- this._attachments = attachments;
60
- }
61
- async _api(pathname, token, body) {
62
- const url = new URL(this._options.flakinessEndpoint);
63
- url.pathname = pathname;
64
- return await fetch(url, {
65
- method: "POST",
66
- headers: {
67
- "Authorization": `Bearer ${token}`,
68
- "Content-Type": "application/json"
69
- },
70
- body: body ? JSON.stringify(body) : void 0
71
- }).then(async (response) => !response.ok ? {
72
- result: void 0,
73
- error: response.status + " " + url.href + " " + await response.text()
74
- } : {
75
- result: await response.json(),
76
- error: void 0
77
- }).catch((error) => ({
78
- result: void 0,
79
- error
80
- }));
81
- }
82
- async upload() {
83
- const response = await this._api("/api/upload/start", this._options.flakinessAccessToken);
84
- if (response?.error || !response.result)
85
- return { success: false, message: response.error };
86
- const webUrl = new URL(response.result.webUrl, this._options.flakinessEndpoint).toString();
87
- const attachmentsPresignedUrls = await this._api("/api/upload/attachments", response.result.uploadToken, {
88
- attachmentIds: this._attachments.map((a) => a.id)
89
- });
90
- if (attachmentsPresignedUrls?.error || !attachmentsPresignedUrls.result)
91
- return { success: false, message: attachmentsPresignedUrls.error };
92
- const attachments = new Map(attachmentsPresignedUrls.result.map((a) => [a.attachmentId, a.presignedUrl]));
93
- await Promise.all([
94
- this._uploadReport(JSON.stringify(this._report), response.result.presignedReportUrl),
95
- ...this._attachments.map((attachment) => {
96
- const uploadURL = attachments.get(attachment.id);
97
- if (!uploadURL)
98
- throw new Error("Internal error: missing upload URL for attachment!");
99
- return this._uploadAttachment(attachment, uploadURL);
100
- })
101
- ]);
102
- await this._api("/api/upload/finish", response.result.uploadToken);
103
- return { success: true, reportUrl: webUrl };
104
- }
105
- async _uploadReport(data, uploadUrl) {
106
- const compressed = await compressTextAsync(data);
107
- const headers = {
108
- "Content-Type": "application/json",
109
- "Content-Length": Buffer.byteLength(compressed) + "",
110
- "Content-Encoding": "br"
111
- };
112
- await retryWithBackoff(async () => {
113
- const response = await fetch(uploadUrl, {
114
- method: "PUT",
115
- headers,
116
- body: Buffer.from(compressed)
117
- });
118
- if (!response.ok) {
119
- throw new Error(`Request to ${uploadUrl} failed with ${response.status}`);
120
- }
121
- await response.arrayBuffer();
122
- }, HTTP_BACKOFF);
123
- }
124
- async _uploadAttachment(attachment, uploadUrl) {
125
- const mimeType = attachment.contentType.toLocaleLowerCase().trim();
126
- const compressable = mimeType.startsWith("text/") || mimeType.endsWith("+json") || mimeType.endsWith("+text") || mimeType.endsWith("+xml");
127
- if (!compressable && attachment.type === "file") {
128
- await retryWithBackoff(async () => {
129
- const fileBuffer = await fs.promises.readFile(attachment.path);
130
- const response = await fetch(uploadUrl, {
131
- method: "PUT",
132
- headers: {
133
- "Content-Type": attachment.contentType,
134
- "Content-Length": fileBuffer.length + ""
135
- },
136
- body: new Uint8Array(fileBuffer)
137
- });
138
- if (!response.ok) {
139
- throw new Error(`Request to ${uploadUrl} failed with ${response.status}`);
140
- }
141
- await response.arrayBuffer();
142
- }, HTTP_BACKOFF);
143
- return;
144
- }
145
- let buffer = attachment.type === "buffer" ? attachment.body : await fs.promises.readFile(attachment.path);
146
- assert(buffer);
147
- const encoding = compressable ? "br" : void 0;
148
- if (compressable)
149
- buffer = await compressTextAsync(buffer);
150
- const headers = {
151
- "Content-Type": attachment.contentType,
152
- "Content-Length": Buffer.byteLength(buffer) + ""
153
- };
154
- if (encoding) {
155
- headers["Content-Encoding"] = encoding;
156
- }
157
- await retryWithBackoff(async () => {
158
- const response = await fetch(uploadUrl, {
159
- method: "PUT",
160
- headers,
161
- body: new Uint8Array(buffer)
162
- });
163
- if (!response.ok) {
164
- throw new Error(`Request to ${uploadUrl} failed with ${response.status}`);
165
- }
166
- await response.arrayBuffer();
167
- }, HTTP_BACKOFF);
168
- }
169
- }
170
- export {
171
- createDataAttachment,
172
- createFileAttachment,
173
- uploadReport
174
- };
175
- //# sourceMappingURL=uploadReport.js.map
package/lib/visitTests.js DELETED
@@ -1,18 +0,0 @@
1
- function visitTests(report, testVisitor) {
2
- function visitSuite(suite, parents) {
3
- parents.push(suite);
4
- for (const test of suite.tests ?? [])
5
- testVisitor(test, parents);
6
- for (const childSuite of suite.suites ?? [])
7
- visitSuite(childSuite, parents);
8
- parents.pop();
9
- }
10
- for (const test of report.tests ?? [])
11
- testVisitor(test, []);
12
- for (const suite of report.suites)
13
- visitSuite(suite, []);
14
- }
15
- export {
16
- visitTests
17
- };
18
- //# sourceMappingURL=visitTests.js.map
@@ -1,30 +0,0 @@
1
- import fs from "fs";
2
- import path from "path";
3
- async function writeReport(report, attachments, outputFolder) {
4
- const reportPath = path.join(outputFolder, "report.json");
5
- const attachmentsFolder = path.join(outputFolder, "attachments");
6
- await fs.promises.rm(outputFolder, { recursive: true, force: true });
7
- await fs.promises.mkdir(outputFolder, { recursive: true });
8
- await fs.promises.writeFile(reportPath, JSON.stringify(report), "utf-8");
9
- if (attachments.length)
10
- await fs.promises.mkdir(attachmentsFolder);
11
- const movedAttachments = [];
12
- for (const attachment of attachments) {
13
- const attachmentPath = path.join(attachmentsFolder, attachment.id);
14
- if (attachment.type === "file")
15
- await fs.promises.cp(attachment.path, attachmentPath);
16
- else if (attachment.type === "buffer")
17
- await fs.promises.writeFile(attachmentPath, attachment.body);
18
- movedAttachments.push({
19
- type: "file",
20
- contentType: attachment.contentType,
21
- id: attachment.id,
22
- path: attachmentPath
23
- });
24
- }
25
- return movedAttachments;
26
- }
27
- export {
28
- writeReport
29
- };
30
- //# sourceMappingURL=writeReport.js.map