@empiricalrun/test-gen 0.79.2 → 0.79.3
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/CHANGELOG.md +8 -0
- package/dist/agent/chat/exports.d.ts +1 -0
- package/dist/agent/chat/exports.d.ts.map +1 -1
- package/dist/agent/chat/exports.js +3 -1
- package/dist/agent/chat/index.js +1 -1
- package/dist/agent/chat/prompt/repo.d.ts.map +1 -1
- package/dist/agent/chat/prompt/repo.js +4 -3
- package/dist/agent/fast-triage/index.d.ts +8 -0
- package/dist/agent/fast-triage/index.d.ts.map +1 -0
- package/dist/agent/fast-triage/index.js +51 -0
- package/dist/agent/index.d.ts +2 -1
- package/dist/agent/index.d.ts.map +1 -1
- package/dist/agent/index.js +4 -1
- package/dist/agent/triage/index.js +2 -2
- package/dist/bin/index.js +5 -3
- package/dist/bin/utils/platform/web/index.d.ts.map +1 -1
- package/dist/bin/utils/platform/web/index.js +3 -2
- package/dist/dashboard/client.d.ts +11 -1
- package/dist/dashboard/client.d.ts.map +1 -1
- package/dist/dashboard/client.js +22 -9
- package/dist/file-info/adapters/github/index.d.ts.map +1 -1
- package/dist/file-info/adapters/github/index.js +1 -1
- package/dist/generate-summary/frame-sampling.d.ts +12 -0
- package/dist/generate-summary/frame-sampling.d.ts.map +1 -0
- package/dist/generate-summary/frame-sampling.js +72 -0
- package/dist/generate-summary/generate-error-stack-summary.d.ts +11 -0
- package/dist/generate-summary/generate-error-stack-summary.d.ts.map +1 -0
- package/dist/generate-summary/generate-error-stack-summary.js +44 -0
- package/dist/generate-summary/generate-failed-step-screenshot-diff-summary.d.ts +58 -0
- package/dist/generate-summary/generate-failed-step-screenshot-diff-summary.d.ts.map +1 -0
- package/dist/generate-summary/generate-failed-step-screenshot-diff-summary.js +460 -0
- package/dist/generate-summary/generate-grouped-summary.d.ts +18 -0
- package/dist/generate-summary/generate-grouped-summary.d.ts.map +1 -0
- package/dist/generate-summary/generate-grouped-summary.js +91 -0
- package/dist/generate-summary/merge-summary.d.ts +16 -0
- package/dist/generate-summary/merge-summary.d.ts.map +1 -0
- package/dist/generate-summary/merge-summary.js +46 -0
- package/dist/generate-summary/pick-videos-for-comparison.d.ts +9 -0
- package/dist/generate-summary/pick-videos-for-comparison.d.ts.map +1 -0
- package/dist/generate-summary/pick-videos-for-comparison.js +54 -0
- package/dist/telemetry/index.d.ts.map +1 -1
- package/dist/telemetry/index.js +3 -1
- package/dist/tools/definitions/delete-file.js +1 -1
- package/dist/tools/definitions/grep.d.ts.map +1 -1
- package/dist/tools/definitions/grep.js +1 -1
- package/dist/tools/definitions/rename-file.js +2 -2
- package/dist/tools/definitions/safe-bash.d.ts.map +1 -1
- package/dist/tools/definitions/safe-bash.js +10 -8
- package/dist/tools/definitions/str_replace_editor.d.ts.map +1 -1
- package/dist/tools/definitions/str_replace_editor.js +12 -4
- package/dist/tools/definitions/trace-dot-zip.d.ts +7 -0
- package/dist/tools/definitions/trace-dot-zip.d.ts.map +1 -0
- package/dist/tools/definitions/trace-dot-zip.js +16 -0
- package/dist/tools/definitions/utils.js +1 -1
- package/dist/tools/delete-file/index.d.ts.map +1 -1
- package/dist/tools/delete-file/index.js +9 -6
- package/dist/tools/diagnosis-fetcher.d.ts.map +1 -1
- package/dist/tools/diagnosis-fetcher.js +40 -2
- package/dist/tools/fetch-file/index.d.ts.map +1 -1
- package/dist/tools/fetch-file/index.js +102 -3
- package/dist/tools/file-operations/index.d.ts.map +1 -1
- package/dist/tools/file-operations/index.js +7 -5
- package/dist/tools/file-operations/shared/helpers.d.ts +13 -0
- package/dist/tools/file-operations/shared/helpers.d.ts.map +1 -1
- package/dist/tools/file-operations/shared/helpers.js +24 -0
- package/dist/tools/file-operations/view/index.d.ts.map +1 -1
- package/dist/tools/file-operations/view/index.js +9 -3
- package/dist/tools/grep/index.d.ts.map +1 -1
- package/dist/tools/grep/index.js +7 -2
- package/dist/tools/index.d.ts +1 -1
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +6 -6
- package/dist/tools/merge-conflicts/index.d.ts.map +1 -1
- package/dist/tools/merge-conflicts/index.js +8 -8
- package/dist/tools/rename-file/index.d.ts.map +1 -1
- package/dist/tools/rename-file/index.js +11 -7
- package/dist/tools/run-test.d.ts.map +1 -1
- package/dist/tools/run-test.js +12 -7
- package/dist/tools/safe-bash/index.d.ts.map +1 -1
- package/dist/tools/safe-bash/index.js +18 -2
- package/dist/tools/test-gen-browser.d.ts.map +1 -1
- package/dist/tools/test-gen-browser.js +12 -9
- package/dist/tools/trace-dot-zip/index.d.ts +3 -1
- package/dist/tools/trace-dot-zip/index.d.ts.map +1 -1
- package/dist/tools/trace-dot-zip/index.js +8 -20
- package/dist/tools/trace-dot-zip/utils/console-trace.d.ts.map +1 -1
- package/dist/tools/trace-dot-zip/utils/console-trace.js +11 -5
- package/dist/tools/trace-dot-zip/utils/extract-screenshots.d.ts +27 -0
- package/dist/tools/trace-dot-zip/utils/extract-screenshots.d.ts.map +1 -0
- package/dist/tools/trace-dot-zip/utils/extract-screenshots.js +128 -0
- package/dist/tools/trace-dot-zip/utils/extract-steps.d.ts +12 -0
- package/dist/tools/trace-dot-zip/utils/extract-steps.d.ts.map +1 -0
- package/dist/tools/trace-dot-zip/utils/extract-steps.js +130 -0
- package/dist/tools/trace-dot-zip/utils/extract-zip.d.ts +13 -16
- package/dist/tools/trace-dot-zip/utils/extract-zip.d.ts.map +1 -1
- package/dist/tools/trace-dot-zip/utils/extract-zip.js +27 -167
- package/dist/tools/trace-dot-zip/utils/network-trace.d.ts.map +1 -1
- package/dist/tools/trace-dot-zip/utils/network-trace.js +136 -105
- package/dist/trace-utils/cli.d.ts +3 -0
- package/dist/trace-utils/cli.d.ts.map +1 -0
- package/dist/trace-utils/cli.js +302 -0
- package/dist/trace-utils/console.d.ts +11 -0
- package/dist/trace-utils/console.d.ts.map +1 -0
- package/dist/trace-utils/console.js +74 -0
- package/dist/trace-utils/dom-snapshot.d.ts +19 -0
- package/dist/trace-utils/dom-snapshot.d.ts.map +1 -0
- package/dist/trace-utils/dom-snapshot.js +328 -0
- package/dist/trace-utils/index.d.ts +8 -0
- package/dist/trace-utils/index.d.ts.map +1 -1
- package/dist/trace-utils/index.js +19 -1
- package/dist/trace-utils/network.d.ts +16 -0
- package/dist/trace-utils/network.d.ts.map +1 -0
- package/dist/trace-utils/network.js +178 -0
- package/dist/trace-utils/normalize-trace-url.d.ts +2 -0
- package/dist/trace-utils/normalize-trace-url.d.ts.map +1 -0
- package/dist/trace-utils/normalize-trace-url.js +15 -0
- package/dist/trace-utils/screenshots.d.ts +24 -0
- package/dist/trace-utils/screenshots.d.ts.map +1 -0
- package/dist/trace-utils/screenshots.js +197 -0
- package/dist/trace-utils/steps.d.ts +10 -0
- package/dist/trace-utils/steps.d.ts.map +1 -0
- package/dist/trace-utils/steps.js +126 -0
- package/dist/trace-utils/types.d.ts +51 -0
- package/dist/trace-utils/types.d.ts.map +1 -0
- package/dist/trace-utils/types.js +2 -0
- package/dist/utils/playwright-report-parser.d.ts +1 -12
- package/dist/utils/playwright-report-parser.d.ts.map +1 -1
- package/dist/utils/playwright-report-parser.js +8 -136
- package/dist/video-core/index.d.ts.map +1 -1
- package/dist/video-core/index.js +17 -33
- package/package.json +12 -6
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -0,0 +1,460 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.generateFailedStepScreenshotDiffSummary = exports.getScreenshotsInFailedStepWindow = exports.getFailedStep = exports.findSimilarActionFromList = void 0;
|
|
7
|
+
const llm_1 = require("@empiricalrun/llm");
|
|
8
|
+
const r2_uploader_1 = require("@empiricalrun/r2-uploader");
|
|
9
|
+
const trace_utils_1 = require("@empiricalrun/test-gen/trace-utils");
|
|
10
|
+
// import { waitUntil } from "@vercel/functions";
|
|
11
|
+
const buffer_1 = require("buffer");
|
|
12
|
+
// import fs from "fs";
|
|
13
|
+
const lodash_isequal_1 = __importDefault(require("lodash.isequal"));
|
|
14
|
+
// import prompt from "../../../../prompts/video-diff-summary-prompt.handlebars";
|
|
15
|
+
const trace_utils_2 = require("../trace-utils");
|
|
16
|
+
const frame_sampling_1 = require("./frame-sampling");
|
|
17
|
+
const responseFormat = {
|
|
18
|
+
type: "json_schema",
|
|
19
|
+
json_schema: {
|
|
20
|
+
name: "screenshot-difference-with-failure-reason",
|
|
21
|
+
strict: true,
|
|
22
|
+
schema: {
|
|
23
|
+
type: "object",
|
|
24
|
+
required: ["difference_in_screenshots", "reason_for_test_failure"],
|
|
25
|
+
properties: {
|
|
26
|
+
difference_in_screenshots: {
|
|
27
|
+
type: "string",
|
|
28
|
+
description: "what has changed between the successful run and failed run screenshots",
|
|
29
|
+
},
|
|
30
|
+
reason_for_test_failure: {
|
|
31
|
+
type: "string",
|
|
32
|
+
description: "reason for test failure",
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
additionalProperties: false,
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
};
|
|
39
|
+
const convertBufferToBase64 = (imageBuffer, mimeType) => {
|
|
40
|
+
const base64Image = imageBuffer.toString("base64");
|
|
41
|
+
const dataUrl = `data:${mimeType};base64,${base64Image}`;
|
|
42
|
+
return dataUrl;
|
|
43
|
+
};
|
|
44
|
+
const uploadScreenshotsToR2 = async ({ successScreenshots, failureScreenshots, testRunId, project, test, }) => {
|
|
45
|
+
try {
|
|
46
|
+
const file = {
|
|
47
|
+
buffer: buffer_1.Buffer.from(JSON.stringify({
|
|
48
|
+
success: successScreenshots.map((screenshot) => {
|
|
49
|
+
return screenshot.base64;
|
|
50
|
+
}),
|
|
51
|
+
failure: failureScreenshots.map((screenshot) => {
|
|
52
|
+
return screenshot.base64;
|
|
53
|
+
}),
|
|
54
|
+
}), "utf-8"),
|
|
55
|
+
fileName: `test-case-${test.testCaseId}-${test.slug}.json`,
|
|
56
|
+
mimeType: "application/json",
|
|
57
|
+
};
|
|
58
|
+
const folderName = process.env.NODE_ENV === "production"
|
|
59
|
+
? "visual-comparison-screenshots"
|
|
60
|
+
: "visual-comparison-screenshots-development";
|
|
61
|
+
const fileUrls = await (0, r2_uploader_1.uploadInMemoryFiles)({
|
|
62
|
+
files: [file],
|
|
63
|
+
destinationDir: `${project.repo_name.replace("-tests", "")}/${testRunId}/${folderName}`,
|
|
64
|
+
uploadBucket: "test-report",
|
|
65
|
+
accountId: process.env.R2_ACCOUNT_ID,
|
|
66
|
+
accessKeyId: process.env.R2_ACCESS_KEY_ID,
|
|
67
|
+
secretAccessKey: process.env.R2_SECRET_ACCESS_KEY,
|
|
68
|
+
});
|
|
69
|
+
return fileUrls;
|
|
70
|
+
}
|
|
71
|
+
catch (e) {
|
|
72
|
+
console.error(`[testRunId - ${testRunId}] uploadScreenshotsToR2 failed`, e);
|
|
73
|
+
return {};
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
const findSimilarActionFromList = ({ actionList, actionToFind, }) => {
|
|
77
|
+
const actionStack = actionToFind?.stack?.[0];
|
|
78
|
+
let matchingAction = actionList.find((currentAction) => {
|
|
79
|
+
const currentStack = currentAction?.stack?.[0];
|
|
80
|
+
return (currentStack?.file === actionStack?.file &&
|
|
81
|
+
currentStack?.line === actionStack?.line &&
|
|
82
|
+
currentStack?.column === actionStack?.column &&
|
|
83
|
+
currentAction?.apiName === actionToFind?.apiName &&
|
|
84
|
+
(0, lodash_isequal_1.default)(currentAction?.params, actionToFind?.params));
|
|
85
|
+
});
|
|
86
|
+
if (matchingAction) {
|
|
87
|
+
return matchingAction;
|
|
88
|
+
}
|
|
89
|
+
matchingAction = actionList.find((currentAction) => {
|
|
90
|
+
const currentStack = currentAction?.stack?.[0];
|
|
91
|
+
return (currentStack?.file === actionStack?.file &&
|
|
92
|
+
currentAction?.apiName === actionToFind?.apiName &&
|
|
93
|
+
(0, lodash_isequal_1.default)(currentAction?.params, actionToFind?.params));
|
|
94
|
+
});
|
|
95
|
+
if (matchingAction) {
|
|
96
|
+
return matchingAction;
|
|
97
|
+
}
|
|
98
|
+
matchingAction = actionList.find((currentAction) => {
|
|
99
|
+
const currentStack = currentAction?.stack?.[0];
|
|
100
|
+
return (currentStack?.file === actionStack?.file &&
|
|
101
|
+
currentStack?.line === actionStack?.line &&
|
|
102
|
+
currentStack?.column === actionStack?.column &&
|
|
103
|
+
currentAction?.apiName === actionToFind?.apiName);
|
|
104
|
+
});
|
|
105
|
+
return matchingAction;
|
|
106
|
+
};
|
|
107
|
+
exports.findSimilarActionFromList = findSimilarActionFromList;
|
|
108
|
+
const getFailedStep = async ({ zipUrl, sendParentForBeforeAction = true, }) => {
|
|
109
|
+
const allTestActions = [];
|
|
110
|
+
const testTraceFile = "test.trace";
|
|
111
|
+
await (0, trace_utils_2.extractFileFromZipFromUrl)({
|
|
112
|
+
zipUrl,
|
|
113
|
+
fileNames: [testTraceFile],
|
|
114
|
+
chunkProcessor: (_, fileName) => {
|
|
115
|
+
let testTraceTempBuffer = "";
|
|
116
|
+
return (chunk) => {
|
|
117
|
+
switch (fileName) {
|
|
118
|
+
case testTraceFile: {
|
|
119
|
+
testTraceTempBuffer += chunk;
|
|
120
|
+
let lines = testTraceTempBuffer.split("\n");
|
|
121
|
+
lines.slice(0, -1).forEach((line) => {
|
|
122
|
+
const parsedEvent = JSON.parse(line);
|
|
123
|
+
allTestActions.push(parsedEvent);
|
|
124
|
+
});
|
|
125
|
+
testTraceTempBuffer = lines[lines.length - 1];
|
|
126
|
+
break;
|
|
127
|
+
}
|
|
128
|
+
default:
|
|
129
|
+
break;
|
|
130
|
+
}
|
|
131
|
+
};
|
|
132
|
+
},
|
|
133
|
+
});
|
|
134
|
+
const totalActions = allTestActions.length;
|
|
135
|
+
let lastBeforeAction;
|
|
136
|
+
let lastBeforeActionParent;
|
|
137
|
+
for (let actionIndex = 0; actionIndex < totalActions; actionIndex++) {
|
|
138
|
+
const currentAction = allTestActions[actionIndex];
|
|
139
|
+
if (currentAction.type === "before") {
|
|
140
|
+
lastBeforeAction = currentAction;
|
|
141
|
+
}
|
|
142
|
+
if (currentAction.type === "error") {
|
|
143
|
+
break;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
// This handles cases where the failed step is inside a locator
|
|
147
|
+
// patch method of playwright-utils, and our intent is to find
|
|
148
|
+
// the parent action -- which is the caller in the actual test file
|
|
149
|
+
const isPlaywrightUtilsPatch = lastBeforeAction?.stack?.some((stackFrame) => stackFrame.file.includes("@empiricalrun/playwright-utils"));
|
|
150
|
+
if (isPlaywrightUtilsPatch && lastBeforeAction?.parentId) {
|
|
151
|
+
lastBeforeActionParent = allTestActions.find((action) => action.type === "before" &&
|
|
152
|
+
action.callId === lastBeforeAction?.parentId);
|
|
153
|
+
}
|
|
154
|
+
if (!lastBeforeAction) {
|
|
155
|
+
throw new Error("No before action found before error");
|
|
156
|
+
}
|
|
157
|
+
// Return parent action if requested and available, otherwise return the last before action
|
|
158
|
+
return sendParentForBeforeAction && lastBeforeActionParent
|
|
159
|
+
? lastBeforeActionParent
|
|
160
|
+
: lastBeforeAction;
|
|
161
|
+
};
|
|
162
|
+
exports.getFailedStep = getFailedStep;
|
|
163
|
+
const getScreenshotsInFailedStepWindow = async ({ failedStep, testRunId, zipUrl, isSuccessRun = false, }) => {
|
|
164
|
+
try {
|
|
165
|
+
const allTestActions = [];
|
|
166
|
+
const allScreenshots = [];
|
|
167
|
+
const stepIdToCallIdMap = {};
|
|
168
|
+
const detailedBeforeTraceActions = [];
|
|
169
|
+
const wallTimeToCallIdMap = {};
|
|
170
|
+
const testTraceFile = "test.trace";
|
|
171
|
+
const fileNames = await (0, trace_utils_2.getFilenamesInZip)(zipUrl, {
|
|
172
|
+
recursive: false,
|
|
173
|
+
});
|
|
174
|
+
const detailedTraceFileList = fileNames.filter((fileName) => fileName.includes("trace.trace"));
|
|
175
|
+
await (0, trace_utils_2.extractFileFromZipFromUrl)({
|
|
176
|
+
zipUrl,
|
|
177
|
+
fileNames: [testTraceFile, ...detailedTraceFileList],
|
|
178
|
+
chunkProcessor: (_, fileName) => {
|
|
179
|
+
let testTraceTempBuffer = "";
|
|
180
|
+
let detailedTraceTempBuffer = "";
|
|
181
|
+
return (chunk) => {
|
|
182
|
+
if (fileName === testTraceFile) {
|
|
183
|
+
testTraceTempBuffer += chunk;
|
|
184
|
+
let lines = testTraceTempBuffer.split("\n");
|
|
185
|
+
lines.slice(0, -1).forEach((line) => {
|
|
186
|
+
const parsedEvent = JSON.parse(line);
|
|
187
|
+
if (parsedEvent.type === "before" ||
|
|
188
|
+
parsedEvent.type === "after") {
|
|
189
|
+
allTestActions.push(parsedEvent);
|
|
190
|
+
}
|
|
191
|
+
});
|
|
192
|
+
testTraceTempBuffer = lines[lines.length - 1];
|
|
193
|
+
}
|
|
194
|
+
else if (fileName.includes("trace.trace")) {
|
|
195
|
+
detailedTraceTempBuffer += chunk;
|
|
196
|
+
let lines = detailedTraceTempBuffer.split("\n");
|
|
197
|
+
lines.slice(0, -1).forEach((line) => {
|
|
198
|
+
const parsedEvent = JSON.parse(line);
|
|
199
|
+
if (parsedEvent.type === "screencast-frame") {
|
|
200
|
+
allScreenshots.push({
|
|
201
|
+
...parsedEvent,
|
|
202
|
+
sha1: `resources/${parsedEvent.sha1}`,
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
else if (parsedEvent.type === "before") {
|
|
206
|
+
detailedBeforeTraceActions.push(parsedEvent);
|
|
207
|
+
if (parsedEvent.stepId) {
|
|
208
|
+
stepIdToCallIdMap[parsedEvent.stepId] =
|
|
209
|
+
parsedEvent;
|
|
210
|
+
}
|
|
211
|
+
if (parsedEvent.wallTime) {
|
|
212
|
+
wallTimeToCallIdMap[parsedEvent.wallTime] =
|
|
213
|
+
parsedEvent;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
});
|
|
217
|
+
detailedTraceTempBuffer = lines[lines.length - 1];
|
|
218
|
+
}
|
|
219
|
+
};
|
|
220
|
+
},
|
|
221
|
+
});
|
|
222
|
+
const beforeActions = allTestActions.filter((action) => {
|
|
223
|
+
const isBefore = action.type === "before";
|
|
224
|
+
return isBefore;
|
|
225
|
+
});
|
|
226
|
+
let currentFailedStep = (0, exports.findSimilarActionFromList)({
|
|
227
|
+
actionList: beforeActions,
|
|
228
|
+
actionToFind: failedStep,
|
|
229
|
+
});
|
|
230
|
+
// if the callId is a step then that mapping doesnt exist inside the detail trace.
|
|
231
|
+
// we need to find the right step inside it to map it
|
|
232
|
+
// currently assuming test.step can only be in case playwright utils patched apis
|
|
233
|
+
if (currentFailedStep?.callId.includes("test.step")) {
|
|
234
|
+
currentFailedStep =
|
|
235
|
+
beforeActions.find((a) => a.parentId === currentFailedStep.callId) ||
|
|
236
|
+
currentFailedStep;
|
|
237
|
+
}
|
|
238
|
+
// console.log("allscreenshots", allScreenshots.slice(0, 3));
|
|
239
|
+
const failedStepIndex = beforeActions.findIndex((a) => {
|
|
240
|
+
return a.callId === currentFailedStep.callId;
|
|
241
|
+
});
|
|
242
|
+
console.log("failedStepIndex", failedStepIndex);
|
|
243
|
+
console.log("beforeActions", beforeActions[failedStepIndex]);
|
|
244
|
+
let stepEndTime = allTestActions.find((action) => {
|
|
245
|
+
return (action.type === "after" && action.callId === currentFailedStep.callId);
|
|
246
|
+
}).endTime;
|
|
247
|
+
let lastActionCapturedInTestTrace = currentFailedStep;
|
|
248
|
+
let lastActionCapturedInDetailTrace = (stepIdToCallIdMap[currentFailedStep.callId] ||
|
|
249
|
+
wallTimeToCallIdMap[currentFailedStep.wallTime]);
|
|
250
|
+
if (!lastActionCapturedInDetailTrace) {
|
|
251
|
+
const actionsTakenTillLastFailedStepInReverseOrder = beforeActions
|
|
252
|
+
.slice(0, failedStepIndex)
|
|
253
|
+
.reverse();
|
|
254
|
+
const totalReversedActions = actionsTakenTillLastFailedStepInReverseOrder.length;
|
|
255
|
+
for (let actionIndex = 0; actionIndex < totalReversedActions; actionIndex++) {
|
|
256
|
+
const action = actionsTakenTillLastFailedStepInReverseOrder[actionIndex];
|
|
257
|
+
const correspondingDetailTraceAction = stepIdToCallIdMap[action.callId] ||
|
|
258
|
+
wallTimeToCallIdMap[action.wallTime];
|
|
259
|
+
if (correspondingDetailTraceAction) {
|
|
260
|
+
lastActionCapturedInTestTrace = action;
|
|
261
|
+
lastActionCapturedInDetailTrace = correspondingDetailTraceAction;
|
|
262
|
+
}
|
|
263
|
+
if (lastActionCapturedInDetailTrace) {
|
|
264
|
+
break;
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
const defaultWindowSize = 2000;
|
|
269
|
+
const pageIdForFailedStep = lastActionCapturedInDetailTrace.pageId;
|
|
270
|
+
const stepStartTime = isSuccessRun
|
|
271
|
+
? lastActionCapturedInTestTrace.startTime - defaultWindowSize / 2
|
|
272
|
+
: lastActionCapturedInTestTrace.startTime;
|
|
273
|
+
const endTime = isSuccessRun
|
|
274
|
+
? stepEndTime + defaultWindowSize
|
|
275
|
+
: stepEndTime;
|
|
276
|
+
// console.log("stepStartTime", stepStartTime);
|
|
277
|
+
// console.log("endTime", endTime);
|
|
278
|
+
const screenshotsForPage = allScreenshots.filter((screenshot) => {
|
|
279
|
+
return screenshot.pageId === pageIdForFailedStep;
|
|
280
|
+
});
|
|
281
|
+
allScreenshots.sort((a, b) => a.timestamp - b.timestamp);
|
|
282
|
+
const availableScreenshotTimestamps = screenshotsForPage.map((s) => s.timestamp);
|
|
283
|
+
let screenshotsToConsider = allScreenshots.filter((screenshot) => {
|
|
284
|
+
return (screenshot.pageId === pageIdForFailedStep &&
|
|
285
|
+
screenshot.timestamp <= endTime &&
|
|
286
|
+
screenshot.timestamp >= stepStartTime);
|
|
287
|
+
});
|
|
288
|
+
if (screenshotsToConsider.length === 0) {
|
|
289
|
+
const lastScreenshotTimestamp = availableScreenshotTimestamps[availableScreenshotTimestamps.length - 1];
|
|
290
|
+
// if the available screenshot timestamps are a lot less than the step start time
|
|
291
|
+
// take a window of last 2 seconds from the end of the screenshot timeline
|
|
292
|
+
if (lastScreenshotTimestamp < stepStartTime) {
|
|
293
|
+
screenshotsToConsider = allScreenshots.filter((screenshot) => {
|
|
294
|
+
return (screenshot.pageId === pageIdForFailedStep &&
|
|
295
|
+
screenshot.timestamp <= lastScreenshotTimestamp &&
|
|
296
|
+
screenshot.timestamp >= lastScreenshotTimestamp - defaultWindowSize);
|
|
297
|
+
});
|
|
298
|
+
}
|
|
299
|
+
else {
|
|
300
|
+
// if the failed step is in the middle of screenshot timestamp but no corresponding image is found
|
|
301
|
+
// take a window of 4 seconds before and after the failed step
|
|
302
|
+
screenshotsToConsider = allScreenshots.filter((screenshot) => {
|
|
303
|
+
return (screenshot.pageId === pageIdForFailedStep &&
|
|
304
|
+
screenshot.timestamp <= endTime + defaultWindowSize / 2 &&
|
|
305
|
+
screenshot.timestamp >= stepStartTime - defaultWindowSize / 2);
|
|
306
|
+
});
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
const imageBufferMap = {};
|
|
310
|
+
await (0, trace_utils_2.extractFileFromZipFromUrl)({
|
|
311
|
+
zipUrl,
|
|
312
|
+
fileNames: screenshotsToConsider.map((s) => s.sha1),
|
|
313
|
+
chunkProcessor: (_, fileName) => {
|
|
314
|
+
imageBufferMap[fileName] = imageBufferMap[fileName] || [];
|
|
315
|
+
return (chunk) => {
|
|
316
|
+
if (chunk) {
|
|
317
|
+
imageBufferMap[fileName].push(chunk);
|
|
318
|
+
}
|
|
319
|
+
};
|
|
320
|
+
},
|
|
321
|
+
});
|
|
322
|
+
const screenshotsWithBase64 = screenshotsToConsider.map((screenshot) => {
|
|
323
|
+
const imageBuffer = buffer_1.Buffer.concat(imageBufferMap[screenshot.sha1]);
|
|
324
|
+
const base64 = convertBufferToBase64(imageBuffer, "image/jpeg");
|
|
325
|
+
return {
|
|
326
|
+
metadata: { ...screenshot, base64 },
|
|
327
|
+
image: imageBuffer.toString("base64"),
|
|
328
|
+
};
|
|
329
|
+
});
|
|
330
|
+
// console.log("SCREENSHOT", screenshotsWithBase64.length);
|
|
331
|
+
const images = await (0, trace_utils_1.deduplicateImages)({
|
|
332
|
+
base64Images: screenshotsWithBase64,
|
|
333
|
+
threshold: 0.001,
|
|
334
|
+
logPrefix: `TestRun: ${testRunId}`,
|
|
335
|
+
});
|
|
336
|
+
// console.log("DEDUPED IMAGES", images.length);
|
|
337
|
+
return images.map((i) => i.metadata);
|
|
338
|
+
}
|
|
339
|
+
catch (e) {
|
|
340
|
+
console.warn(`Error extracting screenshots for testRunId - "${testRunId}", zipUrl - "${zipUrl}"`, e?.message);
|
|
341
|
+
return [];
|
|
342
|
+
}
|
|
343
|
+
};
|
|
344
|
+
exports.getScreenshotsInFailedStepWindow = getScreenshotsInFailedStepWindow;
|
|
345
|
+
const generateFailedStepScreenshotDiffSummary = async ({ testRunId, test, modelConfig, trace, project, }) => {
|
|
346
|
+
if (!test.success?.trace && !test.failure?.trace) {
|
|
347
|
+
console.warn(`[testRunId - ${testRunId}][test - ${test.title}] No success and failure trace found for test, exiting early ...`);
|
|
348
|
+
return "";
|
|
349
|
+
}
|
|
350
|
+
if (!test.failure?.trace) {
|
|
351
|
+
console.warn(`[testRunId - ${testRunId}][test - ${test.title}] No failure trace found for test, exiting early ...`);
|
|
352
|
+
return "";
|
|
353
|
+
}
|
|
354
|
+
try {
|
|
355
|
+
const failedStep = await (0, exports.getFailedStep)({
|
|
356
|
+
testRunId,
|
|
357
|
+
zipUrl: test.failure.trace,
|
|
358
|
+
});
|
|
359
|
+
let failureScreenshots = await (0, exports.getScreenshotsInFailedStepWindow)({
|
|
360
|
+
failedStep,
|
|
361
|
+
testRunId,
|
|
362
|
+
zipUrl: test.failure.trace,
|
|
363
|
+
});
|
|
364
|
+
const totalFailureScreenshots = failureScreenshots?.length ?? 0;
|
|
365
|
+
console.log(`[testRunId - ${testRunId}][test - ${test.title}] has - "${totalFailureScreenshots}" failure screenshots`);
|
|
366
|
+
trace?.event({
|
|
367
|
+
name: "collected-failure-actions",
|
|
368
|
+
output: {
|
|
369
|
+
success: true,
|
|
370
|
+
data: { count: totalFailureScreenshots },
|
|
371
|
+
},
|
|
372
|
+
});
|
|
373
|
+
if (!totalFailureScreenshots) {
|
|
374
|
+
return "";
|
|
375
|
+
}
|
|
376
|
+
let successScreenshots = test.success.trace
|
|
377
|
+
? await (0, exports.getScreenshotsInFailedStepWindow)({
|
|
378
|
+
failedStep,
|
|
379
|
+
testRunId,
|
|
380
|
+
zipUrl: test.success.trace,
|
|
381
|
+
isSuccessRun: true,
|
|
382
|
+
})
|
|
383
|
+
: [];
|
|
384
|
+
const totalSuccessScreenshots = successScreenshots?.length ?? 0;
|
|
385
|
+
console.log(`[testRunId - ${testRunId}][test - ${test.title}] has - "${totalSuccessScreenshots}" success screenshots`);
|
|
386
|
+
trace?.event({
|
|
387
|
+
name: "collected-success-actions",
|
|
388
|
+
output: {
|
|
389
|
+
success: true,
|
|
390
|
+
data: { count: totalSuccessScreenshots },
|
|
391
|
+
},
|
|
392
|
+
});
|
|
393
|
+
const totalScreenshots = totalSuccessScreenshots + totalFailureScreenshots;
|
|
394
|
+
const maxScreenshotsAllowed = 250;
|
|
395
|
+
if (totalScreenshots > maxScreenshotsAllowed) {
|
|
396
|
+
console.log(`[testRunId - ${testRunId}][testCaseId - ${test.testCaseId}] has - "${totalScreenshots}" screenshots, sampling to - "${maxScreenshotsAllowed}"`);
|
|
397
|
+
const sampledList = (0, frame_sampling_1.sampleCombinedList)(failureScreenshots, successScreenshots, maxScreenshotsAllowed);
|
|
398
|
+
failureScreenshots = sampledList.list1;
|
|
399
|
+
successScreenshots = sampledList.list2;
|
|
400
|
+
}
|
|
401
|
+
// waitUntil(
|
|
402
|
+
// uploadScreenshotsToR2({
|
|
403
|
+
// successScreenshots,
|
|
404
|
+
// failureScreenshots,
|
|
405
|
+
// testRunId,
|
|
406
|
+
// project,
|
|
407
|
+
// test,
|
|
408
|
+
// }),
|
|
409
|
+
// );
|
|
410
|
+
const modelProvider = modelConfig?.provider ?? "openai";
|
|
411
|
+
const model = modelConfig?.model ?? "gpt-4o";
|
|
412
|
+
const temperature = modelConfig?.temperature ?? 0.5;
|
|
413
|
+
const llm = new llm_1.LLM({
|
|
414
|
+
provider: modelProvider,
|
|
415
|
+
defaultModel: model,
|
|
416
|
+
});
|
|
417
|
+
const generationTrace = trace?.generation({
|
|
418
|
+
name: "generate-trace-screenshot-diff-summary",
|
|
419
|
+
model,
|
|
420
|
+
modelParameters: {
|
|
421
|
+
temperature,
|
|
422
|
+
},
|
|
423
|
+
});
|
|
424
|
+
const totalImageCount = successScreenshots.length + failureScreenshots.length;
|
|
425
|
+
// If there are too many images LLM failes to respond within alloted timeout
|
|
426
|
+
const imageDetail = totalImageCount > 40 ? "low" : "high";
|
|
427
|
+
const messages = (0, llm_1.compilePrompt)("prompt", {
|
|
428
|
+
successScreenshots: successScreenshots.map((s) => s.base64),
|
|
429
|
+
failureScreenshots: failureScreenshots.map((s) => s.base64),
|
|
430
|
+
errorStack: test.failure.stack,
|
|
431
|
+
}, {
|
|
432
|
+
imageDetail,
|
|
433
|
+
});
|
|
434
|
+
const llmResponse = await llm.createChatCompletion({
|
|
435
|
+
messages,
|
|
436
|
+
trace: generationTrace,
|
|
437
|
+
modelParameters: {
|
|
438
|
+
temperature,
|
|
439
|
+
},
|
|
440
|
+
responseFormat,
|
|
441
|
+
});
|
|
442
|
+
const parsedResponse = JSON.parse(llmResponse?.content);
|
|
443
|
+
generationTrace?.end({
|
|
444
|
+
output: {
|
|
445
|
+
videoDiffSummary: JSON.stringify(parsedResponse),
|
|
446
|
+
},
|
|
447
|
+
usage: {
|
|
448
|
+
input: llm.promptTokens,
|
|
449
|
+
output: llm.completionTokens,
|
|
450
|
+
unit: "TOKENS",
|
|
451
|
+
},
|
|
452
|
+
});
|
|
453
|
+
return parsedResponse?.reason_for_test_failure ?? "";
|
|
454
|
+
}
|
|
455
|
+
catch (e) {
|
|
456
|
+
console.error(`[testRunId - ${testRunId}][testCaseId - ${test.testCaseId}] Error generating trace screenshot diff summary`, e);
|
|
457
|
+
return "";
|
|
458
|
+
}
|
|
459
|
+
};
|
|
460
|
+
exports.generateFailedStepScreenshotDiffSummary = generateFailedStepScreenshotDiffSummary;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { TraceClient } from "@empiricalrun/llm";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
declare const groupedSummarySchema: z.ZodArray<z.ZodObject<{
|
|
4
|
+
testIds: z.ZodArray<z.ZodString>;
|
|
5
|
+
groupSummary: z.ZodString;
|
|
6
|
+
}, z.core.$strip>>;
|
|
7
|
+
export type GroupedSummarySchemaType = z.infer<typeof groupedSummarySchema>;
|
|
8
|
+
type ArgsT = {
|
|
9
|
+
testRunId: number;
|
|
10
|
+
tests: {
|
|
11
|
+
testId: string;
|
|
12
|
+
errorSummary: string;
|
|
13
|
+
}[];
|
|
14
|
+
trace?: TraceClient;
|
|
15
|
+
};
|
|
16
|
+
export declare const generateGroupedSummary: ({ testRunId, tests, trace, }: ArgsT) => Promise<GroupedSummarySchemaType | null>;
|
|
17
|
+
export {};
|
|
18
|
+
//# sourceMappingURL=generate-grouped-summary.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generate-grouped-summary.d.ts","sourceRoot":"","sources":["../../src/generate-summary/generate-grouped-summary.ts"],"names":[],"mappings":"AAAA,OAAO,EAAsB,WAAW,EAAE,MAAM,mBAAmB,CAAC;AACpE,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAIxB,QAAA,MAAM,oBAAoB;;;kBAKzB,CAAC;AACF,MAAM,MAAM,wBAAwB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAC;AA0C5E,KAAK,KAAK,GAAG;IACX,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IAClD,KAAK,CAAC,EAAE,WAAW,CAAC;CACrB,CAAC;AACF,eAAO,MAAM,sBAAsB,GAAU,8BAI1C,KAAK,KAAG,OAAO,CAAC,wBAAwB,GAAG,IAAI,CAgDjD,CAAC"}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.generateGroupedSummary = void 0;
|
|
7
|
+
const llm_1 = require("@empiricalrun/llm");
|
|
8
|
+
const zod_1 = require("zod");
|
|
9
|
+
const group_summary_handlebars_1 = __importDefault(require("@/prompts/group-summary.handlebars"));
|
|
10
|
+
const groupedSummarySchema = zod_1.z.array(zod_1.z.object({
|
|
11
|
+
testIds: zod_1.z.array(zod_1.z.string()),
|
|
12
|
+
groupSummary: zod_1.z.string(),
|
|
13
|
+
}));
|
|
14
|
+
const getResponseFormat = (availableTestIds) => ({
|
|
15
|
+
type: "json_schema",
|
|
16
|
+
json_schema: {
|
|
17
|
+
name: "test-case-failure-grouped-summary",
|
|
18
|
+
strict: true,
|
|
19
|
+
schema: {
|
|
20
|
+
type: "object",
|
|
21
|
+
properties: {
|
|
22
|
+
groups: {
|
|
23
|
+
type: "array",
|
|
24
|
+
description: "array containing tests grouped by similar error summaries",
|
|
25
|
+
items: {
|
|
26
|
+
type: "object",
|
|
27
|
+
properties: {
|
|
28
|
+
testIds: {
|
|
29
|
+
type: "array",
|
|
30
|
+
description: "array of test ids which are grouped together",
|
|
31
|
+
items: {
|
|
32
|
+
type: "string",
|
|
33
|
+
description: "Unique identifier for the tests, provided by the user",
|
|
34
|
+
enum: availableTestIds,
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
groupSummary: {
|
|
38
|
+
type: "string",
|
|
39
|
+
description: "Summary of the grouped tests",
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
required: ["testIds", "groupSummary"],
|
|
43
|
+
additionalProperties: false,
|
|
44
|
+
},
|
|
45
|
+
},
|
|
46
|
+
},
|
|
47
|
+
required: ["groups"],
|
|
48
|
+
additionalProperties: false,
|
|
49
|
+
},
|
|
50
|
+
},
|
|
51
|
+
});
|
|
52
|
+
const generateGroupedSummary = async ({ testRunId, tests, trace, }) => {
|
|
53
|
+
let output = null;
|
|
54
|
+
const availableTestIds = tests.map((t) => t.testId);
|
|
55
|
+
try {
|
|
56
|
+
const messages = (0, llm_1.compilePrompt)(promptTemplate_0, {
|
|
57
|
+
testList: JSON.stringify(tests),
|
|
58
|
+
});
|
|
59
|
+
trace?.event({
|
|
60
|
+
input: {
|
|
61
|
+
testList: JSON.stringify(tests),
|
|
62
|
+
},
|
|
63
|
+
output: { messages },
|
|
64
|
+
});
|
|
65
|
+
const llm = new llm_1.LLM({
|
|
66
|
+
trace,
|
|
67
|
+
provider: "openai",
|
|
68
|
+
defaultModel: "gpt-4o",
|
|
69
|
+
});
|
|
70
|
+
const llmResponse = await llm.createChatCompletion({
|
|
71
|
+
messages,
|
|
72
|
+
trace,
|
|
73
|
+
modelParameters: {
|
|
74
|
+
temperature: 0.2,
|
|
75
|
+
},
|
|
76
|
+
responseFormat: getResponseFormat(availableTestIds),
|
|
77
|
+
});
|
|
78
|
+
output = groupedSummarySchema.parse(JSON.parse(llmResponse?.content).groups);
|
|
79
|
+
}
|
|
80
|
+
catch (e) {
|
|
81
|
+
console.error(`Error grouping tests, returning all tests without groups for testRunId - "${testRunId}"`, e?.message);
|
|
82
|
+
output = tests.map((test) => {
|
|
83
|
+
return {
|
|
84
|
+
testIds: [test.testId],
|
|
85
|
+
groupSummary: test.errorSummary,
|
|
86
|
+
};
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
return output;
|
|
90
|
+
};
|
|
91
|
+
exports.generateGroupedSummary = generateGroupedSummary;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { TraceClient } from "@empiricalrun/llm";
|
|
2
|
+
import { TestGroup } from "@empiricalrun/shared-types/failure-workflow";
|
|
3
|
+
type ArgsT = {
|
|
4
|
+
testRunId: number;
|
|
5
|
+
test: TestGroup;
|
|
6
|
+
errorSummary: string;
|
|
7
|
+
videoDiffSummary: string;
|
|
8
|
+
networkFailures: {
|
|
9
|
+
endpoint: string;
|
|
10
|
+
status: number;
|
|
11
|
+
}[];
|
|
12
|
+
trace?: TraceClient;
|
|
13
|
+
};
|
|
14
|
+
export declare const mergeSummary: ({ testRunId, test, errorSummary: errorStackSummary, networkFailures, videoDiffSummary, trace, }: ArgsT) => Promise<string>;
|
|
15
|
+
export {};
|
|
16
|
+
//# sourceMappingURL=merge-summary.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"merge-summary.d.ts","sourceRoot":"","sources":["../../src/generate-summary/merge-summary.ts"],"names":[],"mappings":"AAAA,OAAO,EAAsB,WAAW,EAAE,MAAM,mBAAmB,CAAC;AACpE,OAAO,EAAE,SAAS,EAAE,MAAM,6CAA6C,CAAC;AAGxE,KAAK,KAAK,GAAG;IACX,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,SAAS,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;IACrB,gBAAgB,EAAE,MAAM,CAAC;IACzB,eAAe,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IACxD,KAAK,CAAC,EAAE,WAAW,CAAC;CACrB,CAAC;AACF,eAAO,MAAM,YAAY,GAAU,iGAOhC,KAAK,KAAG,OAAO,CAAC,MAAM,CA2CxB,CAAC"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.mergeSummary = void 0;
|
|
7
|
+
const llm_1 = require("@empiricalrun/llm");
|
|
8
|
+
const merge_summary_handlebars_1 = __importDefault(require("@/prompts/merge-summary.handlebars"));
|
|
9
|
+
const mergeSummary = async ({ testRunId, test, errorSummary: errorStackSummary, networkFailures, videoDiffSummary, trace, }) => {
|
|
10
|
+
let output = "";
|
|
11
|
+
const payload = {
|
|
12
|
+
test: JSON.stringify({
|
|
13
|
+
errorSummary: videoDiffSummary || errorStackSummary,
|
|
14
|
+
networkFailures,
|
|
15
|
+
}),
|
|
16
|
+
};
|
|
17
|
+
try {
|
|
18
|
+
const messages = (0, llm_1.compilePrompt)(promptTemplate_0, payload);
|
|
19
|
+
trace?.event({
|
|
20
|
+
input: {
|
|
21
|
+
payload,
|
|
22
|
+
},
|
|
23
|
+
output: { messages },
|
|
24
|
+
});
|
|
25
|
+
const llm = new llm_1.LLM({
|
|
26
|
+
trace,
|
|
27
|
+
// defaultModel: "gpt-4o-2024-08-06",
|
|
28
|
+
providerApiKey: process.env.ANTHROPIC_API_KEY,
|
|
29
|
+
provider: "anthropic",
|
|
30
|
+
defaultModel: "claude-3-5-sonnet-latest",
|
|
31
|
+
});
|
|
32
|
+
const llmResponse = await llm.createChatCompletion({
|
|
33
|
+
messages: messages,
|
|
34
|
+
trace,
|
|
35
|
+
modelParameters: {
|
|
36
|
+
temperature: 0.2,
|
|
37
|
+
},
|
|
38
|
+
});
|
|
39
|
+
output = llmResponse?.content ?? "";
|
|
40
|
+
}
|
|
41
|
+
catch (e) {
|
|
42
|
+
throw new Error(`Failed to merge network+video+error stack summary for test - "${test.title}", testRunId - "${testRunId}"`, e.message);
|
|
43
|
+
}
|
|
44
|
+
return output;
|
|
45
|
+
};
|
|
46
|
+
exports.mergeSummary = mergeSummary;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { TestGroup } from "@empiricalrun/shared-types/failure-workflow";
|
|
2
|
+
export declare const pickVideosForComparison: ({ testRunId, test, }: {
|
|
3
|
+
testRunId: number;
|
|
4
|
+
test: TestGroup;
|
|
5
|
+
}) => Promise<{
|
|
6
|
+
failure: string;
|
|
7
|
+
success: string;
|
|
8
|
+
}>;
|
|
9
|
+
//# sourceMappingURL=pick-videos-for-comparison.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pick-videos-for-comparison.d.ts","sourceRoot":"","sources":["../../src/generate-summary/pick-videos-for-comparison.ts"],"names":[],"mappings":"AAqBA,OAAO,EAAE,SAAS,EAAE,MAAM,6CAA6C,CAAC;AAExE,eAAO,MAAM,uBAAuB,GAAU,sBAG3C;IACD,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,SAAS,CAAC;CACjB,KAAG,OAAO,CAAC;IACV,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;CACjB,CAiEA,CAAC"}
|