@empiricalrun/playwright-utils 0.32.0 → 0.34.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 +19 -0
- package/README.md +2 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/postgres.d.ts +24 -0
- package/dist/postgres.d.ts.map +1 -0
- package/dist/postgres.js +117 -0
- package/dist/reporter/empirical-reporter.d.ts.map +1 -1
- package/dist/reporter/empirical-reporter.js +32 -27
- package/dist/reporter/util.d.ts.map +1 -1
- package/dist/reporter/util.js +15 -2
- package/dist/test/index.d.ts +2 -0
- package/dist/test/index.d.ts.map +1 -1
- package/dist/test/index.js +5 -0
- package/docs/postgres.md +78 -0
- package/package.json +5 -3
- package/tsconfig.tsbuildinfo +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,24 @@
|
|
|
1
1
|
# @empiricalrun/playwright-utils
|
|
2
2
|
|
|
3
|
+
## 0.34.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 88dd018: feat: proxy endpoint for neon db on dash
|
|
8
|
+
|
|
9
|
+
## 0.33.0
|
|
10
|
+
|
|
11
|
+
### Minor Changes
|
|
12
|
+
|
|
13
|
+
- 6c4f6de: feat: support html reporter for 1.57.0
|
|
14
|
+
- a94a7f7: fix: resolve multipart upload retry failures
|
|
15
|
+
|
|
16
|
+
### Patch Changes
|
|
17
|
+
|
|
18
|
+
- Updated dependencies [a94a7f7]
|
|
19
|
+
- @empiricalrun/r2-uploader@0.5.0
|
|
20
|
+
- @empiricalrun/test-gen@0.78.3
|
|
21
|
+
|
|
3
22
|
## 0.32.0
|
|
4
23
|
|
|
5
24
|
### Minor Changes
|
package/README.md
CHANGED
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,cAAc,SAAS,CAAC;AACxB,cAAc,MAAM,CAAC;AACrB,cAAc,yBAAyB,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,cAAc,SAAS,CAAC;AACxB,cAAc,MAAM,CAAC;AACrB,cAAc,yBAAyB,CAAC;AACxC,cAAc,YAAY,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -26,3 +26,4 @@ Object.defineProperty(exports, "devices", { enumerable: true, get: function () {
|
|
|
26
26
|
__exportStar(require("./email"), exports);
|
|
27
27
|
__exportStar(require("./kv"), exports);
|
|
28
28
|
__exportStar(require("./playwright-extensions"), exports);
|
|
29
|
+
__exportStar(require("./postgres"), exports);
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
type PostgresDatabase = {
|
|
2
|
+
projectId: string;
|
|
3
|
+
connectionUri: string;
|
|
4
|
+
};
|
|
5
|
+
type GetOptions = {
|
|
6
|
+
name?: string;
|
|
7
|
+
pgVersion?: number;
|
|
8
|
+
ttl?: number;
|
|
9
|
+
};
|
|
10
|
+
export declare class PostgresClient {
|
|
11
|
+
private client;
|
|
12
|
+
private kv;
|
|
13
|
+
constructor();
|
|
14
|
+
get(name: string, options?: GetOptions): Promise<PostgresDatabase>;
|
|
15
|
+
delete(name: string): Promise<void>;
|
|
16
|
+
query<T = Record<string, unknown>>(connectionUri: string, sql: string): Promise<T[]>;
|
|
17
|
+
execute(connectionUri: string, sql: string): Promise<{
|
|
18
|
+
statement: string;
|
|
19
|
+
rowCount: number;
|
|
20
|
+
}[]>;
|
|
21
|
+
private parseStatements;
|
|
22
|
+
}
|
|
23
|
+
export {};
|
|
24
|
+
//# sourceMappingURL=postgres.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"postgres.d.ts","sourceRoot":"","sources":["../src/postgres.ts"],"names":[],"mappings":"AA6BA,KAAK,gBAAgB,GAAG;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE,MAAM,CAAC;CACvB,CAAC;AAEF,KAAK,UAAU,GAAG;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,GAAG,CAAC,EAAE,MAAM,CAAC;CACd,CAAC;AAIF,qBAAa,cAAc;IACzB,OAAO,CAAC,MAAM,CAAqB;IACnC,OAAO,CAAC,EAAE,CAAW;;IAYf,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,UAAU,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAuClE,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAwBnC,KAAK,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACrC,aAAa,EAAE,MAAM,EACrB,GAAG,EAAE,MAAM,GACV,OAAO,CAAC,CAAC,EAAE,CAAC;IAWT,OAAO,CACX,aAAa,EAAE,MAAM,EACrB,GAAG,EAAE,MAAM,GACV,OAAO,CAAC;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IAsBrD,OAAO,CAAC,eAAe;CAsBxB"}
|
package/dist/postgres.js
ADDED
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PostgresClient = void 0;
|
|
4
|
+
const dashboard_1 = require("@empiricalrun/test-gen/dashboard");
|
|
5
|
+
const pg_1 = require("pg");
|
|
6
|
+
const kv_1 = require("./kv");
|
|
7
|
+
const DEFAULT_TTL = 3600;
|
|
8
|
+
class PostgresClient {
|
|
9
|
+
client;
|
|
10
|
+
kv;
|
|
11
|
+
constructor() {
|
|
12
|
+
if (!process.env.EMPIRICALRUN_API_KEY) {
|
|
13
|
+
throw new Error("EMPIRICALRUN_API_KEY environment variable is required");
|
|
14
|
+
}
|
|
15
|
+
this.client = new dashboard_1.DashboardAPIClient({
|
|
16
|
+
authType: "project-api-key",
|
|
17
|
+
});
|
|
18
|
+
this.kv = new kv_1.KVClient();
|
|
19
|
+
}
|
|
20
|
+
async get(name, options) {
|
|
21
|
+
const kvKey = `postgres:${name}`;
|
|
22
|
+
const existing = await this.kv.get(kvKey);
|
|
23
|
+
if (existing) {
|
|
24
|
+
return existing;
|
|
25
|
+
}
|
|
26
|
+
const response = await this.client.request("/api/neon/proxy", {
|
|
27
|
+
method: "POST",
|
|
28
|
+
body: {
|
|
29
|
+
action: "create",
|
|
30
|
+
payload: {
|
|
31
|
+
name: options?.name ?? name,
|
|
32
|
+
pg_version: options?.pgVersion,
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
});
|
|
36
|
+
if (response.error) {
|
|
37
|
+
throw new Error(`Postgres create failed: ${response.error}`);
|
|
38
|
+
}
|
|
39
|
+
const projectId = response.data.project.id;
|
|
40
|
+
const connectionUri = response.data.connection_uris?.[0]?.connection_uri;
|
|
41
|
+
if (!connectionUri) {
|
|
42
|
+
throw new Error("No connection URI returned from Neon API");
|
|
43
|
+
}
|
|
44
|
+
const db = { projectId, connectionUri };
|
|
45
|
+
await this.kv.set(kvKey, db, options?.ttl ?? DEFAULT_TTL);
|
|
46
|
+
return db;
|
|
47
|
+
}
|
|
48
|
+
async delete(name) {
|
|
49
|
+
const kvKey = `postgres:${name}`;
|
|
50
|
+
const existing = await this.kv.get(kvKey);
|
|
51
|
+
if (!existing) {
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
const response = await this.client.request("/api/neon/proxy", {
|
|
55
|
+
method: "POST",
|
|
56
|
+
body: {
|
|
57
|
+
action: "delete",
|
|
58
|
+
projectId: existing.projectId,
|
|
59
|
+
},
|
|
60
|
+
});
|
|
61
|
+
if (response.error) {
|
|
62
|
+
throw new Error(`Postgres delete failed: ${response.error}`);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
async query(connectionUri, sql) {
|
|
66
|
+
const client = new pg_1.Client({ connectionString: connectionUri });
|
|
67
|
+
try {
|
|
68
|
+
await client.connect();
|
|
69
|
+
const result = await client.query(sql);
|
|
70
|
+
return result.rows;
|
|
71
|
+
}
|
|
72
|
+
finally {
|
|
73
|
+
await client.end();
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
async execute(connectionUri, sql) {
|
|
77
|
+
const client = new pg_1.Client({ connectionString: connectionUri });
|
|
78
|
+
const results = [];
|
|
79
|
+
try {
|
|
80
|
+
await client.connect();
|
|
81
|
+
const statements = this.parseStatements(sql);
|
|
82
|
+
for (const statement of statements) {
|
|
83
|
+
const result = await client.query(statement);
|
|
84
|
+
results.push({
|
|
85
|
+
statement: statement.substring(0, 80),
|
|
86
|
+
rowCount: result.rowCount ?? 0,
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
return results;
|
|
90
|
+
}
|
|
91
|
+
finally {
|
|
92
|
+
await client.end();
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
parseStatements(sql) {
|
|
96
|
+
const lines = sql.split("\n");
|
|
97
|
+
const cleanedLines = [];
|
|
98
|
+
for (const line of lines) {
|
|
99
|
+
const trimmed = line.trim();
|
|
100
|
+
if (trimmed && !trimmed.startsWith("--")) {
|
|
101
|
+
const commentIndex = line.indexOf("--");
|
|
102
|
+
if (commentIndex > 0) {
|
|
103
|
+
cleanedLines.push(line.substring(0, commentIndex));
|
|
104
|
+
}
|
|
105
|
+
else {
|
|
106
|
+
cleanedLines.push(line);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
return cleanedLines
|
|
111
|
+
.join("\n")
|
|
112
|
+
.split(";")
|
|
113
|
+
.map((q) => q.trim())
|
|
114
|
+
.filter((q) => q.length > 0);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
exports.PostgresClient = PostgresClient;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"empirical-reporter.d.ts","sourceRoot":"","sources":["../../src/reporter/empirical-reporter.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EACV,UAAU,EACV,QAAQ,EACR,QAAQ,EACR,UAAU,EACX,MAAM,2BAA2B,CAAC;AAanC,cAAM,iBAAkB,YAAW,QAAQ;IACzC,OAAO,CAAC,eAAe,CAAS;IAChC,OAAO,CAAC,aAAa,CAAyB;IAC9C,OAAO,CAAC,kBAAkB,CAAyB;IACnD,OAAO,CAAC,oBAAoB,CAAkB;IAC9C,OAAO,CAAC,oBAAoB,CAG1B;IACF,OAAO,CAAC,QAAQ,CAAmC;IACnD,OAAO,CAAC,UAAU,CACwE;IAE1F,OAAO,CAAC,YAAY;;IAyBpB,OAAO,CAAC,6BAA6B,CA4BnC;IAEF,SAAS,CAAC,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,UAAU;
|
|
1
|
+
{"version":3,"file":"empirical-reporter.d.ts","sourceRoot":"","sources":["../../src/reporter/empirical-reporter.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EACV,UAAU,EACV,QAAQ,EACR,QAAQ,EACR,UAAU,EACX,MAAM,2BAA2B,CAAC;AAanC,cAAM,iBAAkB,YAAW,QAAQ;IACzC,OAAO,CAAC,eAAe,CAAS;IAChC,OAAO,CAAC,aAAa,CAAyB;IAC9C,OAAO,CAAC,kBAAkB,CAAyB;IACnD,OAAO,CAAC,oBAAoB,CAAkB;IAC9C,OAAO,CAAC,oBAAoB,CAG1B;IACF,OAAO,CAAC,QAAQ,CAAmC;IACnD,OAAO,CAAC,UAAU,CACwE;IAE1F,OAAO,CAAC,YAAY;;IAyBpB,OAAO,CAAC,6BAA6B,CA4BnC;IAEF,SAAS,CAAC,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,UAAU;IA8EtC,KAAK,CAAC,MAAM,EAAE,UAAU;CA+G/B;AAED,eAAe,iBAAiB,CAAC"}
|
|
@@ -67,49 +67,54 @@ class EmpiricalReporter {
|
|
|
67
67
|
}
|
|
68
68
|
try {
|
|
69
69
|
const attachmentPromises = result.attachments.map(async (attachment) => {
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
70
|
+
try {
|
|
71
|
+
const fileMap = await this.enqueTestAttachmentUploadTask(attachment);
|
|
72
|
+
if (!fileMap) {
|
|
73
|
+
logger_1.logger.error(`No upload result for ${attachment.name}`);
|
|
74
|
+
return undefined;
|
|
75
|
+
}
|
|
76
|
+
const url = Object.values(fileMap)[0];
|
|
77
|
+
if (!url) {
|
|
78
|
+
logger_1.logger.error(`No url in file map for ${attachment.name}`);
|
|
79
|
+
return undefined;
|
|
80
|
+
}
|
|
81
|
+
if (!attachment.path) {
|
|
82
|
+
logger_1.logger.error(`No path in attachment for ${attachment.name}`);
|
|
83
|
+
return undefined;
|
|
84
|
+
}
|
|
85
|
+
return {
|
|
86
|
+
name: attachment.name,
|
|
87
|
+
contentType: attachment.contentType,
|
|
88
|
+
path: attachment.path,
|
|
89
|
+
url,
|
|
90
|
+
};
|
|
80
91
|
}
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
logger_1.logger.error(`No path in file map for ${attachment}`);
|
|
92
|
+
catch (err) {
|
|
93
|
+
logger_1.logger.error(`[Empirical Reporter] Error uploading attachment ${attachment.name} for test ${test.title}:`, err);
|
|
84
94
|
return undefined;
|
|
85
95
|
}
|
|
86
|
-
return {
|
|
87
|
-
name: attachment.name,
|
|
88
|
-
contentType: attachment.contentType,
|
|
89
|
-
path: attachment.path,
|
|
90
|
-
url,
|
|
91
|
-
};
|
|
92
96
|
});
|
|
93
97
|
const testCaseRunEventTask = async () => {
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
98
|
+
const uploadedAttachments = await Promise.all(attachmentPromises);
|
|
99
|
+
const successfulAttachments = uploadedAttachments.filter((a) => a !== undefined);
|
|
100
|
+
try {
|
|
101
|
+
logger_1.logger.debug(`[Empirical Reporter] Attachments for test ${test.title} are uploaded:`, successfulAttachments);
|
|
97
102
|
const { suites, projectName } = (0, util_1.suitesAndProjectForTest)(test);
|
|
98
103
|
const params = {
|
|
99
104
|
test,
|
|
100
105
|
suites,
|
|
101
106
|
result,
|
|
102
107
|
projectName,
|
|
103
|
-
attachments:
|
|
108
|
+
attachments: successfulAttachments,
|
|
104
109
|
runId: process.env.TEST_RUN_GITHUB_ACTION_ID,
|
|
105
110
|
};
|
|
106
111
|
return (0, util_1.sendTestCaseUpdateToDashboard)(params);
|
|
107
|
-
}
|
|
108
|
-
|
|
112
|
+
}
|
|
113
|
+
catch (error) {
|
|
109
114
|
logger_1.logger.error(`[Empirical Reporter] Error sending test case event for: ${test.title}:`, error);
|
|
110
|
-
}
|
|
115
|
+
}
|
|
111
116
|
};
|
|
112
|
-
void (
|
|
117
|
+
void testCaseRunEventTask();
|
|
113
118
|
return;
|
|
114
119
|
}
|
|
115
120
|
catch (error) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"util.d.ts","sourceRoot":"","sources":["../../src/reporter/util.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,2BAA2B,EAC3B,qBAAqB,EACtB,MAAM,6CAA6C,CAAC;AACrD,OAAO,EACL,cAAc,EAGd,UAAU,IAAI,oBAAoB,EAElC,QAAQ,EACT,MAAM,2BAA2B,CAAC;AAoBnC,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,qBAAqB,GAC5B,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,EACpB,QAAQ,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"util.d.ts","sourceRoot":"","sources":["../../src/reporter/util.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,2BAA2B,EAC3B,qBAAqB,EACtB,MAAM,6CAA6C,CAAC;AACrD,OAAO,EACL,cAAc,EAGd,UAAU,IAAI,oBAAoB,EAElC,QAAQ,EACT,MAAM,2BAA2B,CAAC;AAoBnC,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,qBAAqB,GAC5B,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,EACpB,QAAQ,EAAE,MAAM,iBAwGjB;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;AAGD,wBAAgB,wBAAwB,CACtC,WAAW,EAAE,oBAAoB,EACjC,MAAM,EAAE,CAAC,IAAI,EAAE,cAAc,KAAK,IAAI,QAavC;AAED;;;;;GAKG;AACH,wBAAgB,iCAAiC,CAC/C,mBAAmB,EAAE,MAAM,EAC3B,QAAQ,EAAE,MAAM,EAChB,cAAc,GAAE,OAAe,GAC9B,GAAG,CAAC,MAAM,EAAE;IAAE,WAAW,EAAE,2BAA2B,EAAE,CAAA;CAAE,EAAE,CAAC,CA4C/D;AAED;;;;GAIG;AACH,wBAAsB,oBAAoB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAO7E;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,gCAAgC,CACpD,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,6CAoClB"}
|
package/dist/reporter/util.js
CHANGED
|
@@ -166,7 +166,12 @@ async function updateHtmlZipFileAttachmentPaths(jsonFilePath, htmlFilePath, repo
|
|
|
166
166
|
console.error(`❌ Failed to read HTML file at ${htmlFilePath}:`, err);
|
|
167
167
|
return;
|
|
168
168
|
}
|
|
169
|
-
|
|
169
|
+
// Support both old format (1.53.x) and new format (1.57.0+)
|
|
170
|
+
// Old: window.playwrightReportBase64 = "data:application/zip;base64,..."
|
|
171
|
+
// New: <script id="playwrightReportBase64" type="application/zip">data:application/zip;base64,...</script>
|
|
172
|
+
const oldFormatMatch = htmlFile.match(/window\.playwrightReportBase64\s*=\s*"(?:data:application\/zip;base64,)?([^"]+)"/);
|
|
173
|
+
const newFormatMatch = htmlFile.match(/<script\s+id="playwrightReportBase64"[^>]*>(?:data:application\/zip;base64,)?([^<]+)<\/script>/);
|
|
174
|
+
const base64 = oldFormatMatch?.[1] || newFormatMatch?.[1];
|
|
170
175
|
if (!base64) {
|
|
171
176
|
console.error("❌ Base64 zip data not found in HTML.");
|
|
172
177
|
return;
|
|
@@ -203,7 +208,15 @@ async function updateHtmlZipFileAttachmentPaths(jsonFilePath, htmlFilePath, repo
|
|
|
203
208
|
const newZip = new adm_zip_1.default();
|
|
204
209
|
newZip.addLocalFolder(tempDir);
|
|
205
210
|
const newBase64 = newZip.toBuffer().toString("base64");
|
|
206
|
-
|
|
211
|
+
let updatedHtml;
|
|
212
|
+
if (oldFormatMatch) {
|
|
213
|
+
// Old format (1.53.x): window.playwrightReportBase64 = "..."
|
|
214
|
+
updatedHtml = htmlFile.replace(/(window\.playwrightReportBase64\s*=\s*")(?:data:application\/zip;base64,)?[^"]*(")/, `$1data:application/zip;base64,${newBase64}$2`);
|
|
215
|
+
}
|
|
216
|
+
else {
|
|
217
|
+
// New format (1.57.0+): <script id="playwrightReportBase64" ...>...</script>
|
|
218
|
+
updatedHtml = htmlFile.replace(/(<script\s+id="playwrightReportBase64"[^>]*>)(?:data:application\/zip;base64,)?[^<]*(<\/script>)/, `$1data:application/zip;base64,${newBase64}$2`);
|
|
219
|
+
}
|
|
207
220
|
await fs_1.default.promises.writeFile(htmlFilePath, updatedHtml, "utf8");
|
|
208
221
|
logger_1.logger.debug("✅ HTML file updated with new base64 zip attachment.");
|
|
209
222
|
}
|
package/dist/test/index.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { BrowserContext, BrowserContextOptions, expect, Page, PlaywrightTestArgs, PlaywrightTestOptions, PlaywrightWorkerArgs, PlaywrightWorkerOptions, TestType } from "@playwright/test";
|
|
2
2
|
import { KVClient } from "../kv";
|
|
3
|
+
import { PostgresClient } from "../postgres";
|
|
3
4
|
import { injectLocatorHighlightScripts } from "./scripts";
|
|
4
5
|
import { HighlighterOpts } from "./types";
|
|
5
6
|
import { setVideoLabel } from "./video-labels";
|
|
@@ -14,6 +15,7 @@ declare const extendExpect: (expectInstance: typeof expect) => import("@playwrig
|
|
|
14
15
|
type TestOptions = {
|
|
15
16
|
page: Page;
|
|
16
17
|
kv: KVClient;
|
|
18
|
+
postgres: PostgresClient;
|
|
17
19
|
customContextPageProvider: (options?: BrowserContextOptions) => Promise<{
|
|
18
20
|
context: BrowserContext;
|
|
19
21
|
page: Page;
|
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;
|
|
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;AAE7C,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;SAC9C;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
|
@@ -6,6 +6,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
6
|
exports.setVideoLabel = exports.injectLocatorHighlightScripts = exports.extendExpect = exports.baseTestFixture = void 0;
|
|
7
7
|
const path_1 = __importDefault(require("path"));
|
|
8
8
|
const kv_1 = require("../kv");
|
|
9
|
+
const postgres_1 = require("../postgres");
|
|
9
10
|
const expect_1 = require("./expect");
|
|
10
11
|
const scripts_1 = require("./scripts");
|
|
11
12
|
Object.defineProperty(exports, "injectLocatorHighlightScripts", { enumerable: true, get: function () { return scripts_1.injectLocatorHighlightScripts; } });
|
|
@@ -53,6 +54,10 @@ const baseTestFixture = function (testFn, options = defaultOptions) {
|
|
|
53
54
|
const client = new kv_1.KVClient();
|
|
54
55
|
await use(client);
|
|
55
56
|
},
|
|
57
|
+
postgres: async ({}, use) => {
|
|
58
|
+
const client = new postgres_1.PostgresClient();
|
|
59
|
+
await use(client);
|
|
60
|
+
},
|
|
56
61
|
customContextPageProvider: async ({ browser }, use, testInfo) => {
|
|
57
62
|
const contexts = [];
|
|
58
63
|
async function createContext(contextOptions = {}) {
|
package/docs/postgres.md
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# Postgres
|
|
2
|
+
|
|
3
|
+
A PostgreSQL client for creating and managing temporary Neon databases in tests.
|
|
4
|
+
|
|
5
|
+
## Fixture usage
|
|
6
|
+
|
|
7
|
+
```ts
|
|
8
|
+
test("example", async ({ page, postgres }) => {
|
|
9
|
+
// Get or create a database (cached via KV for 1 hour by default)
|
|
10
|
+
const { connectionUri } = await postgres.get("my-test-db");
|
|
11
|
+
|
|
12
|
+
// Run queries
|
|
13
|
+
const users = await postgres.query<{ id: number; name: string }>(
|
|
14
|
+
connectionUri,
|
|
15
|
+
"SELECT * FROM users",
|
|
16
|
+
);
|
|
17
|
+
|
|
18
|
+
// Execute multiple SQL statements
|
|
19
|
+
await postgres.execute(
|
|
20
|
+
connectionUri,
|
|
21
|
+
`
|
|
22
|
+
CREATE TABLE users (id SERIAL PRIMARY KEY, name TEXT);
|
|
23
|
+
INSERT INTO users (name) VALUES ('Alice');
|
|
24
|
+
`,
|
|
25
|
+
);
|
|
26
|
+
|
|
27
|
+
// Delete when done (optional - TTL will auto-expire)
|
|
28
|
+
await postgres.delete("my-test-db");
|
|
29
|
+
});
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Standalone usage
|
|
33
|
+
|
|
34
|
+
```ts
|
|
35
|
+
import { PostgresClient } from "@empiricalrun/playwright-utils";
|
|
36
|
+
|
|
37
|
+
const postgres = new PostgresClient();
|
|
38
|
+
|
|
39
|
+
// Get or create a database
|
|
40
|
+
const { projectId, connectionUri } = await postgres.get("my-db", {
|
|
41
|
+
pgVersion: 15,
|
|
42
|
+
ttl: 7200, // 2 hours
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
// Run a query
|
|
46
|
+
const rows = await postgres.query(connectionUri, "SELECT NOW()");
|
|
47
|
+
|
|
48
|
+
// Delete when done
|
|
49
|
+
await postgres.delete("my-db");
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## API
|
|
53
|
+
|
|
54
|
+
### `postgres.get(name, options?)`
|
|
55
|
+
|
|
56
|
+
Gets an existing database or creates a new one. Uses KV store to cache the connection.
|
|
57
|
+
|
|
58
|
+
- `name` - Unique identifier for the database
|
|
59
|
+
- `options.pgVersion` - PostgreSQL version (default: 15)
|
|
60
|
+
- `options.ttl` - Cache TTL in seconds (default: 3600)
|
|
61
|
+
|
|
62
|
+
Returns `{ projectId, connectionUri }`.
|
|
63
|
+
|
|
64
|
+
### `postgres.delete(name)`
|
|
65
|
+
|
|
66
|
+
Deletes the database and removes it from cache.
|
|
67
|
+
|
|
68
|
+
### `postgres.query(connectionUri, sql)`
|
|
69
|
+
|
|
70
|
+
Runs a SQL query and returns rows.
|
|
71
|
+
|
|
72
|
+
### `postgres.execute(connectionUri, sql)`
|
|
73
|
+
|
|
74
|
+
Executes multiple SQL statements separated by `;`.
|
|
75
|
+
|
|
76
|
+
## Requirements
|
|
77
|
+
|
|
78
|
+
- `EMPIRICALRUN_API_KEY` environment variable must be set
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@empiricalrun/playwright-utils",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.34.0",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"registry": "https://registry.npmjs.org/",
|
|
6
6
|
"access": "public"
|
|
@@ -30,21 +30,23 @@
|
|
|
30
30
|
"@types/node": "^20.14.9",
|
|
31
31
|
"@types/serve-handler": "^6.1.4",
|
|
32
32
|
"@types/adm-zip": "^0.5.7",
|
|
33
|
+
"@types/pg": "^8.11.6",
|
|
33
34
|
"playwright-core": "1.53.2",
|
|
34
35
|
"serve-handler": "^6.1.6",
|
|
35
36
|
"@empiricalrun/shared-types": "0.12.0"
|
|
36
37
|
},
|
|
37
38
|
"dependencies": {
|
|
38
39
|
"@babel/code-frame": "^7.24.7",
|
|
40
|
+
"pg": "^8.13.1",
|
|
39
41
|
"adm-zip": "^0.5.16",
|
|
40
42
|
"async-retry": "^1.3.3",
|
|
41
43
|
"authenticator": "^1.1.5",
|
|
42
44
|
"console-log-level": "^1.4.1",
|
|
43
45
|
"puppeteer-extra-plugin-recaptcha": "^3.6.8",
|
|
44
46
|
"rimraf": "^6.0.1",
|
|
47
|
+
"@empiricalrun/r2-uploader": "^0.5.0",
|
|
45
48
|
"@empiricalrun/llm": "^0.25.1",
|
|
46
|
-
"@empiricalrun/
|
|
47
|
-
"@empiricalrun/test-gen": "^0.78.2"
|
|
49
|
+
"@empiricalrun/test-gen": "^0.78.3"
|
|
48
50
|
},
|
|
49
51
|
"scripts": {
|
|
50
52
|
"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/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/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/empirical-reporter.ts","./src/reporter/uploader.ts","./src/reporter/util.ts","./src/test/constants.ts","./src/test/expect.ts","./src/test/index.ts","./src/test/types.ts","./src/test/video-labels.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/email.ts","./src/index.ts","./src/kv.ts","./src/logger.ts","./src/mailosaur-client.ts","./src/playwright-extensions.ts","./src/postgres.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/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/empirical-reporter.ts","./src/reporter/uploader.ts","./src/reporter/util.ts","./src/test/constants.ts","./src/test/expect.ts","./src/test/index.ts","./src/test/types.ts","./src/test/video-labels.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"}
|