@midscene/web 0.0.1 → 0.1.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/dist/es/index.js +337 -110
- package/dist/es/playwright-report.js +2380 -0
- package/dist/lib/index.js +338 -110
- 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
|
}
|
|
@@ -209,14 +318,41 @@ var PlayWrightActionAgent = class {
|
|
|
209
318
|
insightDump = dump;
|
|
210
319
|
};
|
|
211
320
|
this.insight.onceDumpUpdatedFn = dumpCollector;
|
|
212
|
-
const
|
|
321
|
+
const pageContext = await this.insight.contextRetrieverFn();
|
|
322
|
+
const locateCache = this.taskCache.readCache(pageContext, "locate", param.prompt);
|
|
323
|
+
let locateResult;
|
|
324
|
+
const callAI = this.insight.aiVendorFn;
|
|
325
|
+
const element = await this.insight.locate(param.prompt, {
|
|
326
|
+
callAI: async (message) => {
|
|
327
|
+
if (locateCache) {
|
|
328
|
+
locateResult = locateCache;
|
|
329
|
+
return Promise.resolve(locateCache);
|
|
330
|
+
}
|
|
331
|
+
locateResult = await callAI(message);
|
|
332
|
+
return locateResult;
|
|
333
|
+
}
|
|
334
|
+
});
|
|
213
335
|
(0, import_assert2.default)(element, `Element not found: ${param.prompt}`);
|
|
336
|
+
if (locateResult) {
|
|
337
|
+
this.taskCache.saveCache({
|
|
338
|
+
type: "locate",
|
|
339
|
+
pageContext: {
|
|
340
|
+
url: pageContext.url,
|
|
341
|
+
size: pageContext.size
|
|
342
|
+
},
|
|
343
|
+
prompt: param.prompt,
|
|
344
|
+
response: locateResult
|
|
345
|
+
});
|
|
346
|
+
}
|
|
214
347
|
return {
|
|
215
348
|
output: {
|
|
216
349
|
element
|
|
217
350
|
},
|
|
218
351
|
log: {
|
|
219
352
|
dump: insightDump
|
|
353
|
+
},
|
|
354
|
+
cache: {
|
|
355
|
+
hit: Boolean(locateResult)
|
|
220
356
|
}
|
|
221
357
|
};
|
|
222
358
|
}
|
|
@@ -302,43 +438,66 @@ var PlayWrightActionAgent = class {
|
|
|
302
438
|
return tasks;
|
|
303
439
|
}
|
|
304
440
|
async action(userPrompt) {
|
|
305
|
-
|
|
306
|
-
|
|
441
|
+
const taskExecutor = new import_core.Executor(userPrompt);
|
|
442
|
+
taskExecutor.description = userPrompt;
|
|
307
443
|
let plans = [];
|
|
308
444
|
const planningTask = {
|
|
309
445
|
type: "Planning",
|
|
310
446
|
param: {
|
|
311
447
|
userPrompt
|
|
312
448
|
},
|
|
313
|
-
async
|
|
314
|
-
const
|
|
449
|
+
executor: async (param) => {
|
|
450
|
+
const pageContext = await this.insight.contextRetrieverFn();
|
|
451
|
+
let planResult;
|
|
452
|
+
const planCache = this.taskCache.readCache(pageContext, "plan", userPrompt);
|
|
453
|
+
if (planCache) {
|
|
454
|
+
planResult = planCache;
|
|
455
|
+
} else {
|
|
456
|
+
planResult = await (0, import_core.plan)(param.userPrompt, {
|
|
457
|
+
context: pageContext
|
|
458
|
+
});
|
|
459
|
+
}
|
|
315
460
|
(0, import_assert2.default)(planResult.plans.length > 0, "No plans found");
|
|
316
461
|
plans = planResult.plans;
|
|
462
|
+
this.taskCache.saveCache({
|
|
463
|
+
type: "plan",
|
|
464
|
+
pageContext: {
|
|
465
|
+
url: pageContext.url,
|
|
466
|
+
size: pageContext.size
|
|
467
|
+
},
|
|
468
|
+
prompt: userPrompt,
|
|
469
|
+
response: planResult
|
|
470
|
+
});
|
|
317
471
|
return {
|
|
318
|
-
output: planResult
|
|
472
|
+
output: planResult,
|
|
473
|
+
cache: {
|
|
474
|
+
hint: Boolean(planCache)
|
|
475
|
+
}
|
|
319
476
|
};
|
|
320
477
|
}
|
|
321
478
|
};
|
|
322
479
|
try {
|
|
323
|
-
await
|
|
324
|
-
await
|
|
325
|
-
this.
|
|
480
|
+
await taskExecutor.append(this.wrapExecutorWithScreenshot(planningTask));
|
|
481
|
+
await taskExecutor.flush();
|
|
482
|
+
this.executionDump = taskExecutor.dump();
|
|
326
483
|
const executables = await this.convertPlanToExecutable(plans);
|
|
327
|
-
await
|
|
328
|
-
await
|
|
329
|
-
this.
|
|
484
|
+
await taskExecutor.append(executables);
|
|
485
|
+
await taskExecutor.flush();
|
|
486
|
+
this.executionDump = taskExecutor.dump();
|
|
330
487
|
(0, import_assert2.default)(
|
|
331
|
-
|
|
332
|
-
`failed to execute tasks: ${
|
|
488
|
+
taskExecutor.status !== "error",
|
|
489
|
+
`failed to execute tasks: ${taskExecutor.status}, msg: ${taskExecutor.errorMsg || ""}`
|
|
333
490
|
);
|
|
334
491
|
} catch (e) {
|
|
335
|
-
this.
|
|
492
|
+
this.executionDump = taskExecutor.dump();
|
|
336
493
|
const err = new Error(e.message, { cause: e });
|
|
337
494
|
throw err;
|
|
338
495
|
}
|
|
339
496
|
}
|
|
340
497
|
async query(demand) {
|
|
341
|
-
|
|
498
|
+
const description = JSON.stringify(demand);
|
|
499
|
+
const taskExecutor = new import_core.Executor(description);
|
|
500
|
+
taskExecutor.description = description;
|
|
342
501
|
let data;
|
|
343
502
|
const queryTask = {
|
|
344
503
|
type: "Insight",
|
|
@@ -360,11 +519,11 @@ var PlayWrightActionAgent = class {
|
|
|
360
519
|
}
|
|
361
520
|
};
|
|
362
521
|
try {
|
|
363
|
-
await
|
|
364
|
-
await
|
|
365
|
-
this.
|
|
522
|
+
await taskExecutor.append(this.wrapExecutorWithScreenshot(queryTask));
|
|
523
|
+
await taskExecutor.flush();
|
|
524
|
+
this.executionDump = taskExecutor.dump();
|
|
366
525
|
} catch (e) {
|
|
367
|
-
this.
|
|
526
|
+
this.executionDump = taskExecutor.dump();
|
|
368
527
|
const err = new Error(e.message, { cause: e });
|
|
369
528
|
throw err;
|
|
370
529
|
}
|
|
@@ -372,102 +531,171 @@ var PlayWrightActionAgent = class {
|
|
|
372
531
|
}
|
|
373
532
|
};
|
|
374
533
|
|
|
375
|
-
// src/
|
|
376
|
-
var
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
groupName,
|
|
534
|
+
// src/common/agent.ts
|
|
535
|
+
var PageAgent = class {
|
|
536
|
+
constructor(page, opts) {
|
|
537
|
+
this.page = page;
|
|
538
|
+
this.dumps = [
|
|
539
|
+
{
|
|
540
|
+
groupName: (opts == null ? void 0 : opts.taskFile) || "unnamed",
|
|
383
541
|
executions: []
|
|
384
|
-
}
|
|
385
|
-
|
|
386
|
-
|
|
542
|
+
}
|
|
543
|
+
];
|
|
544
|
+
this.testId = (opts == null ? void 0 : opts.testId) || String(process.pid);
|
|
545
|
+
this.actionAgent = new PageTaskExecutor(this.page, {
|
|
546
|
+
cache: (opts == null ? void 0 : opts.cache) || { aiTasks: [] }
|
|
547
|
+
});
|
|
548
|
+
}
|
|
549
|
+
appendDump(execution) {
|
|
550
|
+
const currentDump = this.dumps[0];
|
|
387
551
|
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 });
|
|
552
|
+
}
|
|
553
|
+
writeOutActionDumps() {
|
|
554
|
+
this.dumpFile = (0, import_utils6.writeDumpFile)({
|
|
555
|
+
fileName: `playwright-${this.testId}`,
|
|
556
|
+
fileExt: import_utils6.groupedActionDumpFileExt,
|
|
557
|
+
fileContent: JSON.stringify(this.dumps)
|
|
558
|
+
});
|
|
559
|
+
}
|
|
560
|
+
async aiAction(taskPrompt) {
|
|
411
561
|
let error;
|
|
412
562
|
try {
|
|
413
|
-
await actionAgent.action(taskPrompt);
|
|
563
|
+
await this.actionAgent.action(taskPrompt);
|
|
414
564
|
} catch (e) {
|
|
415
565
|
error = e;
|
|
416
566
|
}
|
|
417
|
-
if (actionAgent.
|
|
418
|
-
appendDump(
|
|
419
|
-
writeOutActionDumps();
|
|
567
|
+
if (this.actionAgent.executionDump) {
|
|
568
|
+
this.appendDump(this.actionAgent.executionDump);
|
|
569
|
+
this.writeOutActionDumps();
|
|
420
570
|
}
|
|
421
571
|
if (error) {
|
|
422
572
|
console.error(error);
|
|
423
573
|
throw new Error(error.message, { cause: error });
|
|
424
574
|
}
|
|
425
|
-
}
|
|
426
|
-
|
|
427
|
-
const { groupName, caseName } = groupAndCaseForTest(testInfo);
|
|
428
|
-
const actionAgent = new PlayWrightActionAgent(page, { taskName: caseName });
|
|
575
|
+
}
|
|
576
|
+
async aiQuery(demand) {
|
|
429
577
|
let error;
|
|
430
578
|
let result;
|
|
431
579
|
try {
|
|
432
|
-
result = await actionAgent.query(demand);
|
|
580
|
+
result = await this.actionAgent.query(demand);
|
|
433
581
|
} catch (e) {
|
|
434
582
|
error = e;
|
|
435
583
|
}
|
|
436
|
-
if (actionAgent.
|
|
437
|
-
appendDump(
|
|
438
|
-
writeOutActionDumps();
|
|
584
|
+
if (this.actionAgent.executionDump) {
|
|
585
|
+
this.appendDump(this.actionAgent.executionDump);
|
|
586
|
+
this.writeOutActionDumps();
|
|
439
587
|
}
|
|
440
588
|
if (error) {
|
|
441
589
|
console.error(error);
|
|
442
590
|
throw new Error(error.message, { cause: error });
|
|
443
591
|
}
|
|
444
592
|
return result;
|
|
593
|
+
}
|
|
594
|
+
async ai(taskPrompt, type = "action") {
|
|
595
|
+
if (type === "action") {
|
|
596
|
+
return this.aiAction(taskPrompt);
|
|
597
|
+
} else if (type === "query") {
|
|
598
|
+
return this.aiQuery(taskPrompt);
|
|
599
|
+
}
|
|
600
|
+
throw new Error(`Unknown or Unsupported task type: ${type}, only support 'action' or 'query'`);
|
|
601
|
+
}
|
|
602
|
+
};
|
|
603
|
+
|
|
604
|
+
// src/playwright/index.ts
|
|
605
|
+
var groupAndCaseForTest = (testInfo) => {
|
|
606
|
+
let taskFile;
|
|
607
|
+
let taskTitle;
|
|
608
|
+
const titlePath = [...testInfo.titlePath];
|
|
609
|
+
if (titlePath.length > 1) {
|
|
610
|
+
taskTitle = titlePath.pop();
|
|
611
|
+
taskFile = `${titlePath.join(" > ")}:${testInfo.line}`;
|
|
612
|
+
} else if (titlePath.length === 1) {
|
|
613
|
+
taskTitle = titlePath[0];
|
|
614
|
+
taskFile = `${taskTitle}:${testInfo.line}`;
|
|
615
|
+
} else {
|
|
616
|
+
taskTitle = "unnamed";
|
|
617
|
+
taskFile = "unnamed";
|
|
618
|
+
}
|
|
619
|
+
return { taskFile, taskTitle };
|
|
620
|
+
};
|
|
621
|
+
var midSceneAgentKeyId = "_midSceneAgentId";
|
|
622
|
+
var PlaywrightAiFixture = () => {
|
|
623
|
+
const pageAgentMap = {};
|
|
624
|
+
const agentForPage = (page, opts) => {
|
|
625
|
+
let idForPage = page[midSceneAgentKeyId];
|
|
626
|
+
if (!idForPage) {
|
|
627
|
+
idForPage = (0, import_crypto.randomUUID)();
|
|
628
|
+
page[midSceneAgentKeyId] = idForPage;
|
|
629
|
+
const testCase = readTestCache(opts.taskFile, opts.taskTitle) || { aiTasks: [] };
|
|
630
|
+
pageAgentMap[idForPage] = new PageAgent(page, {
|
|
631
|
+
testId: `${opts.testId}-${idForPage}`,
|
|
632
|
+
taskFile: opts.taskFile,
|
|
633
|
+
cache: testCase
|
|
634
|
+
});
|
|
635
|
+
}
|
|
636
|
+
return pageAgentMap[idForPage];
|
|
445
637
|
};
|
|
446
638
|
return {
|
|
447
|
-
// shortcut
|
|
448
639
|
ai: async ({ page }, use, testInfo) => {
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
640
|
+
const { taskFile, taskTitle } = groupAndCaseForTest(testInfo);
|
|
641
|
+
const agent = agentForPage(page, { testId: testInfo.testId, taskFile, taskTitle });
|
|
642
|
+
await use(async (taskPrompt, opts) => {
|
|
643
|
+
await page.waitForLoadState("networkidle");
|
|
644
|
+
const actionType = (opts == null ? void 0 : opts.type) || "action";
|
|
645
|
+
const result = await agent.ai(taskPrompt, actionType);
|
|
646
|
+
return result;
|
|
456
647
|
});
|
|
648
|
+
const taskCacheJson = agent.actionAgent.taskCache.generateTaskCache();
|
|
649
|
+
writeTestCache(taskFile, taskTitle, taskCacheJson);
|
|
650
|
+
if (agent.dumpFile) {
|
|
651
|
+
testInfo.annotations.push({
|
|
652
|
+
type: "MIDSCENE_AI_ACTION",
|
|
653
|
+
description: JSON.stringify({
|
|
654
|
+
testId: testInfo.testId,
|
|
655
|
+
dumpPath: agent.dumpFile
|
|
656
|
+
})
|
|
657
|
+
});
|
|
658
|
+
}
|
|
457
659
|
},
|
|
458
660
|
aiAction: async ({ page }, use, testInfo) => {
|
|
661
|
+
const { taskFile, taskTitle } = groupAndCaseForTest(testInfo);
|
|
662
|
+
const agent = agentForPage(page, { testId: testInfo.testId, taskFile, taskTitle });
|
|
459
663
|
await use(async (taskPrompt) => {
|
|
460
|
-
await
|
|
664
|
+
await page.waitForLoadState("networkidle");
|
|
665
|
+
await agent.aiAction(taskPrompt);
|
|
461
666
|
});
|
|
667
|
+
if (agent.dumpFile) {
|
|
668
|
+
testInfo.annotations.push({
|
|
669
|
+
type: "MIDSCENE_AI_ACTION",
|
|
670
|
+
description: JSON.stringify({
|
|
671
|
+
testId: testInfo.testId,
|
|
672
|
+
dumpPath: agent.dumpFile
|
|
673
|
+
})
|
|
674
|
+
});
|
|
675
|
+
}
|
|
462
676
|
},
|
|
463
677
|
aiQuery: async ({ page }, use, testInfo) => {
|
|
678
|
+
const { taskFile, taskTitle } = groupAndCaseForTest(testInfo);
|
|
679
|
+
const agent = agentForPage(page, { testId: testInfo.testId, taskFile, taskTitle });
|
|
464
680
|
await use(async function(demand) {
|
|
465
|
-
|
|
681
|
+
await page.waitForLoadState("networkidle");
|
|
682
|
+
const result = await agent.aiQuery(demand);
|
|
683
|
+
return result;
|
|
466
684
|
});
|
|
685
|
+
if (agent.dumpFile) {
|
|
686
|
+
testInfo.annotations.push({
|
|
687
|
+
type: "MIDSCENE_AI_ACTION",
|
|
688
|
+
description: JSON.stringify({
|
|
689
|
+
testId: testInfo.testId,
|
|
690
|
+
dumpPath: agent.dumpFile
|
|
691
|
+
})
|
|
692
|
+
});
|
|
693
|
+
}
|
|
467
694
|
}
|
|
468
695
|
};
|
|
469
696
|
};
|
|
470
697
|
// Annotate the CommonJS export names for ESM import in node:
|
|
471
698
|
0 && (module.exports = {
|
|
472
|
-
PlaywrightAiFixture
|
|
699
|
+
PlaywrightAiFixture,
|
|
700
|
+
PuppeteerAgent
|
|
473
701
|
});
|