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