@empiricalrun/playwright-utils 0.39.1 → 0.39.2
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 +7 -0
- package/dist/config/index.d.ts.map +1 -1
- package/dist/config/index.js +5 -2
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/mailosaur-client.d.ts +5 -0
- package/dist/mailosaur-client.d.ts.map +1 -1
- package/dist/reporter/empirical-reporter.d.ts.map +1 -1
- package/dist/reporter/empirical-reporter.js +19 -0
- package/dist/telemetry.d.ts +10 -1
- package/dist/telemetry.d.ts.map +1 -1
- package/dist/telemetry.js +19 -2
- package/dist/test/expect/index.d.ts +4 -0
- package/dist/test/expect/index.d.ts.map +1 -0
- package/dist/test/expect/index.js +7 -0
- package/dist/test/expect/types.d.ts +5 -0
- package/dist/test/expect/types.d.ts.map +1 -0
- package/dist/test/expect/types.js +2 -0
- package/dist/test/expect/visual.d.ts +4 -0
- package/dist/test/expect/visual.d.ts.map +1 -0
- package/dist/test/{expect.js → expect/visual.js} +1 -1
- package/dist/test/expect/webhook.d.ts +7 -0
- package/dist/test/expect/webhook.d.ts.map +1 -0
- package/dist/test/expect/webhook.js +41 -0
- package/dist/test/index.d.ts +2 -0
- package/dist/test/index.d.ts.map +1 -1
- package/dist/test/index.js +1 -1
- package/dist/test/scripts/pw-locator-patch/dismiss-overlays/index.d.ts.map +1 -1
- package/dist/test/scripts/pw-locator-patch/dismiss-overlays/index.js +7 -3
- package/dist/webhook.d.ts +20 -0
- package/dist/webhook.d.ts.map +1 -0
- package/dist/webhook.js +116 -0
- package/package.json +3 -3
- package/tsconfig.tsbuildinfo +1 -1
- package/dist/test/expect.d.ts +0 -7
- package/dist/test/expect.d.ts.map +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/config/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AAE7D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AAgBzD,wBAAgB,gBAAgB,IAAI,MAAM,CAazC;AA6BD,eAAO,MAAM,UAAU,EAAE,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/config/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AAE7D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AAgBzD,wBAAgB,gBAAgB,IAAI,MAAM,CAazC;AA6BD,eAAO,MAAM,UAAU,EAAE,oBA4BxB,CAAC;AAEF,eAAO,MAAM,OAAO,EAAE,iBAarB,CAAC"}
|
package/dist/config/index.js
CHANGED
|
@@ -71,8 +71,11 @@ exports.baseConfig = {
|
|
|
71
71
|
retries: process.env.CI ? 2 : 0,
|
|
72
72
|
// Let playwright choose max workers based on size of infra
|
|
73
73
|
workers: undefined,
|
|
74
|
-
// maxFailures counts retries as individual failures, so this is equivalent to 20 failed tests
|
|
75
|
-
|
|
74
|
+
// maxFailures counts retries as individual failures, so this is equivalent to ~20 failed tests
|
|
75
|
+
// When sharding, distribute the limit across shards (rounded up)
|
|
76
|
+
maxFailures: process.env.CI
|
|
77
|
+
? Math.ceil(30 / (parseInt(process.env.TOTAL_SHARDS, 10) || 1))
|
|
78
|
+
: undefined,
|
|
76
79
|
timeout: 15 * 60 * 1_000,
|
|
77
80
|
expect: {
|
|
78
81
|
timeout: 15_000,
|
package/dist/index.d.ts
CHANGED
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,QAAQ,CAAC;AACvB,cAAc,eAAe,CAAC;AAC9B,OAAO,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAC3C,OAAO,EAAE,UAAU,EAAE,gBAAgB,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AACjE,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAChD,cAAc,SAAS,CAAC;AACxB,cAAc,MAAM,CAAC;AACrB,cAAc,yBAAyB,CAAC;AACxC,cAAc,YAAY,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,QAAQ,CAAC;AACvB,cAAc,eAAe,CAAC;AAC9B,OAAO,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAC3C,OAAO,EAAE,UAAU,EAAE,gBAAgB,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AACjE,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAChD,cAAc,SAAS,CAAC;AACxB,cAAc,MAAM,CAAC;AACrB,cAAc,yBAAyB,CAAC;AACxC,cAAc,YAAY,CAAC;AAC3B,cAAc,WAAW,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -14,6 +14,11 @@ export interface IDashboardAPIClient {
|
|
|
14
14
|
path: string;
|
|
15
15
|
body?: unknown;
|
|
16
16
|
}): Promise<T>;
|
|
17
|
+
callWebhookSiteProxy<T>(opts: {
|
|
18
|
+
method: "GET" | "POST" | "PATCH" | "PUT" | "DELETE";
|
|
19
|
+
path: string;
|
|
20
|
+
body?: unknown;
|
|
21
|
+
}): Promise<T>;
|
|
17
22
|
getBaseUrl(): string;
|
|
18
23
|
}
|
|
19
24
|
type Link = {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mailosaur-client.d.ts","sourceRoot":"","sources":["../src/mailosaur-client.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,mBAAmB;IAClC,OAAO,CAAC,CAAC,EACP,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE;QACP,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QAChC,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QAC/B,MAAM,EAAE,KAAK,GAAG,MAAM,GAAG,KAAK,GAAG,OAAO,GAAG,QAAQ,CAAC;KACrD,GACA,OAAO,CAAC,CAAC,CAAC,CAAC;IACd,eAAe,CAAC,CAAC,EAAE,IAAI,EAAE;QACvB,MAAM,EAAE,KAAK,GAAG,MAAM,GAAG,OAAO,GAAG,KAAK,GAAG,QAAQ,CAAC;QACpD,GAAG,EAAE,MAAM,CAAC;QACZ,IAAI,CAAC,EAAE,OAAO,CAAC;KAChB,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;IACf,kBAAkB,CAAC,CAAC,EAAE,IAAI,EAAE;QAC1B,MAAM,EAAE,KAAK,GAAG,MAAM,GAAG,OAAO,GAAG,KAAK,GAAG,QAAQ,CAAC;QACpD,IAAI,EAAE,MAAM,CAAC;QACb,IAAI,CAAC,EAAE,OAAO,CAAC;KAChB,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;IACf,UAAU,IAAI,MAAM,CAAC;CACtB;AAED,KAAK,IAAI,GAAG;IACV,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG;IAC3B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,IAAI,EAAE,CAAC;IACd,KAAK,EAAE,MAAM,EAAE,CAAC;CACjB,CAAC;AAkCF,KAAK,sBAAsB,GAAG;IAC5B,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;CACjB,GAAG,CACA;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,KAAK,CAAA;CAAE,GACrC;IAAE,MAAM,CAAC,EAAE,KAAK,CAAC;IAAC,SAAS,EAAE,mBAAmB,CAAA;CAAE,CACrD,CAAC;AAEF,qBAAa,eAAe;IAC1B,OAAO,CAAC,MAAM,CAAC,CAAS;IACxB,OAAO,CAAC,SAAS,CAAC,CAAsB;IACxC,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,OAAO,CAAS;gBAEZ,IAAI,EAAE,sBAAsB;YAW1B,WAAW;IA0CnB,YAAY,CAChB,OAAO,EAAE,MAAM,EACf,MAAM,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,GAC1D,OAAO,CAAC,cAAc,CAAC;YAqBZ,iBAAiB;YA2EjB,cAAc;IA+B5B,OAAO,CAAC,kBAAkB;IAmB1B,OAAO,CAAC,KAAK;CAGd"}
|
|
1
|
+
{"version":3,"file":"mailosaur-client.d.ts","sourceRoot":"","sources":["../src/mailosaur-client.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,mBAAmB;IAClC,OAAO,CAAC,CAAC,EACP,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE;QACP,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QAChC,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QAC/B,MAAM,EAAE,KAAK,GAAG,MAAM,GAAG,KAAK,GAAG,OAAO,GAAG,QAAQ,CAAC;KACrD,GACA,OAAO,CAAC,CAAC,CAAC,CAAC;IACd,eAAe,CAAC,CAAC,EAAE,IAAI,EAAE;QACvB,MAAM,EAAE,KAAK,GAAG,MAAM,GAAG,OAAO,GAAG,KAAK,GAAG,QAAQ,CAAC;QACpD,GAAG,EAAE,MAAM,CAAC;QACZ,IAAI,CAAC,EAAE,OAAO,CAAC;KAChB,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;IACf,kBAAkB,CAAC,CAAC,EAAE,IAAI,EAAE;QAC1B,MAAM,EAAE,KAAK,GAAG,MAAM,GAAG,OAAO,GAAG,KAAK,GAAG,QAAQ,CAAC;QACpD,IAAI,EAAE,MAAM,CAAC;QACb,IAAI,CAAC,EAAE,OAAO,CAAC;KAChB,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;IACf,oBAAoB,CAAC,CAAC,EAAE,IAAI,EAAE;QAC5B,MAAM,EAAE,KAAK,GAAG,MAAM,GAAG,OAAO,GAAG,KAAK,GAAG,QAAQ,CAAC;QACpD,IAAI,EAAE,MAAM,CAAC;QACb,IAAI,CAAC,EAAE,OAAO,CAAC;KAChB,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;IACf,UAAU,IAAI,MAAM,CAAC;CACtB;AAED,KAAK,IAAI,GAAG;IACV,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG;IAC3B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,IAAI,EAAE,CAAC;IACd,KAAK,EAAE,MAAM,EAAE,CAAC;CACjB,CAAC;AAkCF,KAAK,sBAAsB,GAAG;IAC5B,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;CACjB,GAAG,CACA;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,KAAK,CAAA;CAAE,GACrC;IAAE,MAAM,CAAC,EAAE,KAAK,CAAC;IAAC,SAAS,EAAE,mBAAmB,CAAA;CAAE,CACrD,CAAC;AAEF,qBAAa,eAAe;IAC1B,OAAO,CAAC,MAAM,CAAC,CAAS;IACxB,OAAO,CAAC,SAAS,CAAC,CAAsB;IACxC,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,OAAO,CAAS;gBAEZ,IAAI,EAAE,sBAAsB;YAW1B,WAAW;IA0CnB,YAAY,CAChB,OAAO,EAAE,MAAM,EACf,MAAM,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,GAC1D,OAAO,CAAC,cAAc,CAAC;YAqBZ,iBAAiB;YA2EjB,cAAc;IA+B5B,OAAO,CAAC,kBAAkB;IAmB1B,OAAO,CAAC,KAAK;CAGd"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"empirical-reporter.d.ts","sourceRoot":"","sources":["../../src/reporter/empirical-reporter.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,UAAU,EACV,QAAQ,EACR,QAAQ,EACR,UAAU,EACX,MAAM,2BAA2B,CAAC;
|
|
1
|
+
{"version":3,"file":"empirical-reporter.d.ts","sourceRoot":"","sources":["../../src/reporter/empirical-reporter.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,UAAU,EACV,QAAQ,EACR,QAAQ,EACR,UAAU,EACX,MAAM,2BAA2B,CAAC;AAmBnC,cAAM,iBAAkB,YAAW,QAAQ;IACzC,OAAO,CAAC,kBAAkB,CAAyB;IACnD,OAAO,CAAC,oBAAoB,CAG1B;IACF,OAAO,CAAC,sBAAsB,CAAuB;IACrD,OAAO,CAAC,YAAY,CAAgD;IACpE,OAAO,CAAC,iBAAiB,CAAyC;IAClE,OAAO,CAAC,gBAAgB,CAAkC;IAC1D,OAAO,CAAC,SAAS,CAAyB;;IAM1C,OAAO,CAAC,6BAA6B,CAkCnC;IAEF,SAAS,CAAC,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,UAAU;IAoFtC,KAAK,CAAC,MAAM,EAAE,UAAU;IA8G9B,OAAO,CAAC,gBAAgB;YAoBV,gBAAgB;YAOhB,iBAAiB;CAmChC;AAED,eAAe,iBAAiB,CAAC"}
|
|
@@ -6,6 +6,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
6
|
const fs_1 = __importDefault(require("fs"));
|
|
7
7
|
const path_1 = __importDefault(require("path"));
|
|
8
8
|
const logger_1 = require("../logger");
|
|
9
|
+
const telemetry_1 = require("../telemetry");
|
|
9
10
|
const blob_utils_1 = require("./blob-utils");
|
|
10
11
|
const uploader_1 = require("./uploader");
|
|
11
12
|
const util_1 = require("./util");
|
|
@@ -198,12 +199,30 @@ class EmpiricalReporter {
|
|
|
198
199
|
async _uploadBlobReport(blobDir) {
|
|
199
200
|
if (!this._uploader)
|
|
200
201
|
return;
|
|
202
|
+
const uploadStartTime = Date.now();
|
|
201
203
|
try {
|
|
202
204
|
const files = await fs_1.default.promises.readdir(blobDir);
|
|
203
205
|
logger_1.logger.info(`[Empirical Reporter] Uploading blob report from ${blobDir}, files: ${files.join(", ")}`);
|
|
206
|
+
let sizeBytes = 0;
|
|
207
|
+
for (const file of files) {
|
|
208
|
+
const stat = await fs_1.default.promises.stat(path_1.default.join(blobDir, file));
|
|
209
|
+
sizeBytes += stat.size;
|
|
210
|
+
}
|
|
204
211
|
await this._uploader.uploadDirectory(blobDir, "blobs");
|
|
205
212
|
await this._uploader.waitForUploads();
|
|
206
213
|
logger_1.logger.info("[Empirical Reporter] Blob report uploaded successfully");
|
|
214
|
+
const testRunId = process.env.TEST_RUN_GITHUB_ACTION_ID;
|
|
215
|
+
if (testRunId) {
|
|
216
|
+
const shardIndex = process.env.SHARD_INDEX
|
|
217
|
+
? parseInt(process.env.SHARD_INDEX, 10)
|
|
218
|
+
: undefined;
|
|
219
|
+
await (0, telemetry_1.trackReportUpload)({
|
|
220
|
+
testRunId,
|
|
221
|
+
durationMs: Date.now() - uploadStartTime,
|
|
222
|
+
shardIndex,
|
|
223
|
+
sizeBytes,
|
|
224
|
+
});
|
|
225
|
+
}
|
|
207
226
|
}
|
|
208
227
|
catch (error) {
|
|
209
228
|
logger_1.logger.error("[Empirical Reporter] Failed to upload blob report:", error);
|
package/dist/telemetry.d.ts
CHANGED
|
@@ -1,11 +1,19 @@
|
|
|
1
1
|
import { TelemetryEvent } from "@empiricalrun/shared-types/telemetry";
|
|
2
2
|
export declare function trackEvent(event: TelemetryEvent): Promise<void>;
|
|
3
|
+
interface TrackReportUploadParams {
|
|
4
|
+
testRunId: string;
|
|
5
|
+
durationMs: number;
|
|
6
|
+
shardIndex?: number;
|
|
7
|
+
sizeBytes: number;
|
|
8
|
+
}
|
|
9
|
+
export declare function trackReportUpload(params: TrackReportUploadParams): Promise<void>;
|
|
3
10
|
export declare function trackOverlayDismissed(opts: {
|
|
4
11
|
original: string | undefined;
|
|
5
12
|
description: string;
|
|
6
13
|
text: string | undefined;
|
|
7
14
|
cache: boolean;
|
|
8
15
|
success: boolean;
|
|
16
|
+
durationMs: number;
|
|
9
17
|
testName?: string;
|
|
10
18
|
tokens?: {
|
|
11
19
|
input: number;
|
|
@@ -15,5 +23,6 @@ export declare function trackOverlayDismissed(opts: {
|
|
|
15
23
|
input: number;
|
|
16
24
|
output: number;
|
|
17
25
|
};
|
|
18
|
-
}): void
|
|
26
|
+
}): Promise<void>;
|
|
27
|
+
export {};
|
|
19
28
|
//# sourceMappingURL=telemetry.d.ts.map
|
package/dist/telemetry.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"telemetry.d.ts","sourceRoot":"","sources":["../src/telemetry.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,sCAAsC,CAAC;AAKtE,wBAAsB,UAAU,CAAC,KAAK,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAqBrE;AAED,
|
|
1
|
+
{"version":3,"file":"telemetry.d.ts","sourceRoot":"","sources":["../src/telemetry.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,sCAAsC,CAAC;AAKtE,wBAAsB,UAAU,CAAC,KAAK,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAqBrE;AAED,UAAU,uBAAuB;IAC/B,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,wBAAsB,iBAAiB,CACrC,MAAM,EAAE,uBAAuB,GAC9B,OAAO,CAAC,IAAI,CAAC,CAcf;AAED,wBAAsB,qBAAqB,CAAC,IAAI,EAAE;IAChD,QAAQ,EAAE,MAAM,GAAG,SAAS,CAAC;IAC7B,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,MAAM,GAAG,SAAS,CAAC;IACzB,KAAK,EAAE,OAAO,CAAC;IACf,OAAO,EAAE,OAAO,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IAC3C,IAAI,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;CAC1C,GAAG,OAAO,CAAC,IAAI,CAAC,CAsBhB"}
|
package/dist/telemetry.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.trackEvent = trackEvent;
|
|
4
|
+
exports.trackReportUpload = trackReportUpload;
|
|
4
5
|
exports.trackOverlayDismissed = trackOverlayDismissed;
|
|
5
6
|
const TELEMETRY_WORKER_URL = "https://telemetry-worker.empirical-run.workers.dev";
|
|
6
7
|
async function trackEvent(event) {
|
|
@@ -25,9 +26,24 @@ async function trackEvent(event) {
|
|
|
25
26
|
// Don't throw - telemetry failures shouldn't break the application
|
|
26
27
|
}
|
|
27
28
|
}
|
|
28
|
-
function
|
|
29
|
+
async function trackReportUpload(params) {
|
|
30
|
+
const environment = process.env.ENVIRONMENT || "unknown";
|
|
31
|
+
await trackEvent({
|
|
32
|
+
name: "test_worker.segment.report_upload",
|
|
33
|
+
properties: {
|
|
34
|
+
test_run_id: params.testRunId,
|
|
35
|
+
duration_ms: params.durationMs,
|
|
36
|
+
size_bytes: params.sizeBytes,
|
|
37
|
+
environment,
|
|
38
|
+
...(params.shardIndex !== undefined && {
|
|
39
|
+
shard_index: params.shardIndex,
|
|
40
|
+
}),
|
|
41
|
+
},
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
async function trackOverlayDismissed(opts) {
|
|
29
45
|
const environment = process.env.TEST_RUN_ENVIRONMENT || "unknown";
|
|
30
|
-
|
|
46
|
+
await trackEvent({
|
|
31
47
|
name: "overlay.dismissed",
|
|
32
48
|
properties: {
|
|
33
49
|
environment,
|
|
@@ -44,6 +60,7 @@ function trackOverlayDismissed(opts) {
|
|
|
44
60
|
tokens_output: opts.tokens?.output ?? 0,
|
|
45
61
|
cost_input: opts.cost?.input ?? 0,
|
|
46
62
|
cost_output: opts.cost?.output ?? 0,
|
|
63
|
+
duration_ms: opts.durationMs,
|
|
47
64
|
},
|
|
48
65
|
});
|
|
49
66
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/test/expect/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAC7C,OAAO,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AACvC,OAAO,EAAE,qBAAqB,EAAE,KAAK,qBAAqB,EAAE,MAAM,WAAW,CAAC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.toHaveReceivedWebhook = exports.toLookRight = void 0;
|
|
4
|
+
var visual_1 = require("./visual");
|
|
5
|
+
Object.defineProperty(exports, "toLookRight", { enumerable: true, get: function () { return visual_1.toLookRight; } });
|
|
6
|
+
var webhook_1 = require("./webhook");
|
|
7
|
+
Object.defineProperty(exports, "toHaveReceivedWebhook", { enumerable: true, get: function () { return webhook_1.toHaveReceivedWebhook; } });
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/test/expect/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,aAAa,GAAG;IAC1B,OAAO,EAAE,MAAM,MAAM,CAAC;IACtB,IAAI,EAAE,OAAO,CAAC;CACf,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"visual.d.ts","sourceRoot":"","sources":["../../../src/test/expect/visual.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAM7C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AA0B7C,wBAAsB,WAAW,CAC/B,IAAI,EAAE,IAAI,EACV,eAAe,EAAE,MAAM,GACtB,OAAO,CAAC,aAAa,CAAC,CAkFxB"}
|
|
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.toLookRight = toLookRight;
|
|
4
4
|
const llm_1 = require("@empiricalrun/llm");
|
|
5
5
|
const vision_1 = require("@empiricalrun/llm/vision");
|
|
6
|
-
const mouse_pointer_1 = require("
|
|
6
|
+
const mouse_pointer_1 = require("../scripts/mouse-pointer");
|
|
7
7
|
const SYSTEM_PROMPT = `
|
|
8
8
|
You are a software QA tester who specializes in visual testing. You look at a
|
|
9
9
|
UI screenshot and evaluate whether the UI is rendered correctly. While you don't
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { MatcherResult } from "./types";
|
|
2
|
+
export type WebhookMatcherOptions = {
|
|
3
|
+
content: string | string[];
|
|
4
|
+
timeout?: number;
|
|
5
|
+
};
|
|
6
|
+
export declare function toHaveReceivedWebhook(webhookUrl: string, options: WebhookMatcherOptions): Promise<MatcherResult>;
|
|
7
|
+
//# sourceMappingURL=webhook.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"webhook.d.ts","sourceRoot":"","sources":["../../../src/test/expect/webhook.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAK7C,MAAM,MAAM,qBAAqB,GAAG;IAClC,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IAC3B,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF,wBAAsB,qBAAqB,CACzC,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,qBAAqB,GAC7B,OAAO,CAAC,aAAa,CAAC,CAmCxB"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.toHaveReceivedWebhook = toHaveReceivedWebhook;
|
|
4
|
+
const logger_1 = require("../../logger");
|
|
5
|
+
const webhook_1 = require("../../webhook");
|
|
6
|
+
const POLL_INTERVAL = 6_000;
|
|
7
|
+
const DEFAULT_TIMEOUT = 300_000;
|
|
8
|
+
async function toHaveReceivedWebhook(webhookUrl, options) {
|
|
9
|
+
const { content, timeout = DEFAULT_TIMEOUT } = options;
|
|
10
|
+
const deadline = Date.now() + timeout;
|
|
11
|
+
let lastError;
|
|
12
|
+
while (true) {
|
|
13
|
+
try {
|
|
14
|
+
const requests = await (0, webhook_1.queryWebhookRequests)(webhookUrl, content);
|
|
15
|
+
if (requests[0]) {
|
|
16
|
+
return {
|
|
17
|
+
message: () => `Expected not to receive webhook matching content ${JSON.stringify(content)}, but received it`,
|
|
18
|
+
pass: true,
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
lastError = undefined;
|
|
22
|
+
}
|
|
23
|
+
catch (e) {
|
|
24
|
+
lastError = e;
|
|
25
|
+
logger_1.logger.error("Webhook provider error during polling", e);
|
|
26
|
+
}
|
|
27
|
+
const remaining = deadline - Date.now();
|
|
28
|
+
if (remaining <= 0)
|
|
29
|
+
break;
|
|
30
|
+
await new Promise((r) => setTimeout(r, Math.min(POLL_INTERVAL, remaining)));
|
|
31
|
+
}
|
|
32
|
+
return {
|
|
33
|
+
message: () => {
|
|
34
|
+
const base = `Webhook not received for content ${JSON.stringify(content)} within ${timeout}ms`;
|
|
35
|
+
return lastError
|
|
36
|
+
? `${base}. Last error: ${lastError instanceof Error ? lastError.message : String(lastError)}`
|
|
37
|
+
: base;
|
|
38
|
+
},
|
|
39
|
+
pass: false,
|
|
40
|
+
};
|
|
41
|
+
}
|
package/dist/test/index.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { BrowserContext, BrowserContextOptions, expect, Page, PlaywrightTestArgs, PlaywrightTestOptions, PlaywrightWorkerArgs, PlaywrightWorkerOptions, TestType } from "@playwright/test";
|
|
2
2
|
import { KVClient } from "../kv";
|
|
3
3
|
import { PostgresClient } from "../postgres";
|
|
4
|
+
import { type WebhookMatcherOptions } from "./expect";
|
|
4
5
|
import { injectLocatorHighlightScripts } from "./scripts";
|
|
5
6
|
import { HighlighterOpts } from "./types";
|
|
6
7
|
import { setVideoLabel } from "./video-labels";
|
|
@@ -8,6 +9,7 @@ declare global {
|
|
|
8
9
|
namespace PlaywrightTest {
|
|
9
10
|
interface Matchers<R> {
|
|
10
11
|
toLookRight(description: string): Promise<R>;
|
|
12
|
+
toHaveReceivedWebhook(options: WebhookMatcherOptions): Promise<R>;
|
|
11
13
|
}
|
|
12
14
|
}
|
|
13
15
|
}
|
package/dist/test/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/test/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,cAAc,EACd,qBAAqB,EACrB,MAAM,EACN,IAAI,EACJ,kBAAkB,EAClB,qBAAqB,EACrB,oBAAoB,EACpB,uBAAuB,EACvB,QAAQ,EACT,MAAM,kBAAkB,CAAC;AAG1B,OAAO,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AACjC,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/test/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,cAAc,EACd,qBAAqB,EACrB,MAAM,EACN,IAAI,EACJ,kBAAkB,EAClB,qBAAqB,EACrB,oBAAoB,EACpB,uBAAuB,EACvB,QAAQ,EACT,MAAM,kBAAkB,CAAC;AAG1B,OAAO,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AACjC,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAGL,KAAK,qBAAqB,EAC3B,MAAM,UAAU,CAAC;AAClB,OAAO,EAAE,6BAA6B,EAAE,MAAM,WAAW,CAAC;AAC1D,OAAO,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAC1C,OAAO,EAA8B,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAE3E,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,cAAc,CAAC;QACvB,UAAU,QAAQ,CAAC,CAAC;YAClB,WAAW,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;YAC7C,qBAAqB,CAAC,OAAO,EAAE,qBAAqB,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;SACnE;KACF;CACF;AAED,QAAA,MAAM,YAAY,GAAa,gBAAgB,OAAO,MAAM,0CAG3D,CAAC;AAEF,KAAK,WAAW,GAAG;IACjB,IAAI,EAAE,IAAI,CAAC;IACX,EAAE,EAAE,QAAQ,CAAC;IACb,QAAQ,EAAE,cAAc,CAAC;IACzB,yBAAyB,EAAE,CACzB,OAAO,CAAC,EAAE,qBAAqB,KAC5B,OAAO,CAAC;QAAE,OAAO,EAAE,cAAc,CAAC;QAAC,IAAI,EAAE,IAAI,CAAA;KAAE,CAAC,CAAC;IACtD,UAAU,EAAE,IAAI,CAAC;CAClB,CAAC;AAYF,QAAA,MAAM,eAAe,GACnB,QAAQ,QAAQ,CACd,kBAAkB,GAAG,qBAAqB,EAC1C,oBAAoB,GAAG,uBAAuB,CAC/C,EACD,UAAS,eAAgC,uHA2G1C,CAAC;AAEF,OAAO,EACL,eAAe,EACf,YAAY,EACZ,6BAA6B,EAC7B,aAAa,GACd,CAAC"}
|
package/dist/test/index.js
CHANGED
|
@@ -13,7 +13,7 @@ Object.defineProperty(exports, "injectLocatorHighlightScripts", { enumerable: tr
|
|
|
13
13
|
const video_labels_1 = require("./video-labels");
|
|
14
14
|
Object.defineProperty(exports, "setVideoLabel", { enumerable: true, get: function () { return video_labels_1.setVideoLabel; } });
|
|
15
15
|
const extendExpect = function (expectInstance) {
|
|
16
|
-
expectInstance.extend({ toLookRight: expect_1.toLookRight });
|
|
16
|
+
expectInstance.extend({ toLookRight: expect_1.toLookRight, toHaveReceivedWebhook: expect_1.toHaveReceivedWebhook });
|
|
17
17
|
return expectInstance;
|
|
18
18
|
};
|
|
19
19
|
exports.extendExpect = extendExpect;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/test/scripts/pw-locator-patch/dismiss-overlays/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAGxC,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAG1C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAK9C,wBAAgB,gBAAgB,CAAC,YAAY,EAAE,MAAM,GAAG,SAAS,WAKhE;AAED,qBAAa,mBAAmB;IAOlB,OAAO,CAAC,QAAQ,CAAC,IAAI;IANjC,OAAO,CAAC,iBAAiB,CAIhB;gBAEoB,IAAI,EAAE,IAAI;IAEjC,WAAW;IAMX,GAAG,CACP,aAAa,EAAE,GAAG,EAClB,QAAQ,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,IAAI,EACvC,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/test/scripts/pw-locator-patch/dismiss-overlays/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAGxC,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAG1C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAK9C,wBAAgB,gBAAgB,CAAC,YAAY,EAAE,MAAM,GAAG,SAAS,WAKhE;AAED,qBAAa,mBAAmB;IAOlB,OAAO,CAAC,QAAQ,CAAC,IAAI;IANjC,OAAO,CAAC,iBAAiB,CAIhB;gBAEoB,IAAI,EAAE,IAAI;IAEjC,WAAW;IAMX,GAAG,CACP,aAAa,EAAE,GAAG,EAClB,QAAQ,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,IAAI,EACvC,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,IAAI,CAAC;YAyDF,QAAQ;CA6BvB;AAED,wBAAgB,0BAA0B,CACxC,YAAY,EAAE,MAAM,GACnB,cAAc,GAAG,SAAS,CAiC5B"}
|
|
@@ -27,6 +27,7 @@ class OverlayDismissAgent {
|
|
|
27
27
|
}
|
|
28
28
|
}
|
|
29
29
|
async run(originalError, reporter, testFn) {
|
|
30
|
+
const startTime = Date.now();
|
|
30
31
|
let element;
|
|
31
32
|
try {
|
|
32
33
|
element = extractInterceptingElement(originalError.message);
|
|
@@ -51,28 +52,31 @@ class OverlayDismissAgent {
|
|
|
51
52
|
text,
|
|
52
53
|
});
|
|
53
54
|
if (success) {
|
|
54
|
-
(0, telemetry_1.trackOverlayDismissed)({
|
|
55
|
+
await (0, telemetry_1.trackOverlayDismissed)({
|
|
55
56
|
...eventProps,
|
|
56
57
|
cache: true,
|
|
57
58
|
success: true,
|
|
59
|
+
durationMs: Date.now() - startTime,
|
|
58
60
|
});
|
|
59
61
|
return;
|
|
60
62
|
}
|
|
61
63
|
const usage = await this.runAgent(element, text, testFn);
|
|
62
|
-
(0, telemetry_1.trackOverlayDismissed)({
|
|
64
|
+
await (0, telemetry_1.trackOverlayDismissed)({
|
|
63
65
|
...eventProps,
|
|
64
66
|
cache: false,
|
|
65
67
|
success: true,
|
|
66
68
|
tokens: usage.tokens,
|
|
67
69
|
cost: usage.cost,
|
|
70
|
+
durationMs: Date.now() - startTime,
|
|
68
71
|
});
|
|
69
72
|
}
|
|
70
73
|
catch (agentError) {
|
|
71
74
|
reporter(`Error during overlay dismissal agent execution: ${agentError.toString()}`);
|
|
72
|
-
(0, telemetry_1.trackOverlayDismissed)({
|
|
75
|
+
await (0, telemetry_1.trackOverlayDismissed)({
|
|
73
76
|
...eventProps,
|
|
74
77
|
cache: false,
|
|
75
78
|
success: false,
|
|
79
|
+
durationMs: Date.now() - startTime,
|
|
76
80
|
});
|
|
77
81
|
throw originalError;
|
|
78
82
|
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export type WebhookRequest = {
|
|
2
|
+
uuid: string;
|
|
3
|
+
token_id: string;
|
|
4
|
+
method: string;
|
|
5
|
+
content: string;
|
|
6
|
+
headers: Record<string, string[]>;
|
|
7
|
+
url: string;
|
|
8
|
+
query: Record<string, string> | null;
|
|
9
|
+
ip: string;
|
|
10
|
+
hostname: string;
|
|
11
|
+
user_agent: string;
|
|
12
|
+
size: number;
|
|
13
|
+
created_at: string;
|
|
14
|
+
updated_at: string;
|
|
15
|
+
};
|
|
16
|
+
export declare function getWebhookUrl(options?: {
|
|
17
|
+
expiry?: number | null;
|
|
18
|
+
}): Promise<string>;
|
|
19
|
+
export declare function queryWebhookRequests(webhookUrl: string, content: string | string[]): Promise<WebhookRequest[]>;
|
|
20
|
+
//# sourceMappingURL=webhook.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"webhook.d.ts","sourceRoot":"","sources":["../src/webhook.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,cAAc,GAAG;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;IAClC,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI,CAAC;IACrC,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;CACpB,CAAC;AAaF,wBAAsB,aAAa,CACjC,OAAO,GAAE;IAAE,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CAAO,GACvC,OAAO,CAAC,MAAM,CAAC,CAuCjB;AA2FD,wBAAsB,oBAAoB,CACxC,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE,GACzB,OAAO,CAAC,cAAc,EAAE,CAAC,CAM3B"}
|
package/dist/webhook.js
ADDED
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getWebhookUrl = getWebhookUrl;
|
|
4
|
+
exports.queryWebhookRequests = queryWebhookRequests;
|
|
5
|
+
const dashboard_1 = require("@empiricalrun/test-gen/dashboard");
|
|
6
|
+
const WEBHOOK_SITE_BASE_URL = "https://webhook.site";
|
|
7
|
+
const DEFAULT_TOKEN_EXPIRY = 604800;
|
|
8
|
+
async function getWebhookUrl(options = {}) {
|
|
9
|
+
const expiry = options.expiry === undefined ? DEFAULT_TOKEN_EXPIRY : options.expiry;
|
|
10
|
+
if (process.env.EMPIRICALRUN_API_KEY) {
|
|
11
|
+
const apiClient = new dashboard_1.DashboardAPIClient({
|
|
12
|
+
authType: "project-api-key",
|
|
13
|
+
});
|
|
14
|
+
const result = await apiClient.callWebhookSiteProxy({
|
|
15
|
+
method: "POST",
|
|
16
|
+
path: "/token",
|
|
17
|
+
body: { expiry },
|
|
18
|
+
});
|
|
19
|
+
return `${WEBHOOK_SITE_BASE_URL}/${result.uuid}`;
|
|
20
|
+
}
|
|
21
|
+
const apiKey = process.env.WEBHOOK_SITE_API_KEY;
|
|
22
|
+
if (!apiKey) {
|
|
23
|
+
throw new Error("Either EMPIRICALRUN_API_KEY or WEBHOOK_SITE_API_KEY must be set");
|
|
24
|
+
}
|
|
25
|
+
const response = await fetch(`${WEBHOOK_SITE_BASE_URL}/token`, {
|
|
26
|
+
method: "POST",
|
|
27
|
+
headers: {
|
|
28
|
+
"Api-Key": apiKey,
|
|
29
|
+
"Content-Type": "application/json",
|
|
30
|
+
},
|
|
31
|
+
body: JSON.stringify({ expiry }),
|
|
32
|
+
});
|
|
33
|
+
if (!response.ok) {
|
|
34
|
+
const body = await response.text().catch(() => "");
|
|
35
|
+
throw new Error(`webhook.site error [${response.status} ${response.statusText}]: ${body}`);
|
|
36
|
+
}
|
|
37
|
+
const { uuid } = await response.json();
|
|
38
|
+
return `${WEBHOOK_SITE_BASE_URL}/${uuid}`;
|
|
39
|
+
}
|
|
40
|
+
function extractToken(webhookUrl) {
|
|
41
|
+
let url;
|
|
42
|
+
try {
|
|
43
|
+
url = new URL(webhookUrl);
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
throw new Error(`Invalid webhook URL: ${webhookUrl}`);
|
|
47
|
+
}
|
|
48
|
+
const parts = url.pathname.split("/").filter(Boolean);
|
|
49
|
+
const token = parts[parts.length - 1];
|
|
50
|
+
if (!token) {
|
|
51
|
+
throw new Error(`Invalid webhook URL (no token in path): ${webhookUrl}`);
|
|
52
|
+
}
|
|
53
|
+
return token;
|
|
54
|
+
}
|
|
55
|
+
function escapeQueryTerm(term) {
|
|
56
|
+
return term.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
|
|
57
|
+
}
|
|
58
|
+
function buildContentQuery(content) {
|
|
59
|
+
const terms = Array.isArray(content) ? content : [content];
|
|
60
|
+
if (terms.length === 0 || terms.some((t) => t.length === 0)) {
|
|
61
|
+
throw new Error("content must be a non-empty string or string[]");
|
|
62
|
+
}
|
|
63
|
+
return terms.map((t) => `content:"${escapeQueryTerm(t)}"`).join(" AND ");
|
|
64
|
+
}
|
|
65
|
+
async function queryViaProxy(token, content) {
|
|
66
|
+
const apiClient = new dashboard_1.DashboardAPIClient({
|
|
67
|
+
authType: "project-api-key",
|
|
68
|
+
});
|
|
69
|
+
const params = new URLSearchParams({
|
|
70
|
+
query: buildContentQuery(content),
|
|
71
|
+
sorting: "newest",
|
|
72
|
+
per_page: "20",
|
|
73
|
+
});
|
|
74
|
+
const result = await apiClient.callWebhookSiteProxy({
|
|
75
|
+
method: "GET",
|
|
76
|
+
path: `/token/${token}/requests?${params.toString()}`,
|
|
77
|
+
});
|
|
78
|
+
if (!result.data || !Array.isArray(result.data)) {
|
|
79
|
+
throw new Error("webhook.site proxy response missing 'data' array");
|
|
80
|
+
}
|
|
81
|
+
return result.data;
|
|
82
|
+
}
|
|
83
|
+
async function queryDirect(token, content) {
|
|
84
|
+
const apiKey = process.env.WEBHOOK_SITE_API_KEY;
|
|
85
|
+
if (!apiKey) {
|
|
86
|
+
throw new Error("Either EMPIRICALRUN_API_KEY or WEBHOOK_SITE_API_KEY must be set");
|
|
87
|
+
}
|
|
88
|
+
const params = new URLSearchParams({
|
|
89
|
+
query: buildContentQuery(content),
|
|
90
|
+
sorting: "newest",
|
|
91
|
+
per_page: "20",
|
|
92
|
+
});
|
|
93
|
+
const rawResponse = await fetch(`https://webhook.site/token/${token}/requests?${params.toString()}`, {
|
|
94
|
+
headers: {
|
|
95
|
+
"Api-Key": apiKey,
|
|
96
|
+
},
|
|
97
|
+
});
|
|
98
|
+
if (!rawResponse.ok) {
|
|
99
|
+
const body = await rawResponse.text().catch(() => "");
|
|
100
|
+
throw new Error(`webhook.site error [${rawResponse.status} ${rawResponse.statusText}]: ${body}`);
|
|
101
|
+
}
|
|
102
|
+
const json = await rawResponse.json().catch((e) => {
|
|
103
|
+
throw new Error(`webhook.site returned invalid JSON: ${String(e)}`);
|
|
104
|
+
});
|
|
105
|
+
if (!json || !Array.isArray(json.data)) {
|
|
106
|
+
throw new Error("webhook.site response missing 'data' array");
|
|
107
|
+
}
|
|
108
|
+
return json.data;
|
|
109
|
+
}
|
|
110
|
+
async function queryWebhookRequests(webhookUrl, content) {
|
|
111
|
+
const token = extractToken(webhookUrl);
|
|
112
|
+
if (process.env.EMPIRICALRUN_API_KEY) {
|
|
113
|
+
return queryViaProxy(token, content);
|
|
114
|
+
}
|
|
115
|
+
return queryDirect(token, content);
|
|
116
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@empiricalrun/playwright-utils",
|
|
3
|
-
"version": "0.39.
|
|
3
|
+
"version": "0.39.2",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"registry": "https://registry.npmjs.org/",
|
|
6
6
|
"access": "public"
|
|
@@ -43,8 +43,8 @@
|
|
|
43
43
|
"puppeteer-extra-plugin-recaptcha": "^3.6.8",
|
|
44
44
|
"rimraf": "^6.0.1",
|
|
45
45
|
"@empiricalrun/llm": "^0.25.2",
|
|
46
|
-
"@empiricalrun/
|
|
47
|
-
"@empiricalrun/
|
|
46
|
+
"@empiricalrun/test-gen": "^0.79.3",
|
|
47
|
+
"@empiricalrun/r2-uploader": "^0.9.1"
|
|
48
48
|
},
|
|
49
49
|
"scripts": {
|
|
50
50
|
"dev": "tsc --build --watch",
|
package/tsconfig.tsbuildinfo
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"root":["./src/email.ts","./src/index.ts","./src/kv.ts","./src/logger.ts","./src/mailosaur-client.ts","./src/playwright-extensions.ts","./src/postgres.ts","./src/telemetry.ts","./src/auth/google.ts","./src/auth/index.ts","./src/auth/types.ts","./src/captcha/index.ts","./src/captcha/vision.ts","./src/config/index.ts","./src/config/proxy.ts","./src/config/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/blob-utils.ts","./src/reporter/empirical-reporter.ts","./src/reporter/uploader.ts","./src/reporter/util.ts","./src/test/constants.ts","./src/test/
|
|
1
|
+
{"root":["./src/email.ts","./src/index.ts","./src/kv.ts","./src/logger.ts","./src/mailosaur-client.ts","./src/playwright-extensions.ts","./src/postgres.ts","./src/telemetry.ts","./src/webhook.ts","./src/auth/google.ts","./src/auth/index.ts","./src/auth/types.ts","./src/captcha/index.ts","./src/captcha/vision.ts","./src/config/index.ts","./src/config/proxy.ts","./src/config/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/blob-utils.ts","./src/reporter/empirical-reporter.ts","./src/reporter/uploader.ts","./src/reporter/util.ts","./src/test/constants.ts","./src/test/index.ts","./src/test/types.ts","./src/test/video-labels.ts","./src/test/expect/index.ts","./src/test/expect/types.ts","./src/test/expect/visual.ts","./src/test/expect/webhook.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"}
|
package/dist/test/expect.d.ts
DELETED
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
import type { Page } from "@playwright/test";
|
|
2
|
-
export type VisualMatcherResult = {
|
|
3
|
-
message: () => string;
|
|
4
|
-
pass: boolean;
|
|
5
|
-
};
|
|
6
|
-
export declare function toLookRight(page: Page, pageDescription: string): Promise<VisualMatcherResult>;
|
|
7
|
-
//# sourceMappingURL=expect.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"expect.d.ts","sourceRoot":"","sources":["../../src/test/expect.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAO7C,MAAM,MAAM,mBAAmB,GAAG;IAChC,OAAO,EAAE,MAAM,MAAM,CAAC;IACtB,IAAI,EAAE,OAAO,CAAC;CACf,CAAC;AA0BF,wBAAsB,WAAW,CAC/B,IAAI,EAAE,IAAI,EACV,eAAe,EAAE,MAAM,GACtB,OAAO,CAAC,mBAAmB,CAAC,CAkF9B"}
|