@empiricalrun/test-gen 0.66.1 → 0.67.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 +17 -0
- package/dist/agent/browsing/run.js +4 -4
- package/dist/agent/browsing/utils.d.ts +1 -0
- package/dist/agent/browsing/utils.d.ts.map +1 -1
- package/dist/agent/browsing/utils.js +5 -4
- package/dist/agent/chat/index.d.ts +1 -0
- package/dist/agent/chat/index.d.ts.map +1 -1
- package/dist/agent/chat/index.js +1 -0
- package/dist/agent/chat/prompt/index.d.ts.map +1 -1
- package/dist/agent/chat/prompt/index.js +3 -1
- package/dist/agent/cua/pw-codegen/pw-pause/for-recorder.d.ts +14 -0
- package/dist/agent/cua/pw-codegen/pw-pause/for-recorder.d.ts.map +1 -0
- package/dist/agent/cua/pw-codegen/pw-pause/for-recorder.js +62 -0
- package/dist/agent/cua/pw-codegen/pw-pause/index.d.ts.map +1 -1
- package/dist/agent/cua/pw-codegen/pw-pause/index.js +19 -15
- package/dist/agent/cua/pw-codegen/pw-pause/types.d.ts +14 -0
- package/dist/agent/cua/pw-codegen/pw-pause/types.d.ts.map +1 -0
- package/dist/agent/cua/pw-codegen/pw-pause/types.js +2 -0
- package/dist/artifacts/utils.d.ts +1 -1
- package/dist/artifacts/utils.d.ts.map +1 -1
- package/dist/artifacts/utils.js +5 -5
- package/dist/auth/api-client.d.ts +12 -0
- package/dist/auth/api-client.d.ts.map +1 -0
- package/dist/auth/api-client.js +133 -0
- package/dist/auth/cli-auth.d.ts +18 -0
- package/dist/auth/cli-auth.d.ts.map +1 -0
- package/dist/auth/cli-auth.js +184 -0
- package/dist/auth/index.d.ts +4 -0
- package/dist/auth/index.d.ts.map +1 -0
- package/dist/auth/index.js +17 -0
- package/dist/auth/token-store.d.ts +15 -0
- package/dist/auth/token-store.d.ts.map +1 -0
- package/dist/auth/token-store.js +149 -0
- package/dist/bin/index.js +98 -1
- package/dist/bin/utils/index.d.ts +2 -1
- package/dist/bin/utils/index.d.ts.map +1 -1
- package/dist/bin/utils/index.js +34 -5
- package/dist/browser-injected-scripts/annotate-elements.js +1 -4
- package/dist/file/client.d.ts +1 -0
- package/dist/file/client.d.ts.map +1 -1
- package/dist/file/client.js +3 -0
- package/dist/file/server.d.ts +2 -0
- package/dist/file/server.d.ts.map +1 -1
- package/dist/file/server.js +9 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +16 -0
- package/dist/recorder/display.d.ts +2 -0
- package/dist/recorder/display.d.ts.map +1 -0
- package/dist/recorder/display.js +50 -0
- package/dist/recorder/index.d.ts +4 -0
- package/dist/recorder/index.d.ts.map +1 -0
- package/dist/recorder/index.js +108 -0
- package/dist/recorder/request.d.ts +6 -0
- package/dist/recorder/request.d.ts.map +1 -0
- package/dist/recorder/request.js +55 -0
- package/dist/recorder/temp-files.d.ts +3 -0
- package/dist/recorder/temp-files.d.ts.map +1 -0
- package/dist/recorder/temp-files.js +39 -0
- package/dist/recorder/upload.d.ts +2 -0
- package/dist/recorder/upload.d.ts.map +1 -0
- package/dist/recorder/upload.js +85 -0
- package/dist/recorder/validation.d.ts +2 -0
- package/dist/recorder/validation.d.ts.map +1 -0
- package/dist/recorder/validation.js +24 -0
- package/package.json +2 -1
- package/tsconfig.tsbuildinfo +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,22 @@
|
|
|
1
1
|
# @empiricalrun/test-gen
|
|
2
2
|
|
|
3
|
+
## 0.67.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 267b012: feat: add user login in test-gen cli
|
|
8
|
+
|
|
9
|
+
## 0.66.2
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- 45dfd06: feat: show version comparison in banner
|
|
14
|
+
- 7c66a47: feat: first working version of --use-recorder
|
|
15
|
+
- 5bc45e1: feat: create requests from recorder cli
|
|
16
|
+
- d0569de: fix: ensure browser agent is not stuck on page.pause
|
|
17
|
+
- fd719f1: feat: upload recorder video and attach to request
|
|
18
|
+
- a65a4d2: fix: path and ui issues
|
|
19
|
+
|
|
3
20
|
## 0.66.1
|
|
4
21
|
|
|
5
22
|
### Patch Changes
|
|
@@ -83,10 +83,10 @@ async function runBrowsingAgent({ testCaseName, testCaseSuites, testFilePath, fi
|
|
|
83
83
|
if (error) {
|
|
84
84
|
// Clean up the file if there is any error
|
|
85
85
|
try {
|
|
86
|
-
const fileContent = fs_1.default.readFileSync(
|
|
87
|
-
const updatedContent = (0, web_1.replaceCreateTestWithNewCode)(
|
|
88
|
-
fs_1.default.writeFileSync(
|
|
89
|
-
await (0, web_1.lintErrors)(
|
|
86
|
+
const fileContent = fs_1.default.readFileSync(absFilePathToUpdate, "utf-8");
|
|
87
|
+
const updatedContent = (0, web_1.replaceCreateTestWithNewCode)(absFilePathToUpdate, fileContent, "");
|
|
88
|
+
fs_1.default.writeFileSync(absFilePathToUpdate, updatedContent, "utf-8");
|
|
89
|
+
await (0, web_1.lintErrors)(absFilePathToUpdate);
|
|
90
90
|
}
|
|
91
91
|
catch (e) {
|
|
92
92
|
console.error(`[generateTestsUsingMasterAgent] Failed to remove extra scripts from files post test gen error:`, e);
|
|
@@ -4,6 +4,7 @@ import { Page } from "playwright";
|
|
|
4
4
|
import { PlaywrightTestConfig } from "playwright/test";
|
|
5
5
|
export declare function isRegExp(obj: any): obj is RegExp;
|
|
6
6
|
export declare function prepareBrowsingAgentTask(steps: string[]): string;
|
|
7
|
+
export declare function addImportForMethod(testFilePath: string, methodName: string): Promise<void>;
|
|
7
8
|
export declare function replaceTodoWithCreateTest(testFilePath: string, repoDir: string): Promise<void>;
|
|
8
9
|
export declare function markTestAsOnly({ testCaseName, testCaseSuites, specPath, }: {
|
|
9
10
|
testCaseName: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../../src/agent/browsing/utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,4BAA4B,CAAC;AAI3D,OAAO,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAClC,OAAO,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAC;AAsBvD,wBAAgB,QAAQ,CAAC,GAAG,EAAE,GAAG,GAAG,GAAG,IAAI,MAAM,CAKhD;AAED,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,MAAM,EAAE,UAIvD;
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../../src/agent/browsing/utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,4BAA4B,CAAC;AAI3D,OAAO,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAClC,OAAO,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAC;AAsBvD,wBAAgB,QAAQ,CAAC,GAAG,EAAE,GAAG,GAAG,GAAG,IAAI,MAAM,CAKhD;AAED,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,MAAM,EAAE,UAIvD;AAED,wBAAsB,kBAAkB,CACtC,YAAY,EAAE,MAAM,EACpB,UAAU,EAAE,MAAM,iBAgBnB;AAgED,wBAAsB,yBAAyB,CAC7C,YAAY,EAAE,MAAM,EACpB,OAAO,EAAE,MAAM,iBAwBhB;AAED,wBAAsB,cAAc,CAAC,EACnC,YAAY,EACZ,cAAc,EACd,QAAQ,GACT,EAAE;IACD,YAAY,EAAE,MAAM,CAAC;IACrB,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,QAAQ,EAAE,MAAM,CAAC;CAClB,iBAoBA;AAED,wBAAsB,yBAAyB,CAAC,EAC9C,QAAQ,EACR,QAAQ,EACR,KAAK,GACN,EAAE;IACD,QAAQ,EAAE,QAAQ,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,WAAW,CAAC;CACrB,GAAG,OAAO,CAAC,MAAM,CAAC,CAyDlB;AAyBD,wBAAsB,wBAAwB,CAAC,IAAI,EAAE,IAAI,iBA8HxD;AAED;;;GAGG;AACH,wBAAsB,oBAAoB,CACxC,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,oBAAoB,CAAC,CA2B/B;AAWD,wBAAsB,oBAAoB,CACxC,gBAAgB,EAAE,oBAAoB,GACrC,OAAO,CAAC,MAAM,EAAE,CAAC,CAQnB;AAED;;;;;GAKG;AACH,wBAAsB,iBAAiB,CACrC,YAAY,EAAE,MAAM,EACpB,gBAAgB,EAAE,oBAAoB,EACtC,gBAAgB,GAAE,MAAM,EAAU,GACjC,OAAO,CAAC,MAAM,CAAC,CA+CjB"}
|
|
@@ -5,6 +5,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.isRegExp = isRegExp;
|
|
7
7
|
exports.prepareBrowsingAgentTask = prepareBrowsingAgentTask;
|
|
8
|
+
exports.addImportForMethod = addImportForMethod;
|
|
8
9
|
exports.replaceTodoWithCreateTest = replaceTodoWithCreateTest;
|
|
9
10
|
exports.markTestAsOnly = markTestAsOnly;
|
|
10
11
|
exports.prepareFileForMasterAgent = prepareFileForMasterAgent;
|
|
@@ -33,14 +34,14 @@ function prepareBrowsingAgentTask(steps) {
|
|
|
33
34
|
const task = `${sanitizedSteps.join("\n")}\n`;
|
|
34
35
|
return task;
|
|
35
36
|
}
|
|
36
|
-
async function
|
|
37
|
+
async function addImportForMethod(testFilePath, methodName) {
|
|
37
38
|
// Instead of using "@empiricalrun/test-gen", we use the local dist file
|
|
38
39
|
// This is to avoid assuming that the test-gen package is installed in the project
|
|
39
40
|
const importSource = path_1.default.join(__dirname, "../../../dist/index.js");
|
|
40
41
|
if (!fs_1.default.existsSync(importSource)) {
|
|
41
42
|
throw new Error(`createTest import source not found at ${importSource}`);
|
|
42
43
|
}
|
|
43
|
-
fs_1.default.writeFileSync(testFilePath, (0, web_1.addNewImport)(fs_1.default.readFileSync(testFilePath, "utf-8"), [
|
|
44
|
+
fs_1.default.writeFileSync(testFilePath, (0, web_1.addNewImport)(fs_1.default.readFileSync(testFilePath, "utf-8"), [methodName], importSource));
|
|
44
45
|
}
|
|
45
46
|
async function prepareFileForUpdateScenario({ testCase, specPath, trace, }) {
|
|
46
47
|
const { name, suites } = testCase;
|
|
@@ -77,7 +78,7 @@ async function prepareFileForUpdateScenario({ testCase, specPath, trace, }) {
|
|
|
77
78
|
},
|
|
78
79
|
});
|
|
79
80
|
await (0, web_1.appendScopeToCreateTest)(createTestFilePath, scopeVariables);
|
|
80
|
-
await
|
|
81
|
+
await addImportForMethod(createTestFilePath, "createTest");
|
|
81
82
|
const { pomPrompt, nonSpecFilePrompt } = await (0, context_1.contextForGeneration)(createTestFilePath);
|
|
82
83
|
await (0, fix_ts_errors_1.validateAndFixTypescriptErrors)({
|
|
83
84
|
trace,
|
|
@@ -106,7 +107,7 @@ async function replaceTodoWithCreateTest(testFilePath, repoDir) {
|
|
|
106
107
|
const [, pageVarName] = todoMatch;
|
|
107
108
|
const pageVariable = pageVarName || "page"; // Default to "page" if not specified
|
|
108
109
|
fs_1.default.writeFileSync(absoluteTestFilePath, fileContent.replace(todoRegex, (_, __, todoText) => `await createTest("${todoText.replace(/"/g, '\\"')}", ${pageVariable});`));
|
|
109
|
-
await
|
|
110
|
+
await addImportForMethod(absoluteTestFilePath, "createTest");
|
|
110
111
|
}
|
|
111
112
|
async function markTestAsOnly({ testCaseName, testCaseSuites, specPath, }) {
|
|
112
113
|
const testFileContent = fs_1.default.readFileSync(specPath, "utf-8");
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { SupportedChatModels } from "@empiricalrun/shared-types";
|
|
2
|
+
export declare function fetchEnvironmentVariables(): Promise<Record<string, string>>;
|
|
2
3
|
export declare function runChatAgentForCLI({ useDiskForChatState, selectedModel, initialPromptContent, }: {
|
|
3
4
|
selectedModel: SupportedChatModels;
|
|
4
5
|
useDiskForChatState: boolean;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/agent/chat/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAGL,mBAAmB,EACpB,MAAM,4BAA4B,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/agent/chat/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAGL,mBAAmB,EACpB,MAAM,4BAA4B,CAAC;AAwCpC,wBAAsB,yBAAyB,IAAI,OAAO,CACxD,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CACvB,CAwBA;AAED,wBAAsB,kBAAkB,CAAC,EACvC,mBAAmB,EACnB,aAAa,EACb,oBAAoB,GACrB,EAAE;IACD,aAAa,EAAE,mBAAmB,CAAC;IACnC,mBAAmB,EAAE,OAAO,CAAC;IAC7B,oBAAoB,EAAE,MAAM,GAAG,SAAS,CAAC;CAC1C,iBA6HA;AAuBD,wBAAsB,wBAAwB,CAAC,EAC7C,aAAa,EACb,aAAa,GACd,EAAE;IACD,aAAa,EAAE,mBAAmB,CAAC;IACnC,aAAa,EAAE,MAAM,CAAC;CACvB,iBA6DA"}
|
package/dist/agent/chat/index.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.fetchEnvironmentVariables = fetchEnvironmentVariables;
|
|
3
4
|
exports.runChatAgentForCLI = runChatAgentForCLI;
|
|
4
5
|
exports.runChatAgentForDashboard = runChatAgentForDashboard;
|
|
5
6
|
const llm_1 = require("@empiricalrun/llm");
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/agent/chat/prompt/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAI1C,wBAAsB,iBAAiB,CAAC,QAAQ,EAAE,QAAQ,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/agent/chat/prompt/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAI1C,wBAAsB,iBAAiB,CAAC,QAAQ,EAAE,QAAQ,mBA4GzD"}
|
|
@@ -67,7 +67,9 @@ ONLY when explicitly asked for by the user.
|
|
|
67
67
|
|
|
68
68
|
1. You can't delete some steps from the test to make it pass. The test needs to accomplish its objective (which is to validate a particular user scenario)
|
|
69
69
|
2. Do not add any conditional logic or try catch blocks in a test. A good test deterministically tests a user scenario
|
|
70
|
-
3. Trust Playwright's ability to auto-wait while taking actions on elements.
|
|
70
|
+
3. Trust Playwright's ability to auto-wait while taking actions on elements.
|
|
71
|
+
- Example 1: Do not add checks on locator.isVisible() before clicking on it: Playwright already waits for visibility on locator.click()
|
|
72
|
+
- Example 2: Do not add page.waitForLoadState after a page.goto: Playwright already waits for page "load" event in page.goto()
|
|
71
73
|
4. Do not add waitForTimeout or waitForLoadState in a test. Playwright will automatically wait for the page to load.
|
|
72
74
|
5. Try/catch blocks are a code smell for tests: you should not use them.
|
|
73
75
|
6. Do not use then() or catch() syntax in a test. Use async/await only
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { Page } from "playwright";
|
|
2
|
+
import { SourcesPayload } from "./types";
|
|
3
|
+
export declare class PlaywrightPauseCodegenForRecorder {
|
|
4
|
+
private sourcesCallback;
|
|
5
|
+
private port;
|
|
6
|
+
private page;
|
|
7
|
+
private server;
|
|
8
|
+
private codeForLastAction;
|
|
9
|
+
constructor(sourcesCallback: (code: SourcesPayload[]) => Promise<void>);
|
|
10
|
+
private saveCode;
|
|
11
|
+
initialize(page: Page): Promise<void>;
|
|
12
|
+
startPlaywrightCodegen(page: Page): Promise<void>;
|
|
13
|
+
}
|
|
14
|
+
//# sourceMappingURL=for-recorder.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"for-recorder.d.ts","sourceRoot":"","sources":["../../../../../src/agent/cua/pw-codegen/pw-pause/for-recorder.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAGvC,OAAO,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAEzC,qBAAa,iCAAiC;IAO1C,OAAO,CAAC,eAAe;IANzB,OAAO,CAAC,IAAI,CAAa;IACzB,OAAO,CAAC,IAAI,CAAmB;IAC/B,OAAO,CAAC,MAAM,CAA4C;IAC1D,OAAO,CAAC,iBAAiB,CAAqB;gBAGpC,eAAe,EAAE,CAAC,IAAI,EAAE,cAAc,EAAE,KAAK,OAAO,CAAC,IAAI,CAAC;YAKtD,QAAQ;IAWhB,UAAU,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;IAkBrC,sBAAsB,CAAC,IAAI,EAAE,IAAI;CAiBxC"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.PlaywrightPauseCodegenForRecorder = void 0;
|
|
7
|
+
const express_1 = __importDefault(require("express"));
|
|
8
|
+
const ipc_1 = require("./ipc");
|
|
9
|
+
class PlaywrightPauseCodegenForRecorder {
|
|
10
|
+
sourcesCallback;
|
|
11
|
+
port = 0;
|
|
12
|
+
page;
|
|
13
|
+
server;
|
|
14
|
+
codeForLastAction;
|
|
15
|
+
constructor(sourcesCallback) {
|
|
16
|
+
this.sourcesCallback = sourcesCallback;
|
|
17
|
+
this.port = ipc_1.PW_PAUSE_IPC_PORT;
|
|
18
|
+
}
|
|
19
|
+
async saveCode(code) {
|
|
20
|
+
const generatedCode = code.map((c) => c.actions.join("\n")).join("\n");
|
|
21
|
+
if (generatedCode) {
|
|
22
|
+
console.log(`[PlaywrightPauseCodegen] Received code from Playwright: ${generatedCode}`);
|
|
23
|
+
this.codeForLastAction = generatedCode;
|
|
24
|
+
await this.sourcesCallback(code);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
async initialize(page) {
|
|
28
|
+
// Start server to receive generated code from patch
|
|
29
|
+
const app = (0, express_1.default)();
|
|
30
|
+
app.use(express_1.default.json());
|
|
31
|
+
app.post("/sources", async (req, res) => {
|
|
32
|
+
const { payload } = req.body;
|
|
33
|
+
await this.saveCode(JSON.parse(payload));
|
|
34
|
+
return res.send({ success: true });
|
|
35
|
+
});
|
|
36
|
+
await new Promise((resolve) => {
|
|
37
|
+
this.server = app.listen(this.port, () => resolve());
|
|
38
|
+
});
|
|
39
|
+
console.log(`Server started on port ${this.port}`);
|
|
40
|
+
// Start page.pause experience in the page
|
|
41
|
+
this.page = page;
|
|
42
|
+
await this.startPlaywrightCodegen(page);
|
|
43
|
+
}
|
|
44
|
+
async startPlaywrightCodegen(page) {
|
|
45
|
+
// Similar to the same name method in the main codegen
|
|
46
|
+
const timerPromise = new Promise((resolve) => {
|
|
47
|
+
setTimeout(resolve, 1000);
|
|
48
|
+
});
|
|
49
|
+
const pausePromise = page.pause();
|
|
50
|
+
await timerPromise;
|
|
51
|
+
const evaluatePromise = page.evaluate(() => {
|
|
52
|
+
// @ts-ignore
|
|
53
|
+
console.log(window["__pw_recorderSetMode"]("recording"));
|
|
54
|
+
const glassPane = document.querySelector("x-pw-glass");
|
|
55
|
+
if (glassPane) {
|
|
56
|
+
glassPane.remove();
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
await Promise.all([pausePromise, evaluatePromise]);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
exports.PlaywrightPauseCodegenForRecorder = PlaywrightPauseCodegenForRecorder;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/agent/cua/pw-codegen/pw-pause/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAEvC,OAAO,EAAE,qBAAqB,EAAE,MAAM,UAAU,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/agent/cua/pw-codegen/pw-pause/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAEvC,OAAO,EAAE,qBAAqB,EAAE,MAAM,UAAU,CAAC;AAKjD,OAAO,EAAE,2BAA2B,EAAE,sBAAsB,EAAE,MAAM,SAAS,CAAC;AAE9E,wBAAsB,kBAAkB,CAAC,OAAO,EAAE,MAAM,oBAuCvD;AAED,qBAAa,sBAAuB,YAAW,qBAAqB;IAClE,OAAO,CAAC,IAAI,CAAa;IACzB,OAAO,CAAC,IAAI,CAAmB;IAC/B,OAAO,CAAC,MAAM,CAA4C;IAC1D,OAAO,CAAC,iBAAiB,CAAqB;;YAMhC,QAAQ;IAUhB,UAAU,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;IAkBrC,sBAAsB,CAAC,IAAI,EAAE,IAAI;IAyBjC,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC;IAI7B,oBAAoB,IAAI,OAAO,CAAC,MAAM,CAAC;CAU9C"}
|
|
@@ -54,6 +54,7 @@ class PlaywrightPauseCodegen {
|
|
|
54
54
|
async saveCode(code) {
|
|
55
55
|
const generatedCode = code.map((c) => c.actions.join("\n")).join("\n");
|
|
56
56
|
if (generatedCode) {
|
|
57
|
+
console.log(`[PlaywrightPauseCodegen] Received code from Playwright: ${generatedCode}`);
|
|
57
58
|
this.codeForLastAction = generatedCode;
|
|
58
59
|
}
|
|
59
60
|
}
|
|
@@ -78,22 +79,25 @@ class PlaywrightPauseCodegen {
|
|
|
78
79
|
// TODO: Glass pane needs to be removed on every page load
|
|
79
80
|
// We use bindings that Playwright exposes to the page
|
|
80
81
|
// Ref: https://github.com/microsoft/playwright/blob/e1c8e0f6b33923c95cc4b9416aefa6977b1d3c55/packages/playwright-core/src/server/recorder.ts#L191
|
|
81
|
-
|
|
82
|
-
setTimeout(
|
|
83
|
-
// First, we start recording
|
|
84
|
-
// @ts-ignore
|
|
85
|
-
console.log(window["__pw_recorderSetMode"]("recording"));
|
|
86
|
-
// Then, we will resume the effect of pause()
|
|
87
|
-
// @ts-ignore
|
|
88
|
-
console.log(window["__pw_resume"]());
|
|
89
|
-
// Then, we remove highlights that Playwright shows on the screen
|
|
90
|
-
const glassPane = document.querySelector("x-pw-glass");
|
|
91
|
-
if (glassPane) {
|
|
92
|
-
glassPane.remove();
|
|
93
|
-
}
|
|
94
|
-
}, 1000);
|
|
82
|
+
const timerPromise = new Promise((resolve) => {
|
|
83
|
+
setTimeout(resolve, 1000);
|
|
95
84
|
});
|
|
96
|
-
|
|
85
|
+
const pausePromise = page.pause();
|
|
86
|
+
await timerPromise;
|
|
87
|
+
const evaluatePromise = page.evaluate(() => {
|
|
88
|
+
// First, we start recording
|
|
89
|
+
// @ts-ignore
|
|
90
|
+
console.log(window["__pw_recorderSetMode"]("recording"));
|
|
91
|
+
// Then, we will resume the effect of pause()
|
|
92
|
+
// @ts-ignore
|
|
93
|
+
console.log(window["__pw_resume"]());
|
|
94
|
+
// Then, we remove highlights that Playwright shows on the screen
|
|
95
|
+
const glassPane = document.querySelector("x-pw-glass");
|
|
96
|
+
if (glassPane) {
|
|
97
|
+
glassPane.remove();
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
await Promise.all([pausePromise, evaluatePromise]);
|
|
97
101
|
}
|
|
98
102
|
async recordAction() {
|
|
99
103
|
// Record action is no-op
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export type SourcesPayload = {
|
|
2
|
+
isRecorded: boolean;
|
|
3
|
+
label: "library";
|
|
4
|
+
group: "Node.js";
|
|
5
|
+
id: "javascript";
|
|
6
|
+
text: string;
|
|
7
|
+
header: string;
|
|
8
|
+
footer: string;
|
|
9
|
+
actions: string[];
|
|
10
|
+
language: "javascript";
|
|
11
|
+
revealLine: number;
|
|
12
|
+
highlight: string[];
|
|
13
|
+
};
|
|
14
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../../src/agent/cua/pw-codegen/pw-pause/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,cAAc,GAAG;IAC3B,UAAU,EAAE,OAAO,CAAC;IACpB,KAAK,EAAE,SAAS,CAAC;IACjB,KAAK,EAAE,SAAS,CAAC;IACjB,EAAE,EAAE,YAAY,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,QAAQ,EAAE,YAAY,CAAC;IAEvB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,EAAE,CAAC;CACrB,CAAC"}
|
|
@@ -5,7 +5,7 @@ import { JSONReport as PlaywrightJSONReport } from "@playwright/test/reporter";
|
|
|
5
5
|
* @param report The Playwright JSON report to extract attachments from
|
|
6
6
|
* @returns An array of objects containing path and contentType for each attachment
|
|
7
7
|
*/
|
|
8
|
-
export declare function extractAttachmentsFromPlaywrightJSONReport(report: PlaywrightJSONReport): ArtifactInputPath[];
|
|
8
|
+
export declare function extractAttachmentsFromPlaywrightJSONReport(report: PlaywrightJSONReport, testNameFilter?: string): ArtifactInputPath[];
|
|
9
9
|
/**
|
|
10
10
|
* Scans the given repository directory for Playwright artifacts (attachments) by:
|
|
11
11
|
* - Constructing the path to the Playwright report directory using the provided repoDir.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/artifacts/utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,iBAAiB,EAAE,MAAM,4BAA4B,CAAC;AAC9E,OAAO,EACL,UAAU,IAAI,oBAAoB,EAGnC,MAAM,2BAA2B,CAAC;AAInC;;;;GAIG;AACH,wBAAgB,0CAA0C,CACxD,MAAM,EAAE,oBAAoB,
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/artifacts/utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,iBAAiB,EAAE,MAAM,4BAA4B,CAAC;AAC9E,OAAO,EACL,UAAU,IAAI,oBAAoB,EAGnC,MAAM,2BAA2B,CAAC;AAInC;;;;GAIG;AACH,wBAAgB,0CAA0C,CACxD,MAAM,EAAE,oBAAoB,EAC5B,cAAc,CAAC,EAAE,MAAM,GACtB,iBAAiB,EAAE,CA2DrB;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,MAAM,GAAG,aAAa,EAAE,CAmCxE"}
|
package/dist/artifacts/utils.js
CHANGED
|
@@ -12,7 +12,7 @@ const path_1 = __importDefault(require("path"));
|
|
|
12
12
|
* @param report The Playwright JSON report to extract attachments from
|
|
13
13
|
* @returns An array of objects containing path and contentType for each attachment
|
|
14
14
|
*/
|
|
15
|
-
function extractAttachmentsFromPlaywrightJSONReport(report) {
|
|
15
|
+
function extractAttachmentsFromPlaywrightJSONReport(report, testNameFilter) {
|
|
16
16
|
const attachments = [];
|
|
17
17
|
if (!report || !report.suites || report.suites.length === 0) {
|
|
18
18
|
return attachments;
|
|
@@ -26,6 +26,9 @@ function extractAttachmentsFromPlaywrightJSONReport(report) {
|
|
|
26
26
|
if (!spec.tests) {
|
|
27
27
|
return;
|
|
28
28
|
}
|
|
29
|
+
if (testNameFilter && !testTitle.includes(testNameFilter)) {
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
29
32
|
for (const test of spec.tests) {
|
|
30
33
|
if (!test.results) {
|
|
31
34
|
continue;
|
|
@@ -38,7 +41,7 @@ function extractAttachmentsFromPlaywrightJSONReport(report) {
|
|
|
38
41
|
// Only collect attachments that have a 'path' (i.e., not embedded content)
|
|
39
42
|
if (attachment.path && attachment.contentType) {
|
|
40
43
|
attachments.push({
|
|
41
|
-
name: `${testTitle} ${attachment.name}`,
|
|
44
|
+
name: `${testTitle} - ${attachment.name}`,
|
|
42
45
|
path: attachment.path,
|
|
43
46
|
contentType: attachment.contentType,
|
|
44
47
|
});
|
|
@@ -56,19 +59,16 @@ function extractAttachmentsFromPlaywrightJSONReport(report) {
|
|
|
56
59
|
return;
|
|
57
60
|
}
|
|
58
61
|
for (const suite of suites) {
|
|
59
|
-
// Process specs directly within the current suite
|
|
60
62
|
if (suite.specs) {
|
|
61
63
|
for (const spec of suite.specs) {
|
|
62
64
|
processSpec(spec);
|
|
63
65
|
}
|
|
64
66
|
}
|
|
65
|
-
// Recursively call for any nested suites
|
|
66
67
|
if (suite.suites) {
|
|
67
68
|
traverseSuites(suite.suites);
|
|
68
69
|
}
|
|
69
70
|
}
|
|
70
71
|
}
|
|
71
|
-
// Start the recursive traversal from the top-level suites
|
|
72
72
|
traverseSuites(report.suites);
|
|
73
73
|
return attachments;
|
|
74
74
|
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
declare class APIClient {
|
|
2
|
+
private appUrl;
|
|
3
|
+
constructor();
|
|
4
|
+
request(endpoint: string, options?: RequestInit): Promise<Response>;
|
|
5
|
+
private makeRequest;
|
|
6
|
+
private ensureAuthenticated;
|
|
7
|
+
private refreshToken;
|
|
8
|
+
}
|
|
9
|
+
export declare const apiClient: APIClient;
|
|
10
|
+
export { APIClient };
|
|
11
|
+
export declare function authenticatedFetch(endpoint: string, options?: RequestInit): Promise<any>;
|
|
12
|
+
//# sourceMappingURL=api-client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api-client.d.ts","sourceRoot":"","sources":["../../src/auth/api-client.ts"],"names":[],"mappings":"AAOA,cAAM,SAAS;IACb,OAAO,CAAC,MAAM,CAAS;;IAMjB,OAAO,CACX,QAAQ,EAAE,MAAM,EAChB,OAAO,GAAE,WAAgB,GACxB,OAAO,CAAC,QAAQ,CAAC;YAoCN,WAAW;YAmBX,mBAAmB;YAiBnB,YAAY;CA4C3B;AAED,eAAO,MAAM,SAAS,WAAkB,CAAC;AAEzC,OAAO,EAAE,SAAS,EAAE,CAAC;AAErB,wBAAsB,kBAAkB,CACtC,QAAQ,EAAE,MAAM,EAChB,OAAO,CAAC,EAAE,WAAW,GACpB,OAAO,CAAC,GAAG,CAAC,CAqCd"}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.APIClient = exports.apiClient = void 0;
|
|
4
|
+
exports.authenticatedFetch = authenticatedFetch;
|
|
5
|
+
const token_store_1 = require("./token-store");
|
|
6
|
+
class APIClient {
|
|
7
|
+
appUrl;
|
|
8
|
+
constructor() {
|
|
9
|
+
this.appUrl = process.env.DASHBOARD_DOMAIN || "https://dash.empirical.run";
|
|
10
|
+
}
|
|
11
|
+
async request(endpoint, options = {}) {
|
|
12
|
+
// Ensure user is authenticated
|
|
13
|
+
await this.ensureAuthenticated();
|
|
14
|
+
const tokens = await (0, token_store_1.getStoredTokens)();
|
|
15
|
+
if (!tokens) {
|
|
16
|
+
throw new Error("Not authenticated. Please run the login command first.");
|
|
17
|
+
}
|
|
18
|
+
// Make the request with the access token
|
|
19
|
+
const response = await this.makeRequest(endpoint, options, tokens.access_token);
|
|
20
|
+
// If token is expired, try refreshing and retry once
|
|
21
|
+
if (response.status === 401) {
|
|
22
|
+
console.log("Access token expired, attempting to refresh...");
|
|
23
|
+
const refreshed = await this.refreshToken();
|
|
24
|
+
if (refreshed) {
|
|
25
|
+
const newTokens = await (0, token_store_1.getStoredTokens)();
|
|
26
|
+
if (newTokens) {
|
|
27
|
+
return this.makeRequest(endpoint, options, newTokens.access_token);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
throw new Error("Authentication failed. Please run the login command again.");
|
|
31
|
+
}
|
|
32
|
+
return response;
|
|
33
|
+
}
|
|
34
|
+
async makeRequest(endpoint, options, accessToken) {
|
|
35
|
+
const url = endpoint.startsWith("http")
|
|
36
|
+
? endpoint
|
|
37
|
+
: `${this.appUrl}/api${endpoint}`;
|
|
38
|
+
return fetch(url, {
|
|
39
|
+
...options,
|
|
40
|
+
headers: {
|
|
41
|
+
Authorization: `Bearer ${accessToken}`,
|
|
42
|
+
"Content-Type": "application/json",
|
|
43
|
+
...options.headers,
|
|
44
|
+
},
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
async ensureAuthenticated() {
|
|
48
|
+
if (!(await (0, token_store_1.isAuthenticated)())) {
|
|
49
|
+
const tokens = await (0, token_store_1.getStoredTokens)();
|
|
50
|
+
if (!tokens) {
|
|
51
|
+
throw new Error("Not authenticated. Please run the login command first.");
|
|
52
|
+
}
|
|
53
|
+
// Token exists but is expired, try to refresh
|
|
54
|
+
const refreshed = await this.refreshToken();
|
|
55
|
+
if (!refreshed) {
|
|
56
|
+
throw new Error("Session expired. Please run the login command again.");
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
async refreshToken() {
|
|
61
|
+
const tokens = await (0, token_store_1.getStoredTokens)();
|
|
62
|
+
if (!tokens || !tokens.refresh_token) {
|
|
63
|
+
return false;
|
|
64
|
+
}
|
|
65
|
+
try {
|
|
66
|
+
// Use dashboard refresh endpoint instead of Supabase directly
|
|
67
|
+
const response = await fetch(`${this.appUrl}/api/cli/refresh`, {
|
|
68
|
+
method: "POST",
|
|
69
|
+
headers: {
|
|
70
|
+
"Content-Type": "application/json",
|
|
71
|
+
},
|
|
72
|
+
body: JSON.stringify({
|
|
73
|
+
refresh_token: tokens.refresh_token,
|
|
74
|
+
}),
|
|
75
|
+
});
|
|
76
|
+
if (!response.ok) {
|
|
77
|
+
const errorText = await response.text();
|
|
78
|
+
console.error("Token refresh failed:", errorText);
|
|
79
|
+
await (0, token_store_1.clearTokens)();
|
|
80
|
+
return false;
|
|
81
|
+
}
|
|
82
|
+
const data = await response.json();
|
|
83
|
+
// Store the new tokens (refresh token rotation means we get a new refresh token)
|
|
84
|
+
await (0, token_store_1.storeTokens)({
|
|
85
|
+
access_token: data.access_token,
|
|
86
|
+
refresh_token: data.refresh_token,
|
|
87
|
+
expires_at: data.expires_at,
|
|
88
|
+
user_id: data.user?.id || tokens.user_id,
|
|
89
|
+
user_email: data.user?.email || tokens.user_email,
|
|
90
|
+
});
|
|
91
|
+
console.log("Access token refreshed successfully via dashboard");
|
|
92
|
+
return true;
|
|
93
|
+
}
|
|
94
|
+
catch (error) {
|
|
95
|
+
console.error("Token refresh error:", error);
|
|
96
|
+
await (0, token_store_1.clearTokens)();
|
|
97
|
+
return false;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
exports.APIClient = APIClient;
|
|
102
|
+
exports.apiClient = new APIClient();
|
|
103
|
+
async function authenticatedFetch(endpoint, options) {
|
|
104
|
+
try {
|
|
105
|
+
const response = await exports.apiClient.request(endpoint, options);
|
|
106
|
+
if (!response.ok) {
|
|
107
|
+
const errorText = await response.text();
|
|
108
|
+
let errorMessage;
|
|
109
|
+
try {
|
|
110
|
+
const errorJson = JSON.parse(errorText);
|
|
111
|
+
errorMessage =
|
|
112
|
+
errorJson.error || errorJson.message || "API request failed";
|
|
113
|
+
}
|
|
114
|
+
catch {
|
|
115
|
+
errorMessage =
|
|
116
|
+
errorText || `Request failed with status ${response.status}`;
|
|
117
|
+
}
|
|
118
|
+
throw new Error(errorMessage);
|
|
119
|
+
}
|
|
120
|
+
const contentType = response.headers.get("content-type");
|
|
121
|
+
if (contentType && contentType.includes("application/json")) {
|
|
122
|
+
return response.json();
|
|
123
|
+
}
|
|
124
|
+
return response.text();
|
|
125
|
+
}
|
|
126
|
+
catch (error) {
|
|
127
|
+
if (error.message.includes("Not authenticated") ||
|
|
128
|
+
error.message.includes("Please run the login command")) {
|
|
129
|
+
throw new Error("Authentication required. Please run: npx @empiricalrun/test-gen login");
|
|
130
|
+
}
|
|
131
|
+
throw error;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export interface AuthResult {
|
|
2
|
+
success: boolean;
|
|
3
|
+
user?: {
|
|
4
|
+
id: string;
|
|
5
|
+
email: string;
|
|
6
|
+
};
|
|
7
|
+
error?: string;
|
|
8
|
+
}
|
|
9
|
+
export declare function authenticate(): Promise<AuthResult>;
|
|
10
|
+
export declare function logout(): Promise<void>;
|
|
11
|
+
export declare function getAuthStatus(): Promise<{
|
|
12
|
+
authenticated: boolean;
|
|
13
|
+
user?: {
|
|
14
|
+
id: string;
|
|
15
|
+
email: string;
|
|
16
|
+
};
|
|
17
|
+
}>;
|
|
18
|
+
//# sourceMappingURL=cli-auth.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli-auth.d.ts","sourceRoot":"","sources":["../../src/auth/cli-auth.ts"],"names":[],"mappings":"AAoBA,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,CAAC,EAAE;QACL,EAAE,EAAE,MAAM,CAAC;QACX,KAAK,EAAE,MAAM,CAAC;KACf,CAAC;IACF,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,wBAAsB,YAAY,IAAI,OAAO,CAAC,UAAU,CAAC,CAmKxD;AAED,wBAAsB,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC,CAE5C;AAED,wBAAsB,aAAa,IAAI,OAAO,CAAC;IAC7C,aAAa,EAAE,OAAO,CAAC;IACvB,IAAI,CAAC,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;CACtC,CAAC,CAeD"}
|