@empiricalrun/playwright-utils 0.26.21 → 0.27.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +23 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +36 -6
- package/dist/overlay-tests/cache.spec.js +1 -7
- package/dist/reporter/empirical-reporter.d.ts +12 -0
- package/dist/reporter/empirical-reporter.d.ts.map +1 -0
- package/dist/reporter/empirical-reporter.js +170 -0
- package/dist/reporter/types.d.ts +2 -16
- package/dist/reporter/types.d.ts.map +1 -1
- package/dist/reporter/uploader.d.ts +20 -0
- package/dist/reporter/uploader.d.ts.map +1 -1
- package/dist/reporter/uploader.js +40 -0
- package/dist/reporter/util.d.ts +31 -0
- package/dist/reporter/util.d.ts.map +1 -1
- package/dist/reporter/util.js +159 -0
- package/package.json +7 -4
- package/tsconfig.tsbuildinfo +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,28 @@
|
|
|
1
1
|
# @empiricalrun/playwright-utils
|
|
2
2
|
|
|
3
|
+
## 0.27.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 6390198: feat: add EmpiricalReporter for realtime upload of test artifacts & test results
|
|
8
|
+
|
|
9
|
+
### Patch Changes
|
|
10
|
+
|
|
11
|
+
- Updated dependencies [33abadf]
|
|
12
|
+
- Updated dependencies [1300a80]
|
|
13
|
+
- Updated dependencies [985d721]
|
|
14
|
+
- Updated dependencies [576870a]
|
|
15
|
+
- Updated dependencies [e94a2da]
|
|
16
|
+
- Updated dependencies [41c266d]
|
|
17
|
+
- Updated dependencies [dfb2007]
|
|
18
|
+
- Updated dependencies [6a19421]
|
|
19
|
+
- Updated dependencies [1a46013]
|
|
20
|
+
- Updated dependencies [805f35f]
|
|
21
|
+
- Updated dependencies [0a9ec78]
|
|
22
|
+
- Updated dependencies [c430cc0]
|
|
23
|
+
- @empiricalrun/test-gen@0.66.0
|
|
24
|
+
- @empiricalrun/llm@0.18.1
|
|
25
|
+
|
|
3
26
|
## 0.26.21
|
|
4
27
|
|
|
5
28
|
### Patch Changes
|
package/dist/config.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AAI7D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AAczD,wBAAgB,gBAAgB,IAAI,MAAM,CAazC;AA0CD,eAAO,MAAM,UAAU,EAAE,oBAyBxB,CAAC;AAEF,eAAO,MAAM,OAAO,EAAE,iBAgBrB,CAAC"}
|
package/dist/config.js
CHANGED
|
@@ -5,6 +5,8 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.devices = exports.baseConfig = void 0;
|
|
7
7
|
exports.chromeStablePath = chromeStablePath;
|
|
8
|
+
const fs_1 = __importDefault(require("fs"));
|
|
9
|
+
const path_1 = __importDefault(require("path"));
|
|
8
10
|
const deviceDescriptorsSource_json_1 = __importDefault(require("./devices/v1.47.1/deviceDescriptorsSource.json"));
|
|
9
11
|
function getPlatformOS() {
|
|
10
12
|
const platform = process.platform;
|
|
@@ -35,10 +37,43 @@ function chromeStablePath() {
|
|
|
35
37
|
}
|
|
36
38
|
}
|
|
37
39
|
}
|
|
40
|
+
function getPlaywrightVersion() {
|
|
41
|
+
try {
|
|
42
|
+
const packageJsonPath = path_1.default.join(process.cwd(), "node_modules", "@playwright", "test", "package.json");
|
|
43
|
+
const packageJson = JSON.parse(fs_1.default.readFileSync(packageJsonPath, "utf8"));
|
|
44
|
+
return packageJson.version;
|
|
45
|
+
}
|
|
46
|
+
catch (error) {
|
|
47
|
+
console.log("Package not found");
|
|
48
|
+
return "";
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
function isPlaywrightV147() {
|
|
52
|
+
const version = getPlaywrightVersion();
|
|
53
|
+
return version && version.includes("1.47") ? true : false;
|
|
54
|
+
}
|
|
55
|
+
function getReporters() {
|
|
56
|
+
if (isPlaywrightV147()) {
|
|
57
|
+
return [
|
|
58
|
+
["list"],
|
|
59
|
+
["json", { outputFile: "playwright-report/summary.json" }],
|
|
60
|
+
["./node_modules/@empiricalrun/playwright-utils/dist/reporter/custom.js"],
|
|
61
|
+
];
|
|
62
|
+
}
|
|
63
|
+
return [
|
|
64
|
+
["list"],
|
|
65
|
+
["json", { outputFile: "summary.json" }],
|
|
66
|
+
["html", { open: "never" }],
|
|
67
|
+
[
|
|
68
|
+
"./node_modules/@empiricalrun/playwright-utils/dist/reporter/empirical-reporter.js",
|
|
69
|
+
],
|
|
70
|
+
];
|
|
71
|
+
}
|
|
38
72
|
exports.baseConfig = {
|
|
39
73
|
testDir: "./tests",
|
|
40
74
|
// outputDir is where we store playwright artifacts (during and post test run)
|
|
41
|
-
outputDir: "./playwright-report/data",
|
|
75
|
+
outputDir: isPlaywrightV147() ? "./playwright-report/data" : "./test-results",
|
|
76
|
+
reporter: getReporters(),
|
|
42
77
|
fullyParallel: true,
|
|
43
78
|
forbidOnly: false,
|
|
44
79
|
retries: process.env.CI ? 2 : 0,
|
|
@@ -46,11 +81,6 @@ exports.baseConfig = {
|
|
|
46
81
|
workers: undefined,
|
|
47
82
|
// maxFailures counts retries as individual failures, so this is equivalent to 20 failed tests
|
|
48
83
|
maxFailures: process.env.CI ? 60 : undefined,
|
|
49
|
-
reporter: [
|
|
50
|
-
["list"], // For real-time reporting on CI terminal (vs. the default "dot" reporter)
|
|
51
|
-
["json", { outputFile: "playwright-report/summary.json" }],
|
|
52
|
-
["./node_modules/@empiricalrun/playwright-utils/dist/reporter/custom.js"],
|
|
53
|
-
],
|
|
54
84
|
use: {
|
|
55
85
|
trace: "on",
|
|
56
86
|
video: {
|
|
@@ -38,7 +38,6 @@ fixtures_1.test.afterEach(() => {
|
|
|
38
38
|
]);
|
|
39
39
|
await page.goto(`${server.baseURL}/glass-pane.html`);
|
|
40
40
|
let error;
|
|
41
|
-
let startTime = Date.now();
|
|
42
41
|
// Overwriting API key to ensure we don't call OpenAI
|
|
43
42
|
const originalApiKey = process.env.OPENAI_API_KEY;
|
|
44
43
|
process.env.OPENAI_API_KEY = undefined;
|
|
@@ -50,12 +49,7 @@ fixtures_1.test.afterEach(() => {
|
|
|
50
49
|
catch (e) {
|
|
51
50
|
error = e;
|
|
52
51
|
}
|
|
53
|
-
process.env.OPENAI_API_KEY = originalApiKey;
|
|
54
|
-
const duration = Date.now() - startTime;
|
|
55
52
|
(0, fixtures_1.expect)(error).toBeDefined();
|
|
53
|
+
process.env.OPENAI_API_KEY = originalApiKey;
|
|
56
54
|
(0, fixtures_1.expect)(error.message).toContain('<div class="glass-pane"></div> intercepts pointer events');
|
|
57
|
-
// Expect total time to be action > timeout * 2 (original + dismiss)
|
|
58
|
-
(0, fixtures_1.expect)(duration).toBeGreaterThan(10_000);
|
|
59
|
-
// + some buffer for failing API call
|
|
60
|
-
(0, fixtures_1.expect)(duration).toBeLessThan(14_999);
|
|
61
55
|
});
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { FullResult, Reporter, TestCase, TestResult } from "@playwright/test/reporter";
|
|
2
|
+
declare class EmpiricalReporter implements Reporter {
|
|
3
|
+
private _destinationDir;
|
|
4
|
+
private _uploadBucket;
|
|
5
|
+
private _currentWorkingDir;
|
|
6
|
+
constructor();
|
|
7
|
+
private processTestAttachment;
|
|
8
|
+
onTestEnd(test: TestCase, result: TestResult): void;
|
|
9
|
+
onEnd(result: FullResult): Promise<void>;
|
|
10
|
+
}
|
|
11
|
+
export default EmpiricalReporter;
|
|
12
|
+
//# sourceMappingURL=empirical-reporter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"empirical-reporter.d.ts","sourceRoot":"","sources":["../../src/reporter/empirical-reporter.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,UAAU,EACV,QAAQ,EACR,QAAQ,EACR,UAAU,EACX,MAAM,2BAA2B,CAAC;AAgBnC,cAAM,iBAAkB,YAAW,QAAQ;IACzC,OAAO,CAAC,eAAe,CAAS;IAChC,OAAO,CAAC,aAAa,CAAyB;IAC9C,OAAO,CAAC,kBAAkB,CAAyB;;IAcnD,OAAO,CAAC,qBAAqB,CA0B3B;IAEF,SAAS,CAAC,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,UAAU;IAyEtC,KAAK,CAAC,MAAM,EAAE,UAAU;CA4F/B;AAED,eAAe,iBAAiB,CAAC"}
|
|
@@ -0,0 +1,170 @@
|
|
|
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
|
+
const crypto_1 = require("crypto");
|
|
7
|
+
const fs_1 = __importDefault(require("fs"));
|
|
8
|
+
const path_1 = __importDefault(require("path"));
|
|
9
|
+
const logger_1 = require("../logger");
|
|
10
|
+
const queue_1 = require("./queue");
|
|
11
|
+
const uploader_1 = require("./uploader");
|
|
12
|
+
const util_1 = require("./util");
|
|
13
|
+
class EmpiricalReporter {
|
|
14
|
+
_destinationDir;
|
|
15
|
+
_uploadBucket = "test-report";
|
|
16
|
+
_currentWorkingDir = process.cwd();
|
|
17
|
+
constructor() {
|
|
18
|
+
if (!process.env.PROJECT_NAME || !process.env.TEST_RUN_GITHUB_ACTION_ID) {
|
|
19
|
+
logger_1.logger.error("PROJECT_NAME and TEST_RUN_GITHUB_ACTION_ID must be set");
|
|
20
|
+
this._destinationDir = "";
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
this._destinationDir = path_1.default.join(process.env.PROJECT_NAME, process.env.TEST_RUN_GITHUB_ACTION_ID);
|
|
24
|
+
}
|
|
25
|
+
processTestAttachment = async (attachment) => {
|
|
26
|
+
if (!attachment.path)
|
|
27
|
+
return;
|
|
28
|
+
const testResultSourceDir = path_1.default.join(this._currentWorkingDir, "test-results");
|
|
29
|
+
const exists = await (0, util_1.checkFileExistsAsync)(attachment.path);
|
|
30
|
+
if (exists) {
|
|
31
|
+
logger_1.logger.info(`[Empirical Reporter] Processing attachment: File exists`);
|
|
32
|
+
}
|
|
33
|
+
else {
|
|
34
|
+
logger_1.logger.error(`[Empirical Reporter] Attachment does not exist: ${attachment.path}`);
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
const uploadTask = (0, uploader_1.createUploadTaskV2)({
|
|
38
|
+
sourceDir: testResultSourceDir,
|
|
39
|
+
fileList: [attachment.path],
|
|
40
|
+
destinationDir: path_1.default.join(this._destinationDir, "data"),
|
|
41
|
+
uploadBucket: this._uploadBucket,
|
|
42
|
+
});
|
|
43
|
+
return (0, queue_1.sendTaskToQueue)(uploadTask);
|
|
44
|
+
};
|
|
45
|
+
onTestEnd(test, result) {
|
|
46
|
+
console.log(`[Empirical Reporter] Finished test ${test.title}: ${result.status}`);
|
|
47
|
+
if (!process.env.TEST_RUN_GITHUB_ACTION_ID) {
|
|
48
|
+
logger_1.logger.error("[Empirical Reporter] TEST_RUN_GITHUB_ACTION_ID is not set");
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
try {
|
|
52
|
+
const attachmentPromises = result.attachments.map((attachment) => {
|
|
53
|
+
return this.processTestAttachment(attachment);
|
|
54
|
+
});
|
|
55
|
+
const testCaseRunEventTask = async () => {
|
|
56
|
+
return Promise.all(attachmentPromises)
|
|
57
|
+
.then((uploadedAttachments) => {
|
|
58
|
+
logger_1.logger.debug(`[Empirical Reporter] Attachments for test ${test.title} are uploaded:`, uploadedAttachments);
|
|
59
|
+
const { suites, projectName } = (0, util_1.suitesAndProjectForTest)(test);
|
|
60
|
+
let params = {
|
|
61
|
+
test,
|
|
62
|
+
result,
|
|
63
|
+
assetsURL: {
|
|
64
|
+
trace: "",
|
|
65
|
+
videos: [],
|
|
66
|
+
errorContext: "",
|
|
67
|
+
},
|
|
68
|
+
suites,
|
|
69
|
+
projectName,
|
|
70
|
+
runId: process.env.TEST_RUN_GITHUB_ACTION_ID || "",
|
|
71
|
+
};
|
|
72
|
+
uploadedAttachments.forEach((attachment) => {
|
|
73
|
+
if (attachment) {
|
|
74
|
+
Object.entries(attachment).forEach(([key, value]) => {
|
|
75
|
+
if (key.includes("video")) {
|
|
76
|
+
params.assetsURL.videos.push({
|
|
77
|
+
name: `test-${test.id}-slug-${(0, crypto_1.randomUUID)()}`,
|
|
78
|
+
url: value,
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
if (key.includes("trace")) {
|
|
82
|
+
params.assetsURL.trace = value;
|
|
83
|
+
}
|
|
84
|
+
if (key.includes("error-context")) {
|
|
85
|
+
params.assetsURL.errorContext = value;
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
return (0, util_1.sendTestCaseUpdateToDashboard)(params);
|
|
91
|
+
})
|
|
92
|
+
.catch((error) => {
|
|
93
|
+
logger_1.logger.error(`[Empirical Reporter] Error sending test case event for: ${test.title}:`, error);
|
|
94
|
+
});
|
|
95
|
+
};
|
|
96
|
+
void (0, queue_1.sendTaskToQueue)(testCaseRunEventTask);
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
catch (error) {
|
|
100
|
+
logger_1.logger.error(`[Empirical Reporter] Error in onTestEnd for ${test.title}:`, error);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
async onEnd(result) {
|
|
104
|
+
logger_1.logger.info(`[Empirical Reporter] Test run completed with status: ${result.status}`);
|
|
105
|
+
const startTime = new Date().getTime();
|
|
106
|
+
logger_1.logger.info("[Empirical Reporter] Starting final report upload at: ", new Intl.DateTimeFormat("en-US", {
|
|
107
|
+
hour: "2-digit",
|
|
108
|
+
minute: "2-digit",
|
|
109
|
+
second: "2-digit",
|
|
110
|
+
}).format(startTime));
|
|
111
|
+
const jsonFilePath = path_1.default.join(this._currentWorkingDir, "summary.json");
|
|
112
|
+
const jsonExists = fs_1.default.existsSync(jsonFilePath);
|
|
113
|
+
if (jsonExists) {
|
|
114
|
+
logger_1.logger.info("[Empirical Reporter] Uploading JSON file");
|
|
115
|
+
const uploadJsonTask = (0, uploader_1.createUploadTaskV2)({
|
|
116
|
+
sourceDir: this._currentWorkingDir,
|
|
117
|
+
fileList: [jsonFilePath],
|
|
118
|
+
destinationDir: this._destinationDir,
|
|
119
|
+
uploadBucket: this._uploadBucket,
|
|
120
|
+
});
|
|
121
|
+
void (0, queue_1.sendTaskToQueue)(uploadJsonTask);
|
|
122
|
+
}
|
|
123
|
+
else {
|
|
124
|
+
logger_1.logger.error(`[Empirical Reporter] summary.json does not exist at: ${jsonFilePath}`);
|
|
125
|
+
}
|
|
126
|
+
const htmlFilePath = path_1.default.join(this._currentWorkingDir, "playwright-report/index.html");
|
|
127
|
+
const htmlExists = await (0, util_1.checkFileExistsAsync)(htmlFilePath);
|
|
128
|
+
if (htmlExists) {
|
|
129
|
+
logger_1.logger.info("[Empirical Reporter] Updating the HTML Zip");
|
|
130
|
+
await (0, util_1.updateHtmlZipFileAttachmentPaths)(jsonFilePath, htmlFilePath);
|
|
131
|
+
logger_1.logger.info("[Empirical Reporter] Uploading HTML file");
|
|
132
|
+
const uploadHtmlTask = (0, uploader_1.createUploadTaskV2)({
|
|
133
|
+
sourceDir: path_1.default.join(this._currentWorkingDir, "playwright-report"),
|
|
134
|
+
fileList: [htmlFilePath],
|
|
135
|
+
destinationDir: this._destinationDir,
|
|
136
|
+
uploadBucket: this._uploadBucket,
|
|
137
|
+
});
|
|
138
|
+
void (0, queue_1.sendTaskToQueue)(uploadHtmlTask);
|
|
139
|
+
}
|
|
140
|
+
else {
|
|
141
|
+
logger_1.logger.error(`[Empirical Reporter] playwright-report/index.html does not exist at: ${htmlFilePath}`);
|
|
142
|
+
}
|
|
143
|
+
const traceFilePath = path_1.default.join(this._currentWorkingDir, "playwright-report/trace");
|
|
144
|
+
const traceExists = await (0, util_1.checkFileExistsAsync)(traceFilePath);
|
|
145
|
+
if (traceExists) {
|
|
146
|
+
logger_1.logger.info("[Empirical Reporter] Uploading Trace folder");
|
|
147
|
+
const uploadTraceTask = (0, uploader_1.createUploadTaskV2)({
|
|
148
|
+
sourceDir: traceFilePath,
|
|
149
|
+
destinationDir: path_1.default.join(this._destinationDir, "trace"),
|
|
150
|
+
uploadBucket: this._uploadBucket,
|
|
151
|
+
});
|
|
152
|
+
void (0, queue_1.sendTaskToQueue)(uploadTraceTask);
|
|
153
|
+
}
|
|
154
|
+
else {
|
|
155
|
+
logger_1.logger.error(`[Empirical Reporter] playwright-report/trace does not exist at: ${traceFilePath}`);
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
await (0, queue_1.waitForTaskQueueToFinish)();
|
|
159
|
+
logger_1.logger.info("[Empirical Reporter] All uploads finished");
|
|
160
|
+
const endTime = new Date().getTime();
|
|
161
|
+
logger_1.logger.info("[Empirical Reporter] Finished final report upload at: ", new Intl.DateTimeFormat("en-US", {
|
|
162
|
+
hour: "2-digit",
|
|
163
|
+
minute: "2-digit",
|
|
164
|
+
second: "2-digit",
|
|
165
|
+
}).format(endTime));
|
|
166
|
+
const timeDiff = endTime - startTime;
|
|
167
|
+
logger_1.logger.info("[Empirical Reporter] Time taken to upload after tests ended: ", timeDiff, "ms");
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
exports.default = EmpiricalReporter;
|
package/dist/reporter/types.d.ts
CHANGED
|
@@ -1,17 +1,3 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export type
|
|
3
|
-
name: string;
|
|
4
|
-
url: string;
|
|
5
|
-
};
|
|
6
|
-
export type TestCaseRunEndEvent = {
|
|
7
|
-
test: TestCase;
|
|
8
|
-
result: TestResult;
|
|
9
|
-
assetsURL: {
|
|
10
|
-
trace: string;
|
|
11
|
-
videos: VideoURL[];
|
|
12
|
-
};
|
|
13
|
-
suites: string[];
|
|
14
|
-
projectName: string;
|
|
15
|
-
runId: string;
|
|
16
|
-
};
|
|
1
|
+
import type { TestCaseRunEndEvent as SharedTestCaseRunEndEvent } from "@empiricalrun/shared-types";
|
|
2
|
+
export type TestCaseRunEndEvent = SharedTestCaseRunEndEvent;
|
|
17
3
|
//# sourceMappingURL=types.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/reporter/types.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/reporter/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,IAAI,yBAAyB,EAAE,MAAM,4BAA4B,CAAC;AAEnG,MAAM,MAAM,mBAAmB,GAAG,yBAAyB,CAAC"}
|
|
@@ -10,4 +10,24 @@ export declare function createUploadTask({ fileList, outputFolder, relativePath,
|
|
|
10
10
|
fileList?: string[];
|
|
11
11
|
relativePath?: string;
|
|
12
12
|
}): AsyncTask;
|
|
13
|
+
/**
|
|
14
|
+
* Creates an async task for uploading files to R2 storage.
|
|
15
|
+
* This is an improved version of createUploadTask that:
|
|
16
|
+
* 1. Takes sourceDir directly instead of computing it from outputFolder + relativePath
|
|
17
|
+
* 2. Allows specifying a custom upload bucket instead of using a global constant
|
|
18
|
+
* 3. Preserves the full file path in the returned URLs instead of just the filename
|
|
19
|
+
*
|
|
20
|
+
* @param {Object} params - The upload parameters
|
|
21
|
+
* @param {string} params.sourceDir - The source directory containing files to upload
|
|
22
|
+
* @param {string[]} [params.fileList] - Optional list of specific files to upload
|
|
23
|
+
* @param {string} params.destinationDir - The destination directory in R2 storage
|
|
24
|
+
* @param {string} params.uploadBucket - The R2 bucket to upload to
|
|
25
|
+
* @returns {AsyncTask} An async task that when executed will upload the files and return a map of file paths to URLs
|
|
26
|
+
*/
|
|
27
|
+
export declare function createUploadTaskV2({ sourceDir, fileList, destinationDir, uploadBucket, }: {
|
|
28
|
+
sourceDir: string;
|
|
29
|
+
fileList?: string[];
|
|
30
|
+
destinationDir: string;
|
|
31
|
+
uploadBucket: string;
|
|
32
|
+
}): AsyncTask;
|
|
13
33
|
//# sourceMappingURL=uploader.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"uploader.d.ts","sourceRoot":"","sources":["../../src/reporter/uploader.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAmB,MAAM,2BAA2B,CAAC;AAIrE,OAAO,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAKpC,wBAAsB,WAAW,CAAC,EAChC,QAAQ,EACR,YAAY,EACZ,YAAY,GACb,EAAE;IACD,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;CACtB,GAAG,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,CAwB1B;AAED,wBAAgB,gBAAgB,CAAC,EAC/B,QAAQ,EACR,YAAY,EACZ,YAAiB,GAClB,EAAE;IACD,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB,GAAG,SAAS,CAaZ"}
|
|
1
|
+
{"version":3,"file":"uploader.d.ts","sourceRoot":"","sources":["../../src/reporter/uploader.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAmB,MAAM,2BAA2B,CAAC;AAIrE,OAAO,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAKpC,wBAAsB,WAAW,CAAC,EAChC,QAAQ,EACR,YAAY,EACZ,YAAY,GACb,EAAE;IACD,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;CACtB,GAAG,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,CAwB1B;AAED,wBAAgB,gBAAgB,CAAC,EAC/B,QAAQ,EACR,YAAY,EACZ,YAAiB,GAClB,EAAE;IACD,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB,GAAG,SAAS,CAaZ;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,kBAAkB,CAAC,EACjC,SAAS,EACT,QAAQ,EACR,cAAc,EACd,YAAY,GACb,EAAE;IACD,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,cAAc,EAAE,MAAM,CAAC;IACvB,YAAY,EAAE,MAAM,CAAC;CACtB,GAAG,SAAS,CA0BZ"}
|
|
@@ -5,6 +5,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.uploadFiles = uploadFiles;
|
|
7
7
|
exports.createUploadTask = createUploadTask;
|
|
8
|
+
exports.createUploadTaskV2 = createUploadTaskV2;
|
|
8
9
|
const r2_uploader_1 = require("@empiricalrun/r2-uploader");
|
|
9
10
|
const path_1 = __importDefault(require("path"));
|
|
10
11
|
const logger_1 = require("../logger");
|
|
@@ -45,3 +46,42 @@ function createUploadTask({ fileList, outputFolder, relativePath = "", }) {
|
|
|
45
46
|
.catch((e) => logger_1.logger.error("Error uploading files", e));
|
|
46
47
|
};
|
|
47
48
|
}
|
|
49
|
+
/**
|
|
50
|
+
* Creates an async task for uploading files to R2 storage.
|
|
51
|
+
* This is an improved version of createUploadTask that:
|
|
52
|
+
* 1. Takes sourceDir directly instead of computing it from outputFolder + relativePath
|
|
53
|
+
* 2. Allows specifying a custom upload bucket instead of using a global constant
|
|
54
|
+
* 3. Preserves the full file path in the returned URLs instead of just the filename
|
|
55
|
+
*
|
|
56
|
+
* @param {Object} params - The upload parameters
|
|
57
|
+
* @param {string} params.sourceDir - The source directory containing files to upload
|
|
58
|
+
* @param {string[]} [params.fileList] - Optional list of specific files to upload
|
|
59
|
+
* @param {string} params.destinationDir - The destination directory in R2 storage
|
|
60
|
+
* @param {string} params.uploadBucket - The R2 bucket to upload to
|
|
61
|
+
* @returns {AsyncTask} An async task that when executed will upload the files and return a map of file paths to URLs
|
|
62
|
+
*/
|
|
63
|
+
function createUploadTaskV2({ sourceDir, fileList, destinationDir, uploadBucket, }) {
|
|
64
|
+
return async () => {
|
|
65
|
+
try {
|
|
66
|
+
const uploadedFiles = await (0, r2_uploader_1.uploadDirectory)({
|
|
67
|
+
sourceDir,
|
|
68
|
+
fileList,
|
|
69
|
+
destinationDir,
|
|
70
|
+
uploadBucket,
|
|
71
|
+
});
|
|
72
|
+
const urls = {};
|
|
73
|
+
Object.entries(uploadedFiles).forEach(([filePath]) => {
|
|
74
|
+
// Example: If R2_BASE_URL is "https://r2.example.com" and destinationDir is "test-results/123" and filePath is "screenshot.png"
|
|
75
|
+
// The resulting URL will be: "https://r2.example.com/test-results/123/screenshot.png"
|
|
76
|
+
const fileUrl = new URL(path_1.default.join(destinationDir, filePath), R2_BASE_URL);
|
|
77
|
+
urls[filePath] = fileUrl.toString();
|
|
78
|
+
});
|
|
79
|
+
logger_1.logger.info("Finished Upload Files");
|
|
80
|
+
return urls;
|
|
81
|
+
}
|
|
82
|
+
catch (error) {
|
|
83
|
+
logger_1.logger.error("Error uploading files", error);
|
|
84
|
+
return {};
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
}
|
package/dist/reporter/util.d.ts
CHANGED
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
* See the License for the specific language governing permissions and
|
|
16
16
|
* limitations under the License.
|
|
17
17
|
*/
|
|
18
|
+
import { GenericPlaywrightAttachment } from "@empiricalrun/shared-types";
|
|
18
19
|
import { TestCase } from "@playwright/test/reporter";
|
|
19
20
|
import { TestCaseRunEndEvent } from "./types";
|
|
20
21
|
export declare function getPackageJsonPath(folderPath: string): string;
|
|
@@ -35,4 +36,34 @@ export declare function suitesAndProjectForTest(test: TestCase): {
|
|
|
35
36
|
};
|
|
36
37
|
export declare function sendTestCaseUpdateToDashboard(params: TestCaseRunEndEvent): Promise<void>;
|
|
37
38
|
export declare function safelySerialiseJSON(obj: any, keyFilter?: (key: string) => boolean): any;
|
|
39
|
+
export declare function updateHtmlZipFileAttachmentPaths(jsonFilePath: string, htmlFilePath: string): Promise<void>;
|
|
40
|
+
/**
|
|
41
|
+
* Replaces media paths in test results with normalized paths from the test attachment map.
|
|
42
|
+
* This function updates the attachments array in each test result with the corresponding
|
|
43
|
+
* normalized attachments from the test attachment map.
|
|
44
|
+
*
|
|
45
|
+
* @param jsonData - The JSON data containing test results, which can be either in the format
|
|
46
|
+
* of { files: [{ tests: [...] }] } or { tests: [...] }
|
|
47
|
+
* @param testAttachmentMap - A Map where the key is the test ID and the value is an array of
|
|
48
|
+
* objects containing attachments with updated path for each test result
|
|
49
|
+
* @returns void
|
|
50
|
+
*/
|
|
51
|
+
export declare function replaceMediaPaths(jsonData: any, testAttachmentMap: Map<string, {
|
|
52
|
+
attachments: GenericPlaywrightAttachment[];
|
|
53
|
+
}[]>): void;
|
|
54
|
+
/**
|
|
55
|
+
* Builds a map from test ID to an array of objects containing attachments for each result (retry).
|
|
56
|
+
* The attachments are normalized to use relative paths starting with 'data/'.
|
|
57
|
+
* @param summaryJsonFilePath Path to the summary JSON file
|
|
58
|
+
* @returns Map<string, { attachments: GenericPlaywrightAttachment[] }[]>
|
|
59
|
+
*/
|
|
60
|
+
export declare function buildTestAttachmentMapFromSummary(summaryJsonFilePath: string): Map<string, {
|
|
61
|
+
attachments: GenericPlaywrightAttachment[];
|
|
62
|
+
}[]>;
|
|
63
|
+
/**
|
|
64
|
+
* Checks asynchronously if a file exists at the given path.
|
|
65
|
+
* @param filePath - The path to the file to check
|
|
66
|
+
* @returns Promise<boolean> - true if the file exists, false otherwise
|
|
67
|
+
*/
|
|
68
|
+
export declare function checkFileExistsAsync(filePath: string): Promise<boolean>;
|
|
38
69
|
//# sourceMappingURL=util.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"util.d.ts","sourceRoot":"","sources":["../../src/reporter/util.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,
|
|
1
|
+
{"version":3,"file":"util.d.ts","sourceRoot":"","sources":["../../src/reporter/util.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,EAAE,2BAA2B,EAAE,MAAM,4BAA4B,CAAC;AACzE,OAAO,EAKL,QAAQ,EACT,MAAM,2BAA2B,CAAC;AAiBnC,OAAO,EAAE,mBAAmB,EAAE,MAAM,SAAS,CAAC;AAI9C,wBAAgB,kBAAkB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAmB7D;AAED,wBAAgB,yBAAyB,CACvC,YAAY,EAAE,MAAM,EACpB,SAAS,EAAE,MAAM,EACjB,WAAW,EAAE,MAAM,GAAG,SAAS,UAMhC;AAED,wBAAsB,0BAA0B,CAC9C,UAAU,EAAE,MAAM,EAClB,IAAI,EAAE,MAAM,EACZ,OAAO,GAAE;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAA;CAAO,GAC5E,OAAO,CAAC;IACT,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1B,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1B,WAAW,EAAE,MAAM,CAAC;CACrB,CAAC,CAyCD;AAED,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,QAAQ;;;EAwBrD;AAED,wBAAsB,6BAA6B,CACjD,MAAM,EAAE,mBAAmB,GAC1B,OAAO,CAAC,IAAI,CAAC,CAyDf;AAED,wBAAgB,mBAAmB,CACjC,GAAG,EAAE,GAAG,EACR,SAAS,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,GACnC,GAAG,CAIL;AAED,wBAAsB,gCAAgC,CACpD,YAAY,EAAE,MAAM,EACpB,YAAY,EAAE,MAAM,iBAoFrB;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,iBAAiB,CAC/B,QAAQ,EAAE,GAAG,EACb,iBAAiB,EAAE,GAAG,CACpB,MAAM,EACN;IAAE,WAAW,EAAE,2BAA2B,EAAE,CAAA;CAAE,EAAE,CACjD,QA+BF;AAED;;;;;GAKG;AACH,wBAAgB,iCAAiC,CAC/C,mBAAmB,EAAE,MAAM,GAC1B,GAAG,CAAC,MAAM,EAAE;IAAE,WAAW,EAAE,2BAA2B,EAAE,CAAA;CAAE,EAAE,CAAC,CAqD/D;AAED;;;;GAIG;AACH,wBAAsB,oBAAoB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAO7E"}
|
package/dist/reporter/util.js
CHANGED
|
@@ -26,6 +26,11 @@ exports.normalizeAndSaveAttachment = normalizeAndSaveAttachment;
|
|
|
26
26
|
exports.suitesAndProjectForTest = suitesAndProjectForTest;
|
|
27
27
|
exports.sendTestCaseUpdateToDashboard = sendTestCaseUpdateToDashboard;
|
|
28
28
|
exports.safelySerialiseJSON = safelySerialiseJSON;
|
|
29
|
+
exports.updateHtmlZipFileAttachmentPaths = updateHtmlZipFileAttachmentPaths;
|
|
30
|
+
exports.replaceMediaPaths = replaceMediaPaths;
|
|
31
|
+
exports.buildTestAttachmentMapFromSummary = buildTestAttachmentMapFromSummary;
|
|
32
|
+
exports.checkFileExistsAsync = checkFileExistsAsync;
|
|
33
|
+
const adm_zip_1 = __importDefault(require("adm-zip"));
|
|
29
34
|
const async_retry_1 = __importDefault(require("async-retry"));
|
|
30
35
|
const fs_1 = __importDefault(require("fs"));
|
|
31
36
|
const path_1 = __importDefault(require("path"));
|
|
@@ -166,3 +171,157 @@ async function sendTestCaseUpdateToDashboard(params) {
|
|
|
166
171
|
function safelySerialiseJSON(obj, keyFilter) {
|
|
167
172
|
return JSON.parse(JSON.stringify(obj, (key, value) => (keyFilter?.(key) ? undefined : value)));
|
|
168
173
|
}
|
|
174
|
+
async function updateHtmlZipFileAttachmentPaths(jsonFilePath, htmlFilePath) {
|
|
175
|
+
const testAttachmentMap = buildTestAttachmentMapFromSummary(jsonFilePath);
|
|
176
|
+
let htmlFile;
|
|
177
|
+
try {
|
|
178
|
+
htmlFile = await fs_1.default.promises.readFile(htmlFilePath, "utf8");
|
|
179
|
+
}
|
|
180
|
+
catch (err) {
|
|
181
|
+
console.error(`❌ Failed to read HTML file at ${htmlFilePath}:`, err);
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
const base64 = htmlFile.match(/window\.playwrightReportBase64\s*=\s*"(?:data:application\/zip;base64,)?([^"]+)"/)?.[1];
|
|
185
|
+
if (!base64) {
|
|
186
|
+
console.error("❌ Base64 zip data not found in HTML.");
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
const htmlDir = path_1.default.dirname(path_1.default.resolve(htmlFilePath));
|
|
190
|
+
const tempDir = fs_1.default.mkdtempSync(path_1.default.join(htmlDir, "unzipped-"));
|
|
191
|
+
const zipPath = path_1.default.join(tempDir, "archive.zip");
|
|
192
|
+
try {
|
|
193
|
+
await fs_1.default.promises.writeFile(zipPath, Buffer.from(base64, "base64"));
|
|
194
|
+
new adm_zip_1.default(zipPath).extractAllTo(tempDir, true);
|
|
195
|
+
await fs_1.default.promises.unlink(zipPath);
|
|
196
|
+
}
|
|
197
|
+
catch (err) {
|
|
198
|
+
console.error("❌ Failed to unzip base64 content:", err);
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
try {
|
|
202
|
+
const reportPath = path_1.default.join(tempDir, "report.json");
|
|
203
|
+
const reportData = JSON.parse(await fs_1.default.promises.readFile(reportPath, "utf8"));
|
|
204
|
+
replaceMediaPaths(reportData, testAttachmentMap);
|
|
205
|
+
await fs_1.default.promises.writeFile(reportPath, JSON.stringify(reportData, null, 2), "utf8");
|
|
206
|
+
const files = await fs_1.default.promises.readdir(tempDir);
|
|
207
|
+
for (const file of files.filter((f) => f.endsWith(".json") && f !== "report.json")) {
|
|
208
|
+
try {
|
|
209
|
+
const filePath = path_1.default.join(tempDir, file);
|
|
210
|
+
const jsonData = JSON.parse(await fs_1.default.promises.readFile(filePath, "utf8"));
|
|
211
|
+
replaceMediaPaths(jsonData, testAttachmentMap);
|
|
212
|
+
await fs_1.default.promises.writeFile(filePath, JSON.stringify(jsonData, null, 2), "utf8");
|
|
213
|
+
}
|
|
214
|
+
catch (err) {
|
|
215
|
+
console.error(`❌ Error processing test file ${file}:`, err);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
const newZip = new adm_zip_1.default();
|
|
219
|
+
newZip.addLocalFolder(tempDir);
|
|
220
|
+
const newBase64 = newZip.toBuffer().toString("base64");
|
|
221
|
+
const updatedHtml = htmlFile.replace(/(window\.playwrightReportBase64\s*=\s*")(?:data:application\/zip;base64,)?[^"]*(")/, `$1data:application/zip;base64,${newBase64}$2`);
|
|
222
|
+
await fs_1.default.promises.writeFile(htmlFilePath, updatedHtml, "utf8");
|
|
223
|
+
console.log("✅ HTML file updated with new base64 zip attachment.");
|
|
224
|
+
}
|
|
225
|
+
catch (err) {
|
|
226
|
+
console.error("❌ Error processing report.json or updating files:", err);
|
|
227
|
+
}
|
|
228
|
+
finally {
|
|
229
|
+
// Optional cleanup:
|
|
230
|
+
// await fs.promises.rm(tempDir, { recursive: true, force: true });
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* Replaces media paths in test results with normalized paths from the test attachment map.
|
|
235
|
+
* This function updates the attachments array in each test result with the corresponding
|
|
236
|
+
* normalized attachments from the test attachment map.
|
|
237
|
+
*
|
|
238
|
+
* @param jsonData - The JSON data containing test results, which can be either in the format
|
|
239
|
+
* of { files: [{ tests: [...] }] } or { tests: [...] }
|
|
240
|
+
* @param testAttachmentMap - A Map where the key is the test ID and the value is an array of
|
|
241
|
+
* objects containing attachments with updated path for each test result
|
|
242
|
+
* @returns void
|
|
243
|
+
*/
|
|
244
|
+
function replaceMediaPaths(jsonData, testAttachmentMap) {
|
|
245
|
+
const testsArray = jsonData.files?.flatMap((f) => f.tests) || jsonData.tests || [];
|
|
246
|
+
if (!testsArray.length) {
|
|
247
|
+
console.warn("⚠️ No test entries found in JSON.");
|
|
248
|
+
return;
|
|
249
|
+
}
|
|
250
|
+
for (const test of testsArray) {
|
|
251
|
+
const testId = test?.testId;
|
|
252
|
+
if (!testId) {
|
|
253
|
+
console.warn("⚠️ Test without testId encountered, skipping.");
|
|
254
|
+
continue;
|
|
255
|
+
}
|
|
256
|
+
const mapped = testAttachmentMap.get(testId);
|
|
257
|
+
if (!mapped) {
|
|
258
|
+
console.warn(`⚠️ No mapped attachments found for test: "${testId}"`);
|
|
259
|
+
continue;
|
|
260
|
+
}
|
|
261
|
+
// Update each result's attachments directly by index
|
|
262
|
+
for (let i = 0; i < (test.results || []).length; i++) {
|
|
263
|
+
const result = test.results[i];
|
|
264
|
+
const mappedResult = mapped[i];
|
|
265
|
+
if (!result || !mappedResult)
|
|
266
|
+
continue;
|
|
267
|
+
// Overwrite the attachments array directly
|
|
268
|
+
result.attachments = mappedResult.attachments;
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
/**
|
|
273
|
+
* Builds a map from test ID to an array of objects containing attachments for each result (retry).
|
|
274
|
+
* The attachments are normalized to use relative paths starting with 'data/'.
|
|
275
|
+
* @param summaryJsonFilePath Path to the summary JSON file
|
|
276
|
+
* @returns Map<string, { attachments: GenericPlaywrightAttachment[] }[]>
|
|
277
|
+
*/
|
|
278
|
+
function buildTestAttachmentMapFromSummary(summaryJsonFilePath) {
|
|
279
|
+
const summaryJson = JSON.parse(fs_1.default.readFileSync(summaryJsonFilePath, "utf8"));
|
|
280
|
+
const testAttachmentMap = new Map();
|
|
281
|
+
function normalizeAttachmentPath(attachment) {
|
|
282
|
+
if (!attachment?.path)
|
|
283
|
+
return attachment;
|
|
284
|
+
const relativePath = path_1.default.relative(path_1.default.join(process.cwd(), "test-results"), attachment.path);
|
|
285
|
+
attachment.path = path_1.default.join("data", relativePath);
|
|
286
|
+
return attachment;
|
|
287
|
+
}
|
|
288
|
+
function processSuite(suite) {
|
|
289
|
+
for (const spec of suite.specs || []) {
|
|
290
|
+
const testId = spec.id;
|
|
291
|
+
for (const test of spec.tests) {
|
|
292
|
+
if (!testId) {
|
|
293
|
+
console.warn("⚠️ Test without testId encountered, skipping.");
|
|
294
|
+
continue;
|
|
295
|
+
}
|
|
296
|
+
const resultAttachments = (test.results || []).map((result) => ({
|
|
297
|
+
attachments: (result.attachments || []).map(normalizeAttachmentPath),
|
|
298
|
+
}));
|
|
299
|
+
testAttachmentMap.set(testId, [
|
|
300
|
+
...(testAttachmentMap.get(testId) || []),
|
|
301
|
+
...resultAttachments,
|
|
302
|
+
]);
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
for (const childSuite of suite.suites || []) {
|
|
306
|
+
processSuite(childSuite);
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
for (const suite of summaryJson.suites || []) {
|
|
310
|
+
processSuite(suite);
|
|
311
|
+
}
|
|
312
|
+
return testAttachmentMap;
|
|
313
|
+
}
|
|
314
|
+
/**
|
|
315
|
+
* Checks asynchronously if a file exists at the given path.
|
|
316
|
+
* @param filePath - The path to the file to check
|
|
317
|
+
* @returns Promise<boolean> - true if the file exists, false otherwise
|
|
318
|
+
*/
|
|
319
|
+
async function checkFileExistsAsync(filePath) {
|
|
320
|
+
try {
|
|
321
|
+
await fs_1.default.promises.access(filePath);
|
|
322
|
+
return true;
|
|
323
|
+
}
|
|
324
|
+
catch {
|
|
325
|
+
return false;
|
|
326
|
+
}
|
|
327
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@empiricalrun/playwright-utils",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.27.0",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"registry": "https://registry.npmjs.org/",
|
|
6
6
|
"access": "public"
|
|
@@ -29,20 +29,23 @@
|
|
|
29
29
|
"@types/console-log-level": "^1.4.5",
|
|
30
30
|
"@types/node": "^20.14.9",
|
|
31
31
|
"@types/serve-handler": "^6.1.4",
|
|
32
|
+
"@types/adm-zip": "^0.5.7",
|
|
32
33
|
"playwright-core": "1.47.1",
|
|
33
|
-
"serve-handler": "^6.1.6"
|
|
34
|
+
"serve-handler": "^6.1.6",
|
|
35
|
+
"@empiricalrun/shared-types": "0.5.1"
|
|
34
36
|
},
|
|
35
37
|
"dependencies": {
|
|
36
38
|
"@babel/code-frame": "^7.24.7",
|
|
39
|
+
"adm-zip": "^0.5.16",
|
|
37
40
|
"async-retry": "^1.3.3",
|
|
38
41
|
"authenticator": "^1.1.5",
|
|
39
42
|
"console-log-level": "^1.4.1",
|
|
40
43
|
"mailosaur": "^8.6.1",
|
|
41
44
|
"puppeteer-extra-plugin-recaptcha": "^3.6.8",
|
|
42
45
|
"rimraf": "^6.0.1",
|
|
43
|
-
"@empiricalrun/llm": "^0.18.
|
|
46
|
+
"@empiricalrun/llm": "^0.18.1",
|
|
44
47
|
"@empiricalrun/r2-uploader": "^0.3.9",
|
|
45
|
-
"@empiricalrun/test-gen": "^0.
|
|
48
|
+
"@empiricalrun/test-gen": "^0.66.0"
|
|
46
49
|
},
|
|
47
50
|
"scripts": {
|
|
48
51
|
"dev": "tsc --build --watch",
|
package/tsconfig.tsbuildinfo
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"root":["./src/config.ts","./src/email.ts","./src/index.ts","./src/logger.ts","./src/playwright-extensions.ts","./src/auth/google.ts","./src/auth/index.ts","./src/auth/types.ts","./src/captcha/index.ts","./src/captcha/vision.ts","./src/devices/types.ts","./src/overlay-tests/cache.spec.ts","./src/overlay-tests/click.spec.ts","./src/overlay-tests/fixtures.ts","./src/overlay-tests/patch.spec.ts","./src/reporter/base.ts","./src/reporter/custom.ts","./src/reporter/queue.ts","./src/reporter/reporterV2.ts","./src/reporter/types.ts","./src/reporter/uploader.ts","./src/reporter/util.ts","./src/reporter/third_party/html-reporter-types.ts","./src/test/constants.ts","./src/test/expect.ts","./src/test/index.ts","./src/test/types.ts","./src/test/scripts/agent-capabilities.ts","./src/test/scripts/index.ts","./src/test/scripts/locator-highlights.ts","./src/test/scripts/locator-vision.ts","./src/test/scripts/mouse-pointer.ts","./src/test/scripts/types.ts","./src/test/scripts/pw-locator-patch/dismiss-overlays/cache.ts","./src/test/scripts/pw-locator-patch/dismiss-overlays/index.ts","./src/test/scripts/pw-locator-patch/dismiss-overlays/prompt.ts","./src/test/scripts/pw-locator-patch/dismiss-overlays/types.ts","./src/test/scripts/pw-locator-patch/dismiss-overlays/utils.ts","./src/test/scripts/pw-locator-patch/highlight/click.ts","./src/test/scripts/pw-locator-patch/highlight/expect.ts","./src/test/scripts/pw-locator-patch/highlight/hover.ts","./src/test/scripts/pw-locator-patch/highlight/inner-text.ts","./src/test/scripts/pw-locator-patch/highlight/input-value.ts","./src/test/scripts/pw-locator-patch/highlight/is-checked.ts","./src/test/scripts/pw-locator-patch/highlight/is-disabled.ts","./src/test/scripts/pw-locator-patch/highlight/is-editable.ts","./src/test/scripts/pw-locator-patch/highlight/text-content.ts","./src/test/scripts/pw-locator-patch/utils/index.ts","./src/test/scripts/pw-locator-patch/vision/query.ts"],"version":"5.8.3"}
|
|
1
|
+
{"root":["./src/config.ts","./src/email.ts","./src/index.ts","./src/logger.ts","./src/playwright-extensions.ts","./src/auth/google.ts","./src/auth/index.ts","./src/auth/types.ts","./src/captcha/index.ts","./src/captcha/vision.ts","./src/devices/types.ts","./src/overlay-tests/cache.spec.ts","./src/overlay-tests/click.spec.ts","./src/overlay-tests/fixtures.ts","./src/overlay-tests/patch.spec.ts","./src/reporter/base.ts","./src/reporter/custom.ts","./src/reporter/empirical-reporter.ts","./src/reporter/queue.ts","./src/reporter/reporterV2.ts","./src/reporter/types.ts","./src/reporter/uploader.ts","./src/reporter/util.ts","./src/reporter/third_party/html-reporter-types.ts","./src/test/constants.ts","./src/test/expect.ts","./src/test/index.ts","./src/test/types.ts","./src/test/scripts/agent-capabilities.ts","./src/test/scripts/index.ts","./src/test/scripts/locator-highlights.ts","./src/test/scripts/locator-vision.ts","./src/test/scripts/mouse-pointer.ts","./src/test/scripts/types.ts","./src/test/scripts/pw-locator-patch/dismiss-overlays/cache.ts","./src/test/scripts/pw-locator-patch/dismiss-overlays/index.ts","./src/test/scripts/pw-locator-patch/dismiss-overlays/prompt.ts","./src/test/scripts/pw-locator-patch/dismiss-overlays/types.ts","./src/test/scripts/pw-locator-patch/dismiss-overlays/utils.ts","./src/test/scripts/pw-locator-patch/highlight/click.ts","./src/test/scripts/pw-locator-patch/highlight/expect.ts","./src/test/scripts/pw-locator-patch/highlight/hover.ts","./src/test/scripts/pw-locator-patch/highlight/inner-text.ts","./src/test/scripts/pw-locator-patch/highlight/input-value.ts","./src/test/scripts/pw-locator-patch/highlight/is-checked.ts","./src/test/scripts/pw-locator-patch/highlight/is-disabled.ts","./src/test/scripts/pw-locator-patch/highlight/is-editable.ts","./src/test/scripts/pw-locator-patch/highlight/text-content.ts","./src/test/scripts/pw-locator-patch/utils/index.ts","./src/test/scripts/pw-locator-patch/vision/query.ts"],"version":"5.8.3"}
|