@supatest/cypress-reporter 0.0.4 → 0.0.6
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/index.cjs +304 -170
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +307 -173
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -909,8 +909,7 @@ var require_dist2 = __commonJS({
|
|
|
909
909
|
});
|
|
910
910
|
|
|
911
911
|
// src/index.ts
|
|
912
|
-
import
|
|
913
|
-
import os from "os";
|
|
912
|
+
import fs2 from "fs";
|
|
914
913
|
import path from "path";
|
|
915
914
|
|
|
916
915
|
// ../node_modules/.pnpm/simple-git@3.27.0/node_modules/simple-git/dist/esm/index.js
|
|
@@ -5465,8 +5464,9 @@ init_git_response_error();
|
|
|
5465
5464
|
var simpleGit = gitInstanceFactory;
|
|
5466
5465
|
|
|
5467
5466
|
// ../reporter-core/dist/index.js
|
|
5468
|
-
import fs2 from "fs";
|
|
5469
5467
|
import { createHash } from "crypto";
|
|
5468
|
+
import fs from "fs";
|
|
5469
|
+
import os from "os";
|
|
5470
5470
|
import fs3 from "fs";
|
|
5471
5471
|
var __require2 = /* @__PURE__ */ ((x) => typeof __require !== "undefined" ? __require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
5472
5472
|
get: (a, b) => (typeof __require !== "undefined" ? __require : a)[b]
|
|
@@ -5578,12 +5578,16 @@ var SupatestApiClient = class {
|
|
|
5578
5578
|
this.options.timeoutMs
|
|
5579
5579
|
);
|
|
5580
5580
|
try {
|
|
5581
|
+
const headers = {
|
|
5582
|
+
"Content-Type": "application/json",
|
|
5583
|
+
Authorization: `Bearer ${this.options.apiKey}`
|
|
5584
|
+
};
|
|
5585
|
+
if (this.options.projectId) {
|
|
5586
|
+
headers["X-Project-Id"] = this.options.projectId;
|
|
5587
|
+
}
|
|
5581
5588
|
const response = await _fetch(url, {
|
|
5582
5589
|
method,
|
|
5583
|
-
headers
|
|
5584
|
-
"Content-Type": "application/json",
|
|
5585
|
-
Authorization: `Bearer ${this.options.apiKey}`
|
|
5586
|
-
},
|
|
5590
|
+
headers,
|
|
5587
5591
|
body: body ? JSON.stringify(body) : void 0,
|
|
5588
5592
|
signal: controller.signal
|
|
5589
5593
|
});
|
|
@@ -5607,6 +5611,10 @@ var SupatestApiClient = class {
|
|
|
5607
5611
|
console.log(JSON.stringify(data, null, 2));
|
|
5608
5612
|
}
|
|
5609
5613
|
};
|
|
5614
|
+
var DEFAULT_API_URL = "https://code-api.supatest.ai";
|
|
5615
|
+
var DEFAULT_MAX_CONCURRENT_UPLOADS = 5;
|
|
5616
|
+
var DEFAULT_RETRY_ATTEMPTS = 3;
|
|
5617
|
+
var DEFAULT_TIMEOUT_MS = 3e4;
|
|
5610
5618
|
var ErrorCollector = class {
|
|
5611
5619
|
errors = [];
|
|
5612
5620
|
recordError(category, message, context) {
|
|
@@ -5772,105 +5780,37 @@ async function getLocalGitInfo(rootDir) {
|
|
|
5772
5780
|
return {};
|
|
5773
5781
|
}
|
|
5774
5782
|
}
|
|
5775
|
-
|
|
5776
|
-
|
|
5777
|
-
|
|
5778
|
-
|
|
5779
|
-
|
|
5780
|
-
|
|
5781
|
-
|
|
5782
|
-
|
|
5783
|
-
|
|
5784
|
-
|
|
5785
|
-
|
|
5786
|
-
|
|
5787
|
-
} catch (error) {
|
|
5788
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
5789
|
-
console.warn(
|
|
5790
|
-
`[supatest][dry-run] Cannot access file ${filePath}: ${message}`
|
|
5791
|
-
);
|
|
5792
|
-
}
|
|
5793
|
-
return;
|
|
5794
|
-
}
|
|
5795
|
-
let fileBuffer;
|
|
5796
|
-
try {
|
|
5797
|
-
fileBuffer = await fs2.promises.readFile(filePath);
|
|
5798
|
-
} catch (error) {
|
|
5799
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
5800
|
-
throw new Error(`Failed to read file ${filePath}: ${message}`);
|
|
5801
|
-
}
|
|
5802
|
-
await withRetry(async () => {
|
|
5803
|
-
const controller = new _AbortController();
|
|
5804
|
-
const timeoutId = setTimeout(
|
|
5805
|
-
() => controller.abort(),
|
|
5806
|
-
this.options.timeoutMs
|
|
5807
|
-
);
|
|
5808
|
-
try {
|
|
5809
|
-
const response = await _fetch(signedUrl, {
|
|
5810
|
-
method: "PUT",
|
|
5811
|
-
headers: {
|
|
5812
|
-
"Content-Type": contentType,
|
|
5813
|
-
"Content-Length": String(fileBuffer.length)
|
|
5814
|
-
},
|
|
5815
|
-
body: fileBuffer,
|
|
5816
|
-
signal: controller.signal
|
|
5817
|
-
});
|
|
5818
|
-
if (!response.ok) {
|
|
5819
|
-
const error = new Error(
|
|
5820
|
-
`S3 upload failed: ${response.status} ${response.statusText}`
|
|
5821
|
-
);
|
|
5822
|
-
error.statusCode = response.status;
|
|
5823
|
-
throw error;
|
|
5824
|
-
}
|
|
5825
|
-
} finally {
|
|
5826
|
-
clearTimeout(timeoutId);
|
|
5827
|
-
}
|
|
5828
|
-
}, defaultRetryConfig);
|
|
5829
|
-
}
|
|
5830
|
-
async uploadBatch(items, signedUploads) {
|
|
5831
|
-
const pLimit = (await import("p-limit")).default;
|
|
5832
|
-
const limit = pLimit(this.options.maxConcurrent);
|
|
5833
|
-
const results = await Promise.allSettled(
|
|
5834
|
-
items.map(
|
|
5835
|
-
(item, index) => limit(async () => {
|
|
5836
|
-
var _a2, _b;
|
|
5837
|
-
try {
|
|
5838
|
-
await this.upload(item.signedUrl, item.filePath, item.contentType);
|
|
5839
|
-
return {
|
|
5840
|
-
success: true,
|
|
5841
|
-
attachmentId: (_a2 = signedUploads[index]) == null ? void 0 : _a2.attachmentId
|
|
5842
|
-
};
|
|
5843
|
-
} catch (error) {
|
|
5844
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
5845
|
-
return {
|
|
5846
|
-
success: false,
|
|
5847
|
-
attachmentId: (_b = signedUploads[index]) == null ? void 0 : _b.attachmentId,
|
|
5848
|
-
error: `${item.filePath}: ${message}`
|
|
5849
|
-
};
|
|
5850
|
-
}
|
|
5851
|
-
})
|
|
5852
|
-
)
|
|
5853
|
-
);
|
|
5854
|
-
return results.map((result, index) => {
|
|
5855
|
-
var _a2;
|
|
5856
|
-
if (result.status === "fulfilled") {
|
|
5857
|
-
return result.value;
|
|
5858
|
-
}
|
|
5859
|
-
const message = result.reason instanceof Error ? result.reason.message : String(result.reason);
|
|
5860
|
-
return {
|
|
5861
|
-
success: false,
|
|
5862
|
-
attachmentId: (_a2 = signedUploads[index]) == null ? void 0 : _a2.attachmentId,
|
|
5863
|
-
error: message
|
|
5864
|
-
};
|
|
5865
|
-
});
|
|
5783
|
+
async function getGitInfoWithCI(rootDir) {
|
|
5784
|
+
const ciGitInfo = {
|
|
5785
|
+
branch: process.env.GITHUB_REF_NAME ?? process.env.GITHUB_HEAD_REF ?? process.env.CI_COMMIT_BRANCH ?? process.env.GITLAB_CI_COMMIT_BRANCH ?? process.env.GIT_BRANCH,
|
|
5786
|
+
commit: process.env.GITHUB_SHA ?? process.env.CI_COMMIT_SHA ?? process.env.GITLAB_CI_COMMIT_SHA ?? process.env.GIT_COMMIT,
|
|
5787
|
+
commitMessage: process.env.CI_COMMIT_MESSAGE ?? process.env.GITLAB_CI_COMMIT_MESSAGE,
|
|
5788
|
+
repo: process.env.GITHUB_REPOSITORY ?? process.env.CI_PROJECT_PATH ?? process.env.GITLAB_CI_PROJECT_PATH ?? process.env.GIT_REPO,
|
|
5789
|
+
author: process.env.GITHUB_ACTOR ?? process.env.GITLAB_USER_NAME,
|
|
5790
|
+
authorEmail: process.env.GITLAB_USER_EMAIL,
|
|
5791
|
+
tag: process.env.GITHUB_REF_TYPE === "tag" ? process.env.GITHUB_REF_NAME : void 0
|
|
5792
|
+
};
|
|
5793
|
+
if (ciGitInfo.branch || ciGitInfo.commit) {
|
|
5794
|
+
return ciGitInfo;
|
|
5866
5795
|
}
|
|
5867
|
-
|
|
5796
|
+
const localGitInfo = await getLocalGitInfo(rootDir);
|
|
5797
|
+
return {
|
|
5798
|
+
branch: ciGitInfo.branch ?? localGitInfo.branch,
|
|
5799
|
+
commit: ciGitInfo.commit ?? localGitInfo.commit,
|
|
5800
|
+
commitMessage: ciGitInfo.commitMessage ?? localGitInfo.commitMessage,
|
|
5801
|
+
repo: ciGitInfo.repo ?? localGitInfo.repo,
|
|
5802
|
+
author: ciGitInfo.author ?? localGitInfo.author,
|
|
5803
|
+
authorEmail: ciGitInfo.authorEmail ?? localGitInfo.authorEmail,
|
|
5804
|
+
tag: ciGitInfo.tag ?? localGitInfo.tag,
|
|
5805
|
+
dirty: localGitInfo.dirty
|
|
5806
|
+
};
|
|
5807
|
+
}
|
|
5868
5808
|
function hashKey(value) {
|
|
5869
5809
|
return createHash("sha256").update(value).digest("hex").slice(0, 12);
|
|
5870
5810
|
}
|
|
5871
5811
|
function getFileSize(filePath) {
|
|
5872
5812
|
try {
|
|
5873
|
-
return
|
|
5813
|
+
return fs.statSync(filePath).size;
|
|
5874
5814
|
} catch {
|
|
5875
5815
|
return 0;
|
|
5876
5816
|
}
|
|
@@ -5973,9 +5913,197 @@ function getCIInfo() {
|
|
|
5973
5913
|
}
|
|
5974
5914
|
return void 0;
|
|
5975
5915
|
}
|
|
5916
|
+
function getBaseEnvironmentInfo() {
|
|
5917
|
+
return {
|
|
5918
|
+
os: {
|
|
5919
|
+
platform: os.platform(),
|
|
5920
|
+
release: os.release(),
|
|
5921
|
+
arch: os.arch()
|
|
5922
|
+
},
|
|
5923
|
+
node: {
|
|
5924
|
+
version: process.version
|
|
5925
|
+
},
|
|
5926
|
+
machine: {
|
|
5927
|
+
cpus: os.cpus().length,
|
|
5928
|
+
memory: os.totalmem(),
|
|
5929
|
+
hostname: os.hostname()
|
|
5930
|
+
},
|
|
5931
|
+
ci: getCIInfo()
|
|
5932
|
+
};
|
|
5933
|
+
}
|
|
5934
|
+
function registerInterruptHandler(client, getRunId) {
|
|
5935
|
+
let completed = false;
|
|
5936
|
+
const handler = async (signal) => {
|
|
5937
|
+
if (completed) {
|
|
5938
|
+
return;
|
|
5939
|
+
}
|
|
5940
|
+
completed = true;
|
|
5941
|
+
const runId = getRunId();
|
|
5942
|
+
if (runId) {
|
|
5943
|
+
logWarn(`Received ${signal}, marking run ${runId} as interrupted`);
|
|
5944
|
+
try {
|
|
5945
|
+
await client.completeRun(runId, {
|
|
5946
|
+
status: "interrupted",
|
|
5947
|
+
endedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
5948
|
+
});
|
|
5949
|
+
} catch {
|
|
5950
|
+
}
|
|
5951
|
+
}
|
|
5952
|
+
process.removeListener(signal, sigintHandler);
|
|
5953
|
+
process.removeListener(signal, sigtermHandler);
|
|
5954
|
+
process.kill(process.pid, signal);
|
|
5955
|
+
};
|
|
5956
|
+
const sigintHandler = () => handler("SIGINT");
|
|
5957
|
+
const sigtermHandler = () => handler("SIGTERM");
|
|
5958
|
+
process.once("SIGINT", sigintHandler);
|
|
5959
|
+
process.once("SIGTERM", sigtermHandler);
|
|
5960
|
+
return () => {
|
|
5961
|
+
completed = true;
|
|
5962
|
+
process.removeListener("SIGINT", sigintHandler);
|
|
5963
|
+
process.removeListener("SIGTERM", sigtermHandler);
|
|
5964
|
+
};
|
|
5965
|
+
}
|
|
5966
|
+
var TAG_PATTERNS = {
|
|
5967
|
+
owner: /@owner:([^\s@]+)/,
|
|
5968
|
+
priority: /@priority:(critical|high|medium|low)/i,
|
|
5969
|
+
feature: /@feature:([^\s@]+)/,
|
|
5970
|
+
ticketId: /@ticket:([^\s@]+)/,
|
|
5971
|
+
testType: /@test_type:(smoke|e2e|regression|integration|unit)/i
|
|
5972
|
+
};
|
|
5973
|
+
var KNOWN_KEYS = ["owner", "priority", "feature", "ticket", "test_type", "id", "slow", "flaky"];
|
|
5974
|
+
function parseTestMetadata(tags) {
|
|
5975
|
+
const metadata = {
|
|
5976
|
+
isSlow: false,
|
|
5977
|
+
isFlakyTagged: false,
|
|
5978
|
+
customMetadata: {}
|
|
5979
|
+
};
|
|
5980
|
+
for (const tag of tags) {
|
|
5981
|
+
const lowerTag = tag.toLowerCase();
|
|
5982
|
+
if (lowerTag === "@slow") {
|
|
5983
|
+
metadata.isSlow = true;
|
|
5984
|
+
} else if (lowerTag === "@flaky") {
|
|
5985
|
+
metadata.isFlakyTagged = true;
|
|
5986
|
+
}
|
|
5987
|
+
}
|
|
5988
|
+
for (const tag of tags) {
|
|
5989
|
+
for (const [key, pattern] of Object.entries(TAG_PATTERNS)) {
|
|
5990
|
+
const match = tag.match(pattern);
|
|
5991
|
+
if (match) {
|
|
5992
|
+
const value = match[1];
|
|
5993
|
+
if (key === "priority") {
|
|
5994
|
+
metadata.priority = value.toLowerCase();
|
|
5995
|
+
} else if (key === "testType") {
|
|
5996
|
+
metadata.testType = value.toLowerCase();
|
|
5997
|
+
} else {
|
|
5998
|
+
metadata[key] = value;
|
|
5999
|
+
}
|
|
6000
|
+
}
|
|
6001
|
+
}
|
|
6002
|
+
const customMatch = tag.match(/@([a-zA-Z][a-zA-Z0-9_-]*):([^\s@]+)/);
|
|
6003
|
+
if (customMatch) {
|
|
6004
|
+
const [, key, value] = customMatch;
|
|
6005
|
+
if (!KNOWN_KEYS.includes(key.toLowerCase())) {
|
|
6006
|
+
metadata.customMetadata[key] = value;
|
|
6007
|
+
}
|
|
6008
|
+
}
|
|
6009
|
+
}
|
|
6010
|
+
return metadata;
|
|
6011
|
+
}
|
|
6012
|
+
var AttachmentUploader = class {
|
|
6013
|
+
options;
|
|
6014
|
+
constructor(options) {
|
|
6015
|
+
this.options = options;
|
|
6016
|
+
}
|
|
6017
|
+
async upload(signedUrl, filePath, contentType) {
|
|
6018
|
+
if (this.options.dryRun) {
|
|
6019
|
+
try {
|
|
6020
|
+
const stats = fs3.statSync(filePath);
|
|
6021
|
+
console.log(
|
|
6022
|
+
`[supatest][dry-run] Would upload ${filePath} (${stats.size} bytes) to S3`
|
|
6023
|
+
);
|
|
6024
|
+
} catch (error) {
|
|
6025
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
6026
|
+
console.warn(
|
|
6027
|
+
`[supatest][dry-run] Cannot access file ${filePath}: ${message}`
|
|
6028
|
+
);
|
|
6029
|
+
}
|
|
6030
|
+
return;
|
|
6031
|
+
}
|
|
6032
|
+
let fileBuffer;
|
|
6033
|
+
try {
|
|
6034
|
+
fileBuffer = await fs3.promises.readFile(filePath);
|
|
6035
|
+
} catch (error) {
|
|
6036
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
6037
|
+
throw new Error(`Failed to read file ${filePath}: ${message}`);
|
|
6038
|
+
}
|
|
6039
|
+
await withRetry(async () => {
|
|
6040
|
+
const controller = new _AbortController();
|
|
6041
|
+
const timeoutId = setTimeout(
|
|
6042
|
+
() => controller.abort(),
|
|
6043
|
+
this.options.timeoutMs
|
|
6044
|
+
);
|
|
6045
|
+
try {
|
|
6046
|
+
const response = await _fetch(signedUrl, {
|
|
6047
|
+
method: "PUT",
|
|
6048
|
+
headers: {
|
|
6049
|
+
"Content-Type": contentType,
|
|
6050
|
+
"Content-Length": String(fileBuffer.length)
|
|
6051
|
+
},
|
|
6052
|
+
body: fileBuffer,
|
|
6053
|
+
signal: controller.signal
|
|
6054
|
+
});
|
|
6055
|
+
if (!response.ok) {
|
|
6056
|
+
const error = new Error(
|
|
6057
|
+
`S3 upload failed: ${response.status} ${response.statusText}`
|
|
6058
|
+
);
|
|
6059
|
+
error.statusCode = response.status;
|
|
6060
|
+
throw error;
|
|
6061
|
+
}
|
|
6062
|
+
} finally {
|
|
6063
|
+
clearTimeout(timeoutId);
|
|
6064
|
+
}
|
|
6065
|
+
}, defaultRetryConfig);
|
|
6066
|
+
}
|
|
6067
|
+
async uploadBatch(items, signedUploads) {
|
|
6068
|
+
const pLimit = (await import("p-limit")).default;
|
|
6069
|
+
const limit = pLimit(this.options.maxConcurrent);
|
|
6070
|
+
const results = await Promise.allSettled(
|
|
6071
|
+
items.map(
|
|
6072
|
+
(item, index) => limit(async () => {
|
|
6073
|
+
var _a2, _b;
|
|
6074
|
+
try {
|
|
6075
|
+
await this.upload(item.signedUrl, item.filePath, item.contentType);
|
|
6076
|
+
return {
|
|
6077
|
+
success: true,
|
|
6078
|
+
attachmentId: (_a2 = signedUploads[index]) == null ? void 0 : _a2.attachmentId
|
|
6079
|
+
};
|
|
6080
|
+
} catch (error) {
|
|
6081
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
6082
|
+
return {
|
|
6083
|
+
success: false,
|
|
6084
|
+
attachmentId: (_b = signedUploads[index]) == null ? void 0 : _b.attachmentId,
|
|
6085
|
+
error: `${item.filePath}: ${message}`
|
|
6086
|
+
};
|
|
6087
|
+
}
|
|
6088
|
+
})
|
|
6089
|
+
)
|
|
6090
|
+
);
|
|
6091
|
+
return results.map((result, index) => {
|
|
6092
|
+
var _a2;
|
|
6093
|
+
if (result.status === "fulfilled") {
|
|
6094
|
+
return result.value;
|
|
6095
|
+
}
|
|
6096
|
+
const message = result.reason instanceof Error ? result.reason.message : String(result.reason);
|
|
6097
|
+
return {
|
|
6098
|
+
success: false,
|
|
6099
|
+
attachmentId: (_a2 = signedUploads[index]) == null ? void 0 : _a2.attachmentId,
|
|
6100
|
+
error: message
|
|
6101
|
+
};
|
|
6102
|
+
});
|
|
6103
|
+
}
|
|
6104
|
+
};
|
|
5976
6105
|
|
|
5977
6106
|
// src/index.ts
|
|
5978
|
-
var DEFAULT_API_URL = "https://code-api.supatest.ai";
|
|
5979
6107
|
var SupatestCypressReporter = class {
|
|
5980
6108
|
options;
|
|
5981
6109
|
client;
|
|
@@ -5993,15 +6121,16 @@ var SupatestCypressReporter = class {
|
|
|
5993
6121
|
cypressVersion;
|
|
5994
6122
|
browserName;
|
|
5995
6123
|
browserVersion;
|
|
6124
|
+
unregisterInterruptHandler;
|
|
5996
6125
|
constructor(options = {}) {
|
|
5997
6126
|
this.options = {
|
|
5998
6127
|
projectId: options.projectId || process.env.SUPATEST_PROJECT_ID || "",
|
|
5999
6128
|
apiKey: options.apiKey || process.env.SUPATEST_API_KEY || "",
|
|
6000
6129
|
apiUrl: options.apiUrl || process.env.SUPATEST_API_URL || DEFAULT_API_URL,
|
|
6001
6130
|
uploadAssets: options.uploadAssets ?? true,
|
|
6002
|
-
maxConcurrentUploads: options.maxConcurrentUploads ??
|
|
6003
|
-
retryAttempts: options.retryAttempts ??
|
|
6004
|
-
timeoutMs: options.timeoutMs ??
|
|
6131
|
+
maxConcurrentUploads: options.maxConcurrentUploads ?? DEFAULT_MAX_CONCURRENT_UPLOADS,
|
|
6132
|
+
retryAttempts: options.retryAttempts ?? DEFAULT_RETRY_ATTEMPTS,
|
|
6133
|
+
timeoutMs: options.timeoutMs ?? DEFAULT_TIMEOUT_MS,
|
|
6005
6134
|
dryRun: options.dryRun ?? process.env.SUPATEST_DRY_RUN === "true"
|
|
6006
6135
|
};
|
|
6007
6136
|
}
|
|
@@ -6020,6 +6149,7 @@ var SupatestCypressReporter = class {
|
|
|
6020
6149
|
this.client = new SupatestApiClient({
|
|
6021
6150
|
apiKey: this.options.apiKey,
|
|
6022
6151
|
apiUrl: this.options.apiUrl,
|
|
6152
|
+
projectId: this.options.projectId,
|
|
6023
6153
|
timeoutMs: this.options.timeoutMs,
|
|
6024
6154
|
retryAttempts: this.options.retryAttempts,
|
|
6025
6155
|
dryRun: this.options.dryRun
|
|
@@ -6054,6 +6184,10 @@ var SupatestCypressReporter = class {
|
|
|
6054
6184
|
};
|
|
6055
6185
|
const response = await this.client.createRun(runRequest);
|
|
6056
6186
|
this.runId = response.runId;
|
|
6187
|
+
this.unregisterInterruptHandler = registerInterruptHandler(
|
|
6188
|
+
this.client,
|
|
6189
|
+
() => this.runId
|
|
6190
|
+
);
|
|
6057
6191
|
logInfo(`Run ${this.runId} started (${details.specs.length} spec files)`);
|
|
6058
6192
|
} catch (error) {
|
|
6059
6193
|
this.errorCollector.recordError("RUN_CREATE", getErrorMessage2(error), { error });
|
|
@@ -6062,11 +6196,16 @@ var SupatestCypressReporter = class {
|
|
|
6062
6196
|
}
|
|
6063
6197
|
async onAfterSpec(spec, results) {
|
|
6064
6198
|
if (this.disabled || !this.runId) return;
|
|
6199
|
+
if (!(results == null ? void 0 : results.tests)) {
|
|
6200
|
+
logInfo(`Spec ${spec.relative} had no test results (browser may have crashed), skipping`);
|
|
6201
|
+
return;
|
|
6202
|
+
}
|
|
6065
6203
|
for (const test of results.tests) {
|
|
6066
6204
|
await this.processTestResult(spec, test, results);
|
|
6067
6205
|
}
|
|
6068
6206
|
}
|
|
6069
6207
|
async onAfterRun(results) {
|
|
6208
|
+
var _a2;
|
|
6070
6209
|
if (this.disabled || !this.runId) {
|
|
6071
6210
|
if (this.errorCollector.hasErrors()) {
|
|
6072
6211
|
console.log(this.errorCollector.formatSummary());
|
|
@@ -6075,22 +6214,23 @@ var SupatestCypressReporter = class {
|
|
|
6075
6214
|
}
|
|
6076
6215
|
await Promise.allSettled(this.uploadQueue);
|
|
6077
6216
|
const summary = {
|
|
6078
|
-
total: results.totalTests,
|
|
6079
|
-
passed: results.totalPassed,
|
|
6080
|
-
failed: results.totalFailed,
|
|
6217
|
+
total: (results == null ? void 0 : results.totalTests) ?? 0,
|
|
6218
|
+
passed: (results == null ? void 0 : results.totalPassed) ?? 0,
|
|
6219
|
+
failed: (results == null ? void 0 : results.totalFailed) ?? 0,
|
|
6081
6220
|
flaky: 0,
|
|
6082
|
-
skipped: results.totalSkipped + results.totalPending,
|
|
6221
|
+
skipped: ((results == null ? void 0 : results.totalSkipped) ?? 0) + ((results == null ? void 0 : results.totalPending) ?? 0),
|
|
6083
6222
|
timedOut: 0,
|
|
6084
6223
|
interrupted: 0,
|
|
6085
|
-
durationMs: results.totalDuration
|
|
6224
|
+
durationMs: (results == null ? void 0 : results.totalDuration) ?? 0
|
|
6086
6225
|
};
|
|
6226
|
+
(_a2 = this.unregisterInterruptHandler) == null ? void 0 : _a2.call(this);
|
|
6087
6227
|
try {
|
|
6088
6228
|
await this.client.completeRun(this.runId, {
|
|
6089
|
-
status: results.status === "finished" ? "complete" : "errored",
|
|
6090
|
-
endedAt: results.endedTestsAt,
|
|
6229
|
+
status: (results == null ? void 0 : results.status) === "finished" ? "complete" : "errored",
|
|
6230
|
+
endedAt: (results == null ? void 0 : results.endedTestsAt) ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
6091
6231
|
summary,
|
|
6092
6232
|
timing: {
|
|
6093
|
-
totalDurationMs: results.totalDuration,
|
|
6233
|
+
totalDurationMs: (results == null ? void 0 : results.totalDuration) ?? 0,
|
|
6094
6234
|
timeToFirstTest: this.firstTestStartTime ? this.firstTestStartTime - new Date(this.startedAt).getTime() : void 0,
|
|
6095
6235
|
timeToFirstFailure: this.firstFailureTime ? this.firstFailureTime - new Date(this.startedAt).getTime() : void 0
|
|
6096
6236
|
}
|
|
@@ -6106,16 +6246,17 @@ var SupatestCypressReporter = class {
|
|
|
6106
6246
|
async processTestResult(spec, test, specResults) {
|
|
6107
6247
|
const testId = this.getTestId(spec.relative, test.title);
|
|
6108
6248
|
const status = this.mapCypressStatus(test.state);
|
|
6109
|
-
|
|
6110
|
-
|
|
6249
|
+
const attempts = test.attempts ?? [];
|
|
6250
|
+
if (!this.firstTestStartTime && attempts[0]) {
|
|
6251
|
+
this.firstTestStartTime = new Date(attempts[0].wallClockStartedAt).getTime();
|
|
6111
6252
|
}
|
|
6112
6253
|
if (!this.firstFailureTime && status === "failed") {
|
|
6113
6254
|
this.firstFailureTime = Date.now();
|
|
6114
6255
|
}
|
|
6115
|
-
const lastAttempt =
|
|
6256
|
+
const lastAttempt = attempts[attempts.length - 1];
|
|
6116
6257
|
const resultEntry = {
|
|
6117
|
-
resultId: hashKey(`${testId}:${
|
|
6118
|
-
retry:
|
|
6258
|
+
resultId: hashKey(`${testId}:${attempts.length - 1}`),
|
|
6259
|
+
retry: attempts.length - 1,
|
|
6119
6260
|
status,
|
|
6120
6261
|
startTime: (lastAttempt == null ? void 0 : lastAttempt.wallClockStartedAt) ?? void 0,
|
|
6121
6262
|
durationMs: (lastAttempt == null ? void 0 : lastAttempt.wallClockDuration) || 0,
|
|
@@ -6140,8 +6281,8 @@ var SupatestCypressReporter = class {
|
|
|
6140
6281
|
timeout: 0,
|
|
6141
6282
|
retries: test.attempts.length - 1,
|
|
6142
6283
|
status,
|
|
6143
|
-
durationMs:
|
|
6144
|
-
retryCount:
|
|
6284
|
+
durationMs: attempts.reduce((sum, a) => sum + (a.wallClockDuration || 0), 0),
|
|
6285
|
+
retryCount: attempts.length - 1,
|
|
6145
6286
|
results: [resultEntry],
|
|
6146
6287
|
projectName: this.browserName || "electron",
|
|
6147
6288
|
metadata
|
|
@@ -6179,9 +6320,10 @@ var SupatestCypressReporter = class {
|
|
|
6179
6320
|
}
|
|
6180
6321
|
}
|
|
6181
6322
|
getOutcome(test) {
|
|
6323
|
+
var _a2, _b;
|
|
6182
6324
|
if (test.state === "skipped" || test.state === "pending") return "skipped";
|
|
6183
6325
|
if (test.state === "passed") {
|
|
6184
|
-
if (test.attempts.length > 1 && test.attempts.some((a) => a.state === "failed")) {
|
|
6326
|
+
if ((((_a2 = test.attempts) == null ? void 0 : _a2.length) ?? 0) > 1 && ((_b = test.attempts) == null ? void 0 : _b.some((a) => a.state === "failed"))) {
|
|
6185
6327
|
return "flaky";
|
|
6186
6328
|
}
|
|
6187
6329
|
return "expected";
|
|
@@ -6190,7 +6332,7 @@ var SupatestCypressReporter = class {
|
|
|
6190
6332
|
}
|
|
6191
6333
|
extractErrors(test) {
|
|
6192
6334
|
const errors = [];
|
|
6193
|
-
for (const attempt of test.attempts) {
|
|
6335
|
+
for (const attempt of test.attempts ?? []) {
|
|
6194
6336
|
if (attempt.error) {
|
|
6195
6337
|
errors.push({
|
|
6196
6338
|
message: attempt.error.message,
|
|
@@ -6213,29 +6355,11 @@ var SupatestCypressReporter = class {
|
|
|
6213
6355
|
return tags;
|
|
6214
6356
|
}
|
|
6215
6357
|
parseTestMetadata(tags) {
|
|
6216
|
-
|
|
6217
|
-
isSlow: false,
|
|
6218
|
-
isFlakyTagged: false,
|
|
6219
|
-
customMetadata: {}
|
|
6220
|
-
};
|
|
6221
|
-
for (const tag of tags) {
|
|
6222
|
-
const lower = tag.toLowerCase();
|
|
6223
|
-
if (lower === "@slow") metadata.isSlow = true;
|
|
6224
|
-
if (lower === "@flaky") metadata.isFlakyTagged = true;
|
|
6225
|
-
const ownerMatch = tag.match(/@owner:([^\s@]+)/i);
|
|
6226
|
-
if (ownerMatch) metadata.owner = ownerMatch[1];
|
|
6227
|
-
const priorityMatch = tag.match(/@priority:(critical|high|medium|low)/i);
|
|
6228
|
-
if (priorityMatch) metadata.priority = priorityMatch[1].toLowerCase();
|
|
6229
|
-
const featureMatch = tag.match(/@feature:([^\s@]+)/i);
|
|
6230
|
-
if (featureMatch) metadata.feature = featureMatch[1];
|
|
6231
|
-
const typeMatch = tag.match(/@test_type:(smoke|e2e|regression|integration|unit)/i);
|
|
6232
|
-
if (typeMatch) metadata.testType = typeMatch[1].toLowerCase();
|
|
6233
|
-
}
|
|
6234
|
-
return metadata;
|
|
6358
|
+
return parseTestMetadata(tags);
|
|
6235
6359
|
}
|
|
6236
6360
|
buildAttachmentMeta(test, specResults) {
|
|
6237
6361
|
const attachments = [];
|
|
6238
|
-
for (const screenshot of specResults.screenshots) {
|
|
6362
|
+
for (const screenshot of specResults.screenshots ?? []) {
|
|
6239
6363
|
if (screenshot.path) {
|
|
6240
6364
|
attachments.push({
|
|
6241
6365
|
name: screenshot.name || "screenshot",
|
|
@@ -6259,8 +6383,8 @@ var SupatestCypressReporter = class {
|
|
|
6259
6383
|
}
|
|
6260
6384
|
async uploadAttachments(testId, testResultId, specResults, test) {
|
|
6261
6385
|
const attachments = [];
|
|
6262
|
-
for (const screenshot of specResults.screenshots) {
|
|
6263
|
-
if (screenshot.path &&
|
|
6386
|
+
for (const screenshot of specResults.screenshots ?? []) {
|
|
6387
|
+
if (screenshot.path && fs2.existsSync(screenshot.path)) {
|
|
6264
6388
|
attachments.push({
|
|
6265
6389
|
path: screenshot.path,
|
|
6266
6390
|
meta: {
|
|
@@ -6273,7 +6397,7 @@ var SupatestCypressReporter = class {
|
|
|
6273
6397
|
});
|
|
6274
6398
|
}
|
|
6275
6399
|
}
|
|
6276
|
-
if (specResults.video &&
|
|
6400
|
+
if (specResults.video && fs2.existsSync(specResults.video)) {
|
|
6277
6401
|
attachments.push({
|
|
6278
6402
|
path: specResults.video,
|
|
6279
6403
|
meta: {
|
|
@@ -6296,55 +6420,65 @@ var SupatestCypressReporter = class {
|
|
|
6296
6420
|
filePath: attachments[i].path,
|
|
6297
6421
|
contentType: attachments[i].meta.contentType
|
|
6298
6422
|
}));
|
|
6299
|
-
await this.uploader.uploadBatch(uploadItems, uploads);
|
|
6423
|
+
const results = await this.uploader.uploadBatch(uploadItems, uploads);
|
|
6424
|
+
const failures = results.filter((r) => !r.success);
|
|
6425
|
+
if (failures.length > 0) {
|
|
6426
|
+
failures.forEach((failure) => {
|
|
6427
|
+
const attachment = attachments.find(
|
|
6428
|
+
(_, i) => {
|
|
6429
|
+
var _a2;
|
|
6430
|
+
return ((_a2 = uploads[i]) == null ? void 0 : _a2.attachmentId) === failure.attachmentId;
|
|
6431
|
+
}
|
|
6432
|
+
);
|
|
6433
|
+
this.errorCollector.recordError(
|
|
6434
|
+
"ATTACHMENT_UPLOAD",
|
|
6435
|
+
failure.error || "Upload failed",
|
|
6436
|
+
{
|
|
6437
|
+
attachmentName: attachment == null ? void 0 : attachment.meta.name,
|
|
6438
|
+
filePath: attachment == null ? void 0 : attachment.path,
|
|
6439
|
+
error: failure.error
|
|
6440
|
+
}
|
|
6441
|
+
);
|
|
6442
|
+
});
|
|
6443
|
+
}
|
|
6300
6444
|
} catch (error) {
|
|
6301
6445
|
this.errorCollector.recordError("ATTACHMENT_SIGN", getErrorMessage2(error), { error });
|
|
6302
6446
|
}
|
|
6303
6447
|
}
|
|
6304
6448
|
getEnvironmentInfo() {
|
|
6305
6449
|
return {
|
|
6306
|
-
|
|
6307
|
-
platform: os.platform(),
|
|
6308
|
-
release: os.release(),
|
|
6309
|
-
arch: os.arch()
|
|
6310
|
-
},
|
|
6311
|
-
node: {
|
|
6312
|
-
version: process.version
|
|
6313
|
-
},
|
|
6314
|
-
machine: {
|
|
6315
|
-
cpus: os.cpus().length,
|
|
6316
|
-
memory: os.totalmem(),
|
|
6317
|
-
hostname: os.hostname()
|
|
6318
|
-
},
|
|
6450
|
+
...getBaseEnvironmentInfo(),
|
|
6319
6451
|
cypress: {
|
|
6320
6452
|
version: this.cypressVersion || "unknown"
|
|
6321
|
-
}
|
|
6322
|
-
ci: getCIInfo()
|
|
6453
|
+
}
|
|
6323
6454
|
};
|
|
6324
6455
|
}
|
|
6325
6456
|
async getGitInfo() {
|
|
6326
|
-
|
|
6327
|
-
branch: process.env.GITHUB_REF_NAME ?? process.env.CI_COMMIT_BRANCH ?? process.env.GIT_BRANCH,
|
|
6328
|
-
commit: process.env.GITHUB_SHA ?? process.env.CI_COMMIT_SHA ?? process.env.GIT_COMMIT,
|
|
6329
|
-
repo: process.env.GITHUB_REPOSITORY ?? process.env.CI_PROJECT_PATH,
|
|
6330
|
-
author: process.env.GITHUB_ACTOR ?? process.env.GITLAB_USER_NAME
|
|
6331
|
-
};
|
|
6332
|
-
if (ciGitInfo.branch || ciGitInfo.commit) {
|
|
6333
|
-
return ciGitInfo;
|
|
6334
|
-
}
|
|
6335
|
-
return await getLocalGitInfo(this.rootDir);
|
|
6457
|
+
return getGitInfoWithCI(this.rootDir);
|
|
6336
6458
|
}
|
|
6337
6459
|
};
|
|
6338
6460
|
function supatestPlugin(on, config, options = {}) {
|
|
6339
6461
|
const reporter = new SupatestCypressReporter(options);
|
|
6340
6462
|
on("before:run", async (details) => {
|
|
6341
|
-
|
|
6463
|
+
try {
|
|
6464
|
+
await reporter.onBeforeRun(details);
|
|
6465
|
+
} catch (error) {
|
|
6466
|
+
logWarn(`Supatest reporter error in before:run: ${getErrorMessage2(error)}`);
|
|
6467
|
+
}
|
|
6342
6468
|
});
|
|
6343
6469
|
on("after:spec", async (spec, results) => {
|
|
6344
|
-
|
|
6470
|
+
try {
|
|
6471
|
+
await reporter.onAfterSpec(spec, results);
|
|
6472
|
+
} catch (error) {
|
|
6473
|
+
logWarn(`Supatest reporter error in after:spec: ${getErrorMessage2(error)}`);
|
|
6474
|
+
}
|
|
6345
6475
|
});
|
|
6346
6476
|
on("after:run", async (results) => {
|
|
6347
|
-
|
|
6477
|
+
try {
|
|
6478
|
+
await reporter.onAfterRun(results);
|
|
6479
|
+
} catch (error) {
|
|
6480
|
+
logWarn(`Supatest reporter error in after:run: ${getErrorMessage2(error)}`);
|
|
6481
|
+
}
|
|
6348
6482
|
});
|
|
6349
6483
|
}
|
|
6350
6484
|
export {
|