@midscene/web 0.0.1 → 0.1.1
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/dist/es/index.js +342 -114
- package/dist/es/playwright-report.js +2380 -0
- package/dist/lib/index.js +343 -114
- package/dist/lib/playwright-report.js +2383 -0
- package/dist/script/htmlElement.js +596 -25
- package/dist/script/types/htmlElement.d.ts +2 -0
- package/dist/types/index.d.ts +116 -21
- package/dist/types/playwright-report.d.ts +10 -0
- package/package.json +25 -4
- package/modern.config.ts +0 -13
- package/modern.inspect.config.ts +0 -20
- package/playwright.config.ts +0 -42
- package/src/html-element/constants.ts +0 -10
- package/src/html-element/debug.ts +0 -3
- package/src/html-element/dom-util.ts +0 -11
- package/src/html-element/extractInfo.ts +0 -168
- package/src/html-element/index.ts +0 -1
- package/src/html-element/util.ts +0 -160
- package/src/img/img.ts +0 -132
- package/src/img/util.ts +0 -28
- package/src/index.ts +0 -2
- package/src/playwright/actions.ts +0 -276
- package/src/playwright/cdp.ts +0 -322
- package/src/playwright/element.ts +0 -74
- package/src/playwright/index.ts +0 -120
- package/src/playwright/utils.ts +0 -88
- package/src/puppeteer/element.ts +0 -49
- package/src/puppeteer/index.ts +0 -6
- package/src/puppeteer/utils.ts +0 -116
- package/tests/e2e/ai-auto-todo.spec.ts +0 -24
- package/tests/e2e/ai-xicha.spec.ts +0 -34
- package/tests/e2e/fixture.ts +0 -6
- package/tests/e2e/generate-test-data.spec.ts +0 -60
- package/tests/e2e/todo-app-midscene.spec.ts +0 -98
- package/tests/e2e/tool.ts +0 -63
- package/tsconfig.json +0 -23
- package/vitest.config.ts +0 -14
package/dist/lib/index.js
CHANGED
|
@@ -47,26 +47,27 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
47
47
|
// src/index.ts
|
|
48
48
|
var src_exports = {};
|
|
49
49
|
__export(src_exports, {
|
|
50
|
-
PlaywrightAiFixture: () => PlaywrightAiFixture
|
|
50
|
+
PlaywrightAiFixture: () => PlaywrightAiFixture,
|
|
51
|
+
PuppeteerAgent: () => PageAgent
|
|
51
52
|
});
|
|
52
53
|
module.exports = __toCommonJS(src_exports);
|
|
53
54
|
|
|
54
55
|
// src/playwright/index.ts
|
|
55
|
-
var
|
|
56
|
+
var import_crypto = require("crypto");
|
|
56
57
|
|
|
57
|
-
// src/playwright/
|
|
58
|
-
var
|
|
59
|
-
var
|
|
60
|
-
var
|
|
61
|
-
var import_image2 = require("@midscene/core/image");
|
|
58
|
+
// src/playwright/cache.ts
|
|
59
|
+
var import_path2 = __toESM(require("path"));
|
|
60
|
+
var import_fs2 = __toESM(require("fs"));
|
|
61
|
+
var import_utils2 = require("@midscene/core/utils");
|
|
62
62
|
|
|
63
|
-
// src/
|
|
63
|
+
// src/common/utils.ts
|
|
64
64
|
var import_fs = __toESM(require("fs"));
|
|
65
65
|
var import_assert = __toESM(require("assert"));
|
|
66
66
|
var import_path = __toESM(require("path"));
|
|
67
67
|
var import_image = require("@midscene/core/image");
|
|
68
|
+
var import_utils = require("@midscene/core/utils");
|
|
68
69
|
|
|
69
|
-
// src/
|
|
70
|
+
// src/web-element.ts
|
|
70
71
|
var WebElementInfo = class {
|
|
71
72
|
constructor({
|
|
72
73
|
content,
|
|
@@ -84,24 +85,13 @@ var WebElementInfo = class {
|
|
|
84
85
|
this.id = id;
|
|
85
86
|
this.attributes = attributes;
|
|
86
87
|
}
|
|
87
|
-
async tap() {
|
|
88
|
-
await this.page.mouse.click(this.center[0], this.center[1]);
|
|
89
|
-
}
|
|
90
|
-
async hover() {
|
|
91
|
-
await this.page.mouse.move(this.center[0], this.center[1]);
|
|
92
|
-
}
|
|
93
|
-
async type(text) {
|
|
94
|
-
await this.page.keyboard.type(text);
|
|
95
|
-
}
|
|
96
|
-
async press(key) {
|
|
97
|
-
await this.page.keyboard.press(key);
|
|
98
|
-
}
|
|
99
88
|
};
|
|
100
89
|
|
|
101
|
-
// src/
|
|
102
|
-
async function
|
|
90
|
+
// src/common/utils.ts
|
|
91
|
+
async function parseContextFromWebPage(page, _opt) {
|
|
103
92
|
(0, import_assert.default)(page, "page is required");
|
|
104
|
-
const
|
|
93
|
+
const url = page.url();
|
|
94
|
+
const file = (0, import_utils.getTmpFile)("jpeg");
|
|
105
95
|
await page.screenshot({ path: file, type: "jpeg", quality: 75 });
|
|
106
96
|
const screenshotBuffer = (0, import_fs.readFileSync)(file);
|
|
107
97
|
const screenshotBase64 = (0, import_image.base64Encoded)(file);
|
|
@@ -111,7 +101,8 @@ async function parseContextFromPlaywrightPage(page, _opt) {
|
|
|
111
101
|
return {
|
|
112
102
|
content: elementsInfo,
|
|
113
103
|
size,
|
|
114
|
-
screenshotBase64
|
|
104
|
+
screenshotBase64,
|
|
105
|
+
url
|
|
115
106
|
};
|
|
116
107
|
}
|
|
117
108
|
async function getElementInfosFromPage(page) {
|
|
@@ -153,18 +144,136 @@ function findNearestPackageJson(dir) {
|
|
|
153
144
|
return findNearestPackageJson(parentDir);
|
|
154
145
|
}
|
|
155
146
|
|
|
156
|
-
// src/playwright/
|
|
157
|
-
|
|
158
|
-
|
|
147
|
+
// src/playwright/cache.ts
|
|
148
|
+
function writeTestCache(taskFile, taskTitle, taskCacheJson) {
|
|
149
|
+
const packageJson = getPkgInfo();
|
|
150
|
+
(0, import_utils2.writeDumpFile)({
|
|
151
|
+
fileName: `${taskFile}(${taskTitle})`,
|
|
152
|
+
fileExt: "json",
|
|
153
|
+
fileContent: JSON.stringify(
|
|
154
|
+
__spreadValues({
|
|
155
|
+
pkgName: packageJson.name,
|
|
156
|
+
pkgVersion: packageJson.version,
|
|
157
|
+
taskFile,
|
|
158
|
+
taskTitle
|
|
159
|
+
}, taskCacheJson),
|
|
160
|
+
null,
|
|
161
|
+
2
|
|
162
|
+
),
|
|
163
|
+
type: "cache"
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
function readTestCache(taskFile, taskTitle) {
|
|
167
|
+
const cacheFile = (0, import_path2.join)((0, import_utils2.getDumpDirPath)("cache"), `${taskFile}(${taskTitle}).json`);
|
|
168
|
+
const pkgInfo = getPkgInfo();
|
|
169
|
+
if (process.env.MIDSCENE_CACHE === "true" && import_fs2.default.existsSync(cacheFile)) {
|
|
170
|
+
try {
|
|
171
|
+
const data = import_fs2.default.readFileSync(cacheFile, "utf8");
|
|
172
|
+
const jsonData = JSON.parse(data);
|
|
173
|
+
if (jsonData.pkgName !== pkgInfo.name || jsonData.pkgVersion !== pkgInfo.version) {
|
|
174
|
+
return void 0;
|
|
175
|
+
}
|
|
176
|
+
return jsonData;
|
|
177
|
+
} catch (err) {
|
|
178
|
+
return void 0;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
return void 0;
|
|
182
|
+
}
|
|
183
|
+
function getPkgInfo() {
|
|
184
|
+
const packageJsonDir = findNearestPackageJson(__dirname);
|
|
185
|
+
if (!packageJsonDir) {
|
|
186
|
+
console.error("Cannot find package.json directory: ", __dirname);
|
|
187
|
+
return {
|
|
188
|
+
name: "@midscene/web",
|
|
189
|
+
version: "0.0.0"
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
const packageJsonPath = import_path2.default.join(packageJsonDir, "package.json");
|
|
193
|
+
const data = import_fs2.default.readFileSync(packageJsonPath, "utf8");
|
|
194
|
+
const packageJson = JSON.parse(data);
|
|
195
|
+
return {
|
|
196
|
+
name: packageJson.name,
|
|
197
|
+
version: packageJson.version
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// src/common/agent.ts
|
|
202
|
+
var import_utils6 = require("@midscene/core/utils");
|
|
203
|
+
|
|
204
|
+
// src/common/tasks.ts
|
|
205
|
+
var import_assert2 = __toESM(require("assert"));
|
|
206
|
+
var import_core = __toESM(require("@midscene/core"));
|
|
207
|
+
var import_utils4 = require("@midscene/core/utils");
|
|
208
|
+
var import_image2 = require("@midscene/core/image");
|
|
209
|
+
|
|
210
|
+
// src/common/task-cache.ts
|
|
211
|
+
var TaskCache = class {
|
|
212
|
+
constructor(opts) {
|
|
213
|
+
this.cache = opts == null ? void 0 : opts.cache;
|
|
214
|
+
this.newCache = {
|
|
215
|
+
aiTasks: []
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
readCache(pageContext, type, userPrompt) {
|
|
219
|
+
var _a;
|
|
220
|
+
if (this.cache) {
|
|
221
|
+
const { aiTasks } = this.cache;
|
|
222
|
+
const index = aiTasks.findIndex((item) => item.prompt === userPrompt);
|
|
223
|
+
if (index === -1) {
|
|
224
|
+
return false;
|
|
225
|
+
}
|
|
226
|
+
const taskRes = aiTasks.splice(index, 1)[0];
|
|
227
|
+
if ((taskRes == null ? void 0 : taskRes.type) === "locate" && !((_a = taskRes.response) == null ? void 0 : _a.elements.every((element) => {
|
|
228
|
+
const findIndex = pageContext.content.findIndex(
|
|
229
|
+
(contentElement) => contentElement.id === element.id
|
|
230
|
+
);
|
|
231
|
+
if (findIndex === -1) {
|
|
232
|
+
return false;
|
|
233
|
+
}
|
|
234
|
+
return true;
|
|
235
|
+
}))) {
|
|
236
|
+
return false;
|
|
237
|
+
}
|
|
238
|
+
if (taskRes && taskRes.type === type && taskRes.prompt === userPrompt && this.pageContextEqual(taskRes.pageContext, pageContext)) {
|
|
239
|
+
return taskRes.response;
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
return false;
|
|
243
|
+
}
|
|
244
|
+
saveCache(cache) {
|
|
245
|
+
var _a;
|
|
246
|
+
if (cache) {
|
|
247
|
+
(_a = this.newCache) == null ? void 0 : _a.aiTasks.push(cache);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
pageContextEqual(taskPageContext, pageContext) {
|
|
251
|
+
return taskPageContext.size.width === pageContext.size.width && taskPageContext.size.height === pageContext.size.height;
|
|
252
|
+
}
|
|
253
|
+
/**
|
|
254
|
+
* Generate task cache data.
|
|
255
|
+
* This method is mainly used to create or obtain some cached data for tasks, and it returns a new cache object.
|
|
256
|
+
* In the cache object, it may contain task-related information, states, or other necessary data.
|
|
257
|
+
* It is assumed that the `newCache` property already exists in the current class or object and is a data structure used to store task cache.
|
|
258
|
+
* @returns {Object} Returns a new cache object, which may include task cache data.
|
|
259
|
+
*/
|
|
260
|
+
generateTaskCache() {
|
|
261
|
+
return this.newCache;
|
|
262
|
+
}
|
|
263
|
+
};
|
|
264
|
+
|
|
265
|
+
// src/common/tasks.ts
|
|
266
|
+
var PageTaskExecutor = class {
|
|
267
|
+
constructor(page, opts) {
|
|
159
268
|
this.page = page;
|
|
160
269
|
this.insight = new import_core.default(async () => {
|
|
161
|
-
return await
|
|
270
|
+
return await parseContextFromWebPage(page);
|
|
162
271
|
});
|
|
163
|
-
this.
|
|
272
|
+
this.taskCache = new TaskCache(opts);
|
|
164
273
|
}
|
|
165
274
|
async recordScreenshot(timing) {
|
|
166
|
-
const file = (0,
|
|
167
|
-
await this.page.screenshot(__spreadProps(__spreadValues({},
|
|
275
|
+
const file = (0, import_utils4.getTmpFile)("jpeg");
|
|
276
|
+
await this.page.screenshot(__spreadProps(__spreadValues({}, import_utils4.commonScreenshotParam), {
|
|
168
277
|
path: file
|
|
169
278
|
}));
|
|
170
279
|
const item = {
|
|
@@ -185,7 +294,7 @@ var PlayWrightActionAgent = class {
|
|
|
185
294
|
recorder.push(shot);
|
|
186
295
|
const result = await taskApply.executor(param, context, ...args);
|
|
187
296
|
if (taskApply.type === "Action") {
|
|
188
|
-
await (0,
|
|
297
|
+
await (0, import_utils4.sleep)(1e3);
|
|
189
298
|
const shot2 = await this.recordScreenshot("after Action");
|
|
190
299
|
recorder.push(shot2);
|
|
191
300
|
}
|
|
@@ -200,23 +309,48 @@ var PlayWrightActionAgent = class {
|
|
|
200
309
|
const taskFind = {
|
|
201
310
|
type: "Insight",
|
|
202
311
|
subType: "Locate",
|
|
203
|
-
param:
|
|
204
|
-
prompt: plan2.thought
|
|
205
|
-
},
|
|
312
|
+
param: plan2.param,
|
|
206
313
|
executor: async (param) => {
|
|
207
314
|
let insightDump;
|
|
208
315
|
const dumpCollector = (dump) => {
|
|
209
316
|
insightDump = dump;
|
|
210
317
|
};
|
|
211
318
|
this.insight.onceDumpUpdatedFn = dumpCollector;
|
|
212
|
-
const
|
|
319
|
+
const pageContext = await this.insight.contextRetrieverFn();
|
|
320
|
+
const locateCache = this.taskCache.readCache(pageContext, "locate", param.prompt);
|
|
321
|
+
let locateResult;
|
|
322
|
+
const callAI = this.insight.aiVendorFn;
|
|
323
|
+
const element = await this.insight.locate(param.prompt, {
|
|
324
|
+
callAI: async (message) => {
|
|
325
|
+
if (locateCache) {
|
|
326
|
+
locateResult = locateCache;
|
|
327
|
+
return Promise.resolve(locateCache);
|
|
328
|
+
}
|
|
329
|
+
locateResult = await callAI(message);
|
|
330
|
+
return locateResult;
|
|
331
|
+
}
|
|
332
|
+
});
|
|
213
333
|
(0, import_assert2.default)(element, `Element not found: ${param.prompt}`);
|
|
334
|
+
if (locateResult) {
|
|
335
|
+
this.taskCache.saveCache({
|
|
336
|
+
type: "locate",
|
|
337
|
+
pageContext: {
|
|
338
|
+
url: pageContext.url,
|
|
339
|
+
size: pageContext.size
|
|
340
|
+
},
|
|
341
|
+
prompt: param.prompt,
|
|
342
|
+
response: locateResult
|
|
343
|
+
});
|
|
344
|
+
}
|
|
214
345
|
return {
|
|
215
346
|
output: {
|
|
216
347
|
element
|
|
217
348
|
},
|
|
218
349
|
log: {
|
|
219
350
|
dump: insightDump
|
|
351
|
+
},
|
|
352
|
+
cache: {
|
|
353
|
+
hit: Boolean(locateResult)
|
|
220
354
|
}
|
|
221
355
|
};
|
|
222
356
|
}
|
|
@@ -227,7 +361,10 @@ var PlayWrightActionAgent = class {
|
|
|
227
361
|
type: "Action",
|
|
228
362
|
subType: "Input",
|
|
229
363
|
param: plan2.param,
|
|
230
|
-
executor: async (taskParam) => {
|
|
364
|
+
executor: async (taskParam, { element }) => {
|
|
365
|
+
if (element) {
|
|
366
|
+
await this.page.mouse.click(element.center[0], element.center[1]);
|
|
367
|
+
}
|
|
231
368
|
(0, import_assert2.default)(taskParam.value, "No value to input");
|
|
232
369
|
await this.page.keyboard.type(taskParam.value);
|
|
233
370
|
}
|
|
@@ -302,43 +439,66 @@ var PlayWrightActionAgent = class {
|
|
|
302
439
|
return tasks;
|
|
303
440
|
}
|
|
304
441
|
async action(userPrompt) {
|
|
305
|
-
|
|
306
|
-
|
|
442
|
+
const taskExecutor = new import_core.Executor(userPrompt);
|
|
443
|
+
taskExecutor.description = userPrompt;
|
|
307
444
|
let plans = [];
|
|
308
445
|
const planningTask = {
|
|
309
446
|
type: "Planning",
|
|
310
447
|
param: {
|
|
311
448
|
userPrompt
|
|
312
449
|
},
|
|
313
|
-
async
|
|
314
|
-
const
|
|
450
|
+
executor: async (param) => {
|
|
451
|
+
const pageContext = await this.insight.contextRetrieverFn();
|
|
452
|
+
let planResult;
|
|
453
|
+
const planCache = this.taskCache.readCache(pageContext, "plan", userPrompt);
|
|
454
|
+
if (planCache) {
|
|
455
|
+
planResult = planCache;
|
|
456
|
+
} else {
|
|
457
|
+
planResult = await (0, import_core.plan)(param.userPrompt, {
|
|
458
|
+
context: pageContext
|
|
459
|
+
});
|
|
460
|
+
}
|
|
315
461
|
(0, import_assert2.default)(planResult.plans.length > 0, "No plans found");
|
|
316
462
|
plans = planResult.plans;
|
|
463
|
+
this.taskCache.saveCache({
|
|
464
|
+
type: "plan",
|
|
465
|
+
pageContext: {
|
|
466
|
+
url: pageContext.url,
|
|
467
|
+
size: pageContext.size
|
|
468
|
+
},
|
|
469
|
+
prompt: userPrompt,
|
|
470
|
+
response: planResult
|
|
471
|
+
});
|
|
317
472
|
return {
|
|
318
|
-
output: planResult
|
|
473
|
+
output: planResult,
|
|
474
|
+
cache: {
|
|
475
|
+
hint: Boolean(planCache)
|
|
476
|
+
}
|
|
319
477
|
};
|
|
320
478
|
}
|
|
321
479
|
};
|
|
322
480
|
try {
|
|
323
|
-
await
|
|
324
|
-
await
|
|
325
|
-
this.
|
|
481
|
+
await taskExecutor.append(this.wrapExecutorWithScreenshot(planningTask));
|
|
482
|
+
await taskExecutor.flush();
|
|
483
|
+
this.executionDump = taskExecutor.dump();
|
|
326
484
|
const executables = await this.convertPlanToExecutable(plans);
|
|
327
|
-
await
|
|
328
|
-
await
|
|
329
|
-
this.
|
|
485
|
+
await taskExecutor.append(executables);
|
|
486
|
+
await taskExecutor.flush();
|
|
487
|
+
this.executionDump = taskExecutor.dump();
|
|
330
488
|
(0, import_assert2.default)(
|
|
331
|
-
|
|
332
|
-
`failed to execute tasks: ${
|
|
489
|
+
taskExecutor.status !== "error",
|
|
490
|
+
`failed to execute tasks: ${taskExecutor.status}, msg: ${taskExecutor.errorMsg || ""}`
|
|
333
491
|
);
|
|
334
492
|
} catch (e) {
|
|
335
|
-
this.
|
|
493
|
+
this.executionDump = taskExecutor.dump();
|
|
336
494
|
const err = new Error(e.message, { cause: e });
|
|
337
495
|
throw err;
|
|
338
496
|
}
|
|
339
497
|
}
|
|
340
498
|
async query(demand) {
|
|
341
|
-
|
|
499
|
+
const description = JSON.stringify(demand);
|
|
500
|
+
const taskExecutor = new import_core.Executor(description);
|
|
501
|
+
taskExecutor.description = description;
|
|
342
502
|
let data;
|
|
343
503
|
const queryTask = {
|
|
344
504
|
type: "Insight",
|
|
@@ -360,11 +520,11 @@ var PlayWrightActionAgent = class {
|
|
|
360
520
|
}
|
|
361
521
|
};
|
|
362
522
|
try {
|
|
363
|
-
await
|
|
364
|
-
await
|
|
365
|
-
this.
|
|
523
|
+
await taskExecutor.append(this.wrapExecutorWithScreenshot(queryTask));
|
|
524
|
+
await taskExecutor.flush();
|
|
525
|
+
this.executionDump = taskExecutor.dump();
|
|
366
526
|
} catch (e) {
|
|
367
|
-
this.
|
|
527
|
+
this.executionDump = taskExecutor.dump();
|
|
368
528
|
const err = new Error(e.message, { cause: e });
|
|
369
529
|
throw err;
|
|
370
530
|
}
|
|
@@ -372,102 +532,171 @@ var PlayWrightActionAgent = class {
|
|
|
372
532
|
}
|
|
373
533
|
};
|
|
374
534
|
|
|
375
|
-
// src/
|
|
376
|
-
var
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
groupName,
|
|
535
|
+
// src/common/agent.ts
|
|
536
|
+
var PageAgent = class {
|
|
537
|
+
constructor(page, opts) {
|
|
538
|
+
this.page = page;
|
|
539
|
+
this.dumps = [
|
|
540
|
+
{
|
|
541
|
+
groupName: (opts == null ? void 0 : opts.taskFile) || "unnamed",
|
|
383
542
|
executions: []
|
|
384
|
-
}
|
|
385
|
-
|
|
386
|
-
|
|
543
|
+
}
|
|
544
|
+
];
|
|
545
|
+
this.testId = (opts == null ? void 0 : opts.testId) || String(process.pid);
|
|
546
|
+
this.actionAgent = new PageTaskExecutor(this.page, {
|
|
547
|
+
cache: (opts == null ? void 0 : opts.cache) || { aiTasks: [] }
|
|
548
|
+
});
|
|
549
|
+
}
|
|
550
|
+
appendDump(execution) {
|
|
551
|
+
const currentDump = this.dumps[0];
|
|
387
552
|
currentDump.executions.push(execution);
|
|
388
|
-
}
|
|
389
|
-
|
|
390
|
-
(0,
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
caseName = titlePath.pop();
|
|
398
|
-
groupName = titlePath.join(" > ");
|
|
399
|
-
} else if (titlePath.length === 1) {
|
|
400
|
-
caseName = titlePath[0];
|
|
401
|
-
groupName = caseName;
|
|
402
|
-
} else {
|
|
403
|
-
caseName = "unnamed";
|
|
404
|
-
groupName = "unnamed";
|
|
405
|
-
}
|
|
406
|
-
return { groupName, caseName };
|
|
407
|
-
};
|
|
408
|
-
const aiAction = async (page, testInfo, taskPrompt) => {
|
|
409
|
-
const { groupName, caseName } = groupAndCaseForTest(testInfo);
|
|
410
|
-
const actionAgent = new PlayWrightActionAgent(page, { taskName: caseName });
|
|
553
|
+
}
|
|
554
|
+
writeOutActionDumps() {
|
|
555
|
+
this.dumpFile = (0, import_utils6.writeDumpFile)({
|
|
556
|
+
fileName: `playwright-${this.testId}`,
|
|
557
|
+
fileExt: import_utils6.groupedActionDumpFileExt,
|
|
558
|
+
fileContent: JSON.stringify(this.dumps)
|
|
559
|
+
});
|
|
560
|
+
}
|
|
561
|
+
async aiAction(taskPrompt) {
|
|
411
562
|
let error;
|
|
412
563
|
try {
|
|
413
|
-
await actionAgent.action(taskPrompt);
|
|
564
|
+
await this.actionAgent.action(taskPrompt);
|
|
414
565
|
} catch (e) {
|
|
415
566
|
error = e;
|
|
416
567
|
}
|
|
417
|
-
if (actionAgent.
|
|
418
|
-
appendDump(
|
|
419
|
-
writeOutActionDumps();
|
|
568
|
+
if (this.actionAgent.executionDump) {
|
|
569
|
+
this.appendDump(this.actionAgent.executionDump);
|
|
570
|
+
this.writeOutActionDumps();
|
|
420
571
|
}
|
|
421
572
|
if (error) {
|
|
422
573
|
console.error(error);
|
|
423
574
|
throw new Error(error.message, { cause: error });
|
|
424
575
|
}
|
|
425
|
-
}
|
|
426
|
-
|
|
427
|
-
const { groupName, caseName } = groupAndCaseForTest(testInfo);
|
|
428
|
-
const actionAgent = new PlayWrightActionAgent(page, { taskName: caseName });
|
|
576
|
+
}
|
|
577
|
+
async aiQuery(demand) {
|
|
429
578
|
let error;
|
|
430
579
|
let result;
|
|
431
580
|
try {
|
|
432
|
-
result = await actionAgent.query(demand);
|
|
581
|
+
result = await this.actionAgent.query(demand);
|
|
433
582
|
} catch (e) {
|
|
434
583
|
error = e;
|
|
435
584
|
}
|
|
436
|
-
if (actionAgent.
|
|
437
|
-
appendDump(
|
|
438
|
-
writeOutActionDumps();
|
|
585
|
+
if (this.actionAgent.executionDump) {
|
|
586
|
+
this.appendDump(this.actionAgent.executionDump);
|
|
587
|
+
this.writeOutActionDumps();
|
|
439
588
|
}
|
|
440
589
|
if (error) {
|
|
441
590
|
console.error(error);
|
|
442
591
|
throw new Error(error.message, { cause: error });
|
|
443
592
|
}
|
|
444
593
|
return result;
|
|
594
|
+
}
|
|
595
|
+
async ai(taskPrompt, type = "action") {
|
|
596
|
+
if (type === "action") {
|
|
597
|
+
return this.aiAction(taskPrompt);
|
|
598
|
+
} else if (type === "query") {
|
|
599
|
+
return this.aiQuery(taskPrompt);
|
|
600
|
+
}
|
|
601
|
+
throw new Error(`Unknown or Unsupported task type: ${type}, only support 'action' or 'query'`);
|
|
602
|
+
}
|
|
603
|
+
};
|
|
604
|
+
|
|
605
|
+
// src/playwright/index.ts
|
|
606
|
+
var groupAndCaseForTest = (testInfo) => {
|
|
607
|
+
let taskFile;
|
|
608
|
+
let taskTitle;
|
|
609
|
+
const titlePath = [...testInfo.titlePath];
|
|
610
|
+
if (titlePath.length > 1) {
|
|
611
|
+
taskTitle = titlePath.pop();
|
|
612
|
+
taskFile = `${titlePath.join(" > ")}:${testInfo.line}`;
|
|
613
|
+
} else if (titlePath.length === 1) {
|
|
614
|
+
taskTitle = titlePath[0];
|
|
615
|
+
taskFile = `${taskTitle}:${testInfo.line}`;
|
|
616
|
+
} else {
|
|
617
|
+
taskTitle = "unnamed";
|
|
618
|
+
taskFile = "unnamed";
|
|
619
|
+
}
|
|
620
|
+
return { taskFile, taskTitle };
|
|
621
|
+
};
|
|
622
|
+
var midSceneAgentKeyId = "_midSceneAgentId";
|
|
623
|
+
var PlaywrightAiFixture = () => {
|
|
624
|
+
const pageAgentMap = {};
|
|
625
|
+
const agentForPage = (page, opts) => {
|
|
626
|
+
let idForPage = page[midSceneAgentKeyId];
|
|
627
|
+
if (!idForPage) {
|
|
628
|
+
idForPage = (0, import_crypto.randomUUID)();
|
|
629
|
+
page[midSceneAgentKeyId] = idForPage;
|
|
630
|
+
const testCase = readTestCache(opts.taskFile, opts.taskTitle) || { aiTasks: [] };
|
|
631
|
+
pageAgentMap[idForPage] = new PageAgent(page, {
|
|
632
|
+
testId: `${opts.testId}-${idForPage}`,
|
|
633
|
+
taskFile: opts.taskFile,
|
|
634
|
+
cache: testCase
|
|
635
|
+
});
|
|
636
|
+
}
|
|
637
|
+
return pageAgentMap[idForPage];
|
|
445
638
|
};
|
|
446
639
|
return {
|
|
447
|
-
// shortcut
|
|
448
640
|
ai: async ({ page }, use, testInfo) => {
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
641
|
+
const { taskFile, taskTitle } = groupAndCaseForTest(testInfo);
|
|
642
|
+
const agent = agentForPage(page, { testId: testInfo.testId, taskFile, taskTitle });
|
|
643
|
+
await use(async (taskPrompt, opts) => {
|
|
644
|
+
await page.waitForLoadState("networkidle");
|
|
645
|
+
const actionType = (opts == null ? void 0 : opts.type) || "action";
|
|
646
|
+
const result = await agent.ai(taskPrompt, actionType);
|
|
647
|
+
return result;
|
|
456
648
|
});
|
|
649
|
+
const taskCacheJson = agent.actionAgent.taskCache.generateTaskCache();
|
|
650
|
+
writeTestCache(taskFile, taskTitle, taskCacheJson);
|
|
651
|
+
if (agent.dumpFile) {
|
|
652
|
+
testInfo.annotations.push({
|
|
653
|
+
type: "MIDSCENE_AI_ACTION",
|
|
654
|
+
description: JSON.stringify({
|
|
655
|
+
testId: testInfo.testId,
|
|
656
|
+
dumpPath: agent.dumpFile
|
|
657
|
+
})
|
|
658
|
+
});
|
|
659
|
+
}
|
|
457
660
|
},
|
|
458
661
|
aiAction: async ({ page }, use, testInfo) => {
|
|
662
|
+
const { taskFile, taskTitle } = groupAndCaseForTest(testInfo);
|
|
663
|
+
const agent = agentForPage(page, { testId: testInfo.testId, taskFile, taskTitle });
|
|
459
664
|
await use(async (taskPrompt) => {
|
|
460
|
-
await
|
|
665
|
+
await page.waitForLoadState("networkidle");
|
|
666
|
+
await agent.aiAction(taskPrompt);
|
|
461
667
|
});
|
|
668
|
+
if (agent.dumpFile) {
|
|
669
|
+
testInfo.annotations.push({
|
|
670
|
+
type: "MIDSCENE_AI_ACTION",
|
|
671
|
+
description: JSON.stringify({
|
|
672
|
+
testId: testInfo.testId,
|
|
673
|
+
dumpPath: agent.dumpFile
|
|
674
|
+
})
|
|
675
|
+
});
|
|
676
|
+
}
|
|
462
677
|
},
|
|
463
678
|
aiQuery: async ({ page }, use, testInfo) => {
|
|
679
|
+
const { taskFile, taskTitle } = groupAndCaseForTest(testInfo);
|
|
680
|
+
const agent = agentForPage(page, { testId: testInfo.testId, taskFile, taskTitle });
|
|
464
681
|
await use(async function(demand) {
|
|
465
|
-
|
|
682
|
+
await page.waitForLoadState("networkidle");
|
|
683
|
+
const result = await agent.aiQuery(demand);
|
|
684
|
+
return result;
|
|
466
685
|
});
|
|
686
|
+
if (agent.dumpFile) {
|
|
687
|
+
testInfo.annotations.push({
|
|
688
|
+
type: "MIDSCENE_AI_ACTION",
|
|
689
|
+
description: JSON.stringify({
|
|
690
|
+
testId: testInfo.testId,
|
|
691
|
+
dumpPath: agent.dumpFile
|
|
692
|
+
})
|
|
693
|
+
});
|
|
694
|
+
}
|
|
467
695
|
}
|
|
468
696
|
};
|
|
469
697
|
};
|
|
470
698
|
// Annotate the CommonJS export names for ESM import in node:
|
|
471
699
|
0 && (module.exports = {
|
|
472
|
-
PlaywrightAiFixture
|
|
700
|
+
PlaywrightAiFixture,
|
|
701
|
+
PuppeteerAgent
|
|
473
702
|
});
|