@midscene/core 0.18.1-beta-20250611082446.0 → 0.18.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/ai-model.d.ts +52 -4
- package/dist/es/ai-model.js +9 -1
- package/dist/es/{chunk-D4ZWVRRG.js → chunk-A7OTPIZJ.js} +19 -6
- package/dist/es/chunk-A7OTPIZJ.js.map +1 -0
- package/dist/es/{chunk-X2UALJ3B.js → chunk-EYIL4AHP.js} +353 -3
- package/dist/es/chunk-EYIL4AHP.js.map +1 -0
- package/dist/es/index.d.ts +4 -4
- package/dist/es/index.js +19 -26
- package/dist/es/index.js.map +1 -1
- package/dist/es/{llm-planning-453ffce5.d.ts → llm-planning-a951deb9.d.ts} +2 -2
- package/dist/es/{types-00c9086f.d.ts → types-dce56c26.d.ts} +6 -6
- package/dist/es/utils.d.ts +1 -1
- package/dist/es/utils.js +1 -1
- package/dist/lib/ai-model.d.ts +52 -4
- package/dist/lib/ai-model.js +10 -2
- package/dist/lib/{chunk-D4ZWVRRG.js → chunk-A7OTPIZJ.js} +18 -5
- package/dist/lib/chunk-A7OTPIZJ.js.map +1 -0
- package/dist/lib/{chunk-X2UALJ3B.js → chunk-EYIL4AHP.js} +371 -21
- package/dist/lib/chunk-EYIL4AHP.js.map +1 -0
- package/dist/lib/index.d.ts +4 -4
- package/dist/lib/index.js +37 -44
- package/dist/lib/index.js.map +1 -1
- package/dist/lib/{llm-planning-453ffce5.d.ts → llm-planning-a951deb9.d.ts} +2 -2
- package/dist/{types/types-00c9086f.d.ts → lib/types-dce56c26.d.ts} +6 -6
- package/dist/lib/utils.d.ts +1 -1
- package/dist/lib/utils.js +2 -2
- package/dist/types/ai-model.d.ts +52 -4
- package/dist/types/index.d.ts +4 -4
- package/dist/types/{llm-planning-453ffce5.d.ts → llm-planning-a951deb9.d.ts} +2 -2
- package/dist/{lib/types-00c9086f.d.ts → types/types-dce56c26.d.ts} +6 -6
- package/dist/types/utils.d.ts +1 -1
- package/package.json +4 -3
- package/dist/es/chunk-D4ZWVRRG.js.map +0 -1
- package/dist/es/chunk-X2UALJ3B.js.map +0 -1
- package/dist/lib/chunk-D4ZWVRRG.js.map +0 -1
- package/dist/lib/chunk-X2UALJ3B.js.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"mappings":"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","names":[],"ignoreList":[],"sources":["../../src/utils.ts"],"sourcesContent":["import { execSync } from 'node:child_process';\nimport { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';\nimport { tmpdir } from 'node:os';\nimport * as path from 'node:path';\nimport { dirname } from 'node:path';\nimport {\n defaultRunDirName,\n getMidsceneRunSubDir,\n} from '@midscene/shared/common';\nimport {\n MIDSCENE_DEBUG_MODE,\n MIDSCENE_OPENAI_INIT_CONFIG_JSON,\n getAIConfig,\n getAIConfigInJson,\n} from '@midscene/shared/env';\nimport { getRunningPkgInfo } from '@midscene/shared/fs';\nimport { assert, logMsg } from '@midscene/shared/utils';\nimport { escapeHtml, ifInBrowser, uuid } from '@midscene/shared/utils';\nimport xss from 'xss';\nimport type { Rect, ReportDumpWithAttributes } from './types';\n\nlet logEnvReady = false;\n\nconst xssOptions = {\n escapeHtml,\n};\nexport const groupedActionDumpFileExt = 'web-dump.json';\n\nconst reportTpl = 'REPLACE_ME_WITH_REPORT_HTML';\n\nfunction getReportTpl() {\n return reportTpl;\n}\n\nexport function replaceStringWithFirstAppearance(\n str: string,\n target: string,\n replacement: string,\n) {\n const index = str.indexOf(target);\n return str.slice(0, index) + replacement + str.slice(index + target.length);\n}\n\nexport function reportHTMLContent(\n dumpData: string | ReportDumpWithAttributes[],\n reportPath?: string,\n): string {\n const tpl = getReportTpl();\n if (!tpl) {\n console.warn('reportTpl is not set, will not write report');\n return '';\n }\n\n const dumpPlaceholder = '{{dump}}';\n\n // verify the template contains the placeholder\n if (!tpl.includes(dumpPlaceholder)) {\n console.warn(\n 'Failed to get the Midscene report template due to the lack of the {{dump}} placeholder. If you are building Midscene.js by yourself, please refer to the contribution guide for more information: https://github.com/web-infra-dev/midscene/blob/main/CONTRIBUTING.md#FAQ',\n );\n return '';\n }\n\n // find the first placeholder position\n const placeholderIndex = tpl.indexOf(dumpPlaceholder);\n\n // split the template into two parts before and after the placeholder\n const firstPart = tpl.substring(0, placeholderIndex);\n const secondPart = tpl.substring(placeholderIndex + dumpPlaceholder.length);\n\n // if reportPath is set, it means we are in write to file mode\n const writeToFile = reportPath && !ifInBrowser;\n let resultContent = '';\n\n // helper function: decide to write to file or append to resultContent\n const appendOrWrite = (content: string): void => {\n if (writeToFile) {\n writeFileSync(reportPath!, `${content}\\n`, {\n flag: 'a',\n });\n } else {\n resultContent += `${content}\\n`;\n }\n };\n\n // if writeToFile is true, write the first part to file, otherwise set the first part to the initial value of resultContent\n if (writeToFile) {\n writeFileSync(reportPath!, firstPart, { flag: 'w' }); // use 'w' flag to overwrite the existing file\n } else {\n resultContent = firstPart;\n }\n\n // generate dump content\n // handle empty data or undefined\n if (\n (Array.isArray(dumpData) && dumpData.length === 0) ||\n typeof dumpData === 'undefined'\n ) {\n const dumpContent =\n '<script type=\"midscene_web_dump\" type=\"application/json\"></script>';\n appendOrWrite(dumpContent);\n }\n // handle string type dumpData\n else if (typeof dumpData === 'string') {\n const dumpContent =\n // biome-ignore lint/style/useTemplate: <explanation> do not use template string here, will cause bundle error\n '<script type=\"midscene_web_dump\" type=\"application/json\">\\n' +\n xss(dumpData, xssOptions) +\n '\\n</script>';\n appendOrWrite(dumpContent);\n }\n // handle array type dumpData\n else {\n // for array, handle each item\n for (let i = 0; i < dumpData.length; i++) {\n const { dumpString, attributes } = dumpData[i];\n const attributesArr = Object.keys(attributes || {}).map((key) => {\n return `${key}=\"${encodeURIComponent(attributes![key])}\"`;\n });\n\n const dumpContent =\n // biome-ignore lint/style/useTemplate: <explanation> do not use template string here, will cause bundle error\n '<script type=\"midscene_web_dump\" type=\"application/json\" ' +\n attributesArr.join(' ') +\n '>\\n' +\n xss(dumpString, xssOptions) +\n '\\n</script>';\n appendOrWrite(dumpContent);\n }\n }\n\n // add the second part\n if (writeToFile) {\n writeFileSync(reportPath!, secondPart, { flag: 'a' });\n return reportPath!;\n }\n\n resultContent += secondPart;\n return resultContent;\n}\n\nexport function writeDumpReport(\n fileName: string,\n dumpData: string | ReportDumpWithAttributes[],\n): string | null {\n if (ifInBrowser) {\n console.log('will not write report in browser');\n return null;\n }\n\n const __dirname = dirname(__filename);\n const midscenePkgInfo = getRunningPkgInfo(__dirname);\n if (!midscenePkgInfo) {\n console.warn('midscenePkgInfo not found, will not write report');\n return null;\n }\n\n const reportPath = path.join(\n getMidsceneRunSubDir('report'),\n `${fileName}.html`,\n );\n\n reportHTMLContent(dumpData, reportPath);\n\n if (process.env.MIDSCENE_DEBUG_LOG_JSON) {\n const jsonPath = `${reportPath}.json`;\n let data = dumpData as ReportDumpWithAttributes[];\n\n if (typeof dumpData === 'string') {\n data = JSON.parse(dumpData) as ReportDumpWithAttributes[];\n }\n\n writeFileSync(\n jsonPath,\n JSON.stringify(\n data.map((item) => ({\n dumpString: JSON.parse(item.dumpString),\n attributes: item.attributes,\n })),\n null,\n 2,\n ),\n );\n\n logMsg(`Midscene - dump file written: ${jsonPath}`);\n }\n\n return reportPath;\n}\n\nexport function writeLogFile(opts: {\n fileName: string;\n fileExt: string;\n fileContent: string;\n type: 'dump' | 'cache' | 'report' | 'tmp';\n generateReport?: boolean;\n}) {\n if (ifInBrowser) {\n return '/mock/report.html';\n }\n const { fileName, fileExt, fileContent, type = 'dump' } = opts;\n const targetDir = getMidsceneRunSubDir(type);\n // Ensure directory exists\n if (!logEnvReady) {\n assert(targetDir, 'logDir should be set before writing dump file');\n\n // gitIgnore in the parent directory\n const gitIgnorePath = path.join(targetDir, '../../.gitignore');\n const gitPath = path.join(targetDir, '../../.git');\n let gitIgnoreContent = '';\n\n if (existsSync(gitPath)) {\n // if the git path exists, we need to add the log folder to the git ignore file\n if (existsSync(gitIgnorePath)) {\n gitIgnoreContent = readFileSync(gitIgnorePath, 'utf-8');\n }\n\n // ignore the log folder\n if (!gitIgnoreContent.includes(`${defaultRunDirName}/`)) {\n writeFileSync(\n gitIgnorePath,\n `${gitIgnoreContent}\\n# Midscene.js dump files\\n${defaultRunDirName}/dump\\n${defaultRunDirName}/report\\n${defaultRunDirName}/tmp\\n${defaultRunDirName}/log\\n`,\n 'utf-8',\n );\n }\n }\n\n logEnvReady = true;\n }\n\n const filePath = path.join(targetDir, `${fileName}.${fileExt}`);\n\n if (type !== 'dump') {\n // do not write dump file any more\n writeFileSync(filePath, fileContent);\n }\n\n if (opts?.generateReport) {\n return writeDumpReport(fileName, fileContent);\n }\n\n return filePath;\n}\n\nexport function getTmpDir(): string | null {\n try {\n const runningPkgInfo = getRunningPkgInfo();\n if (!runningPkgInfo) {\n return null;\n }\n const { name } = runningPkgInfo;\n const tmpPath = path.join(tmpdir(), name);\n mkdirSync(tmpPath, { recursive: true });\n return tmpPath;\n } catch (e) {\n return null;\n }\n}\n\nexport function getTmpFile(fileExtWithoutDot: string): string | null {\n if (ifInBrowser) {\n return null;\n }\n const tmpDir = getTmpDir();\n const filename = `${uuid()}.${fileExtWithoutDot}`;\n return path.join(tmpDir!, filename);\n}\n\nexport function overlapped(container: Rect, target: Rect) {\n // container and the target have some part overlapped\n return (\n container.left < target.left + target.width &&\n container.left + container.width > target.left &&\n container.top < target.top + target.height &&\n container.top + container.height > target.top\n );\n}\n\nexport async function sleep(ms: number) {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nexport function replacerForPageObject(key: string, value: any) {\n if (value && value.constructor?.name === 'Page') {\n return '[Page object]';\n }\n if (value && value.constructor?.name === 'Browser') {\n return '[Browser object]';\n }\n return value;\n}\n\nexport function stringifyDumpData(data: any, indents?: number) {\n return JSON.stringify(data, replacerForPageObject, indents);\n}\n\ndeclare const __VERSION__: string;\n\nexport function getVersion() {\n return __VERSION__;\n}\n\nfunction debugLog(...message: any[]) {\n const debugMode = getAIConfig(MIDSCENE_DEBUG_MODE);\n if (debugMode) {\n console.log('[Midscene]', ...message);\n }\n}\n\nlet lastReportedRepoUrl = '';\nexport function uploadTestInfoToServer({ testUrl }: { testUrl: string }) {\n let repoUrl = '';\n let userEmail = '';\n\n const extraConfig = getAIConfigInJson(MIDSCENE_OPENAI_INIT_CONFIG_JSON);\n const serverUrl = extraConfig?.REPORT_SERVER_URL;\n\n try {\n repoUrl = execSync('git config --get remote.origin.url').toString().trim();\n userEmail = execSync('git config --get user.email').toString().trim();\n } catch (error) {\n debugLog('Failed to get git info:', error);\n }\n\n // Only upload test info if:\n // 1. Server URL is configured AND\n // 2. Either:\n // - We have a repo URL that's different from last reported one (to avoid duplicate reports)\n // - OR we don't have a repo URL but have a test URL (for non-git environments)\n if (\n serverUrl &&\n ((repoUrl && repoUrl !== lastReportedRepoUrl) || (!repoUrl && testUrl))\n ) {\n debugLog('Uploading test info to server', {\n serverUrl,\n repoUrl,\n testUrl,\n userEmail,\n });\n\n fetch(serverUrl, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n repo_url: repoUrl,\n test_url: testUrl,\n user_email: userEmail,\n }),\n })\n .then((response) => response.json())\n .then((data) => {\n debugLog('Successfully uploaded test info to server:', data);\n })\n .catch((error) =>\n debugLog('Failed to upload test info to server:', error),\n );\n lastReportedRepoUrl = repoUrl;\n }\n}\n"]}
|
|
@@ -47,6 +47,14 @@ import { vlLocateMode } from "@midscene/shared/env";
|
|
|
47
47
|
import { treeToList } from "@midscene/shared/extractor";
|
|
48
48
|
import { compositeElementInfoImg } from "@midscene/shared/img";
|
|
49
49
|
import { getDebug } from "@midscene/shared/logger";
|
|
50
|
+
var AIActionType = /* @__PURE__ */ ((AIActionType2) => {
|
|
51
|
+
AIActionType2[AIActionType2["ASSERT"] = 0] = "ASSERT";
|
|
52
|
+
AIActionType2[AIActionType2["INSPECT_ELEMENT"] = 1] = "INSPECT_ELEMENT";
|
|
53
|
+
AIActionType2[AIActionType2["EXTRACT_DATA"] = 2] = "EXTRACT_DATA";
|
|
54
|
+
AIActionType2[AIActionType2["PLAN"] = 3] = "PLAN";
|
|
55
|
+
AIActionType2[AIActionType2["DESCRIBE_ELEMENT"] = 4] = "DESCRIBE_ELEMENT";
|
|
56
|
+
return AIActionType2;
|
|
57
|
+
})(AIActionType || {});
|
|
50
58
|
async function callAiFn(msgs, AIActionTypeValue) {
|
|
51
59
|
assert(
|
|
52
60
|
checkAIConfig(),
|
|
@@ -694,6 +702,11 @@ async function describeUserPage(context, opt) {
|
|
|
694
702
|
const treeRoot = context.tree;
|
|
695
703
|
const idElementMap = {};
|
|
696
704
|
const flatElements = treeToList2(treeRoot);
|
|
705
|
+
if (opt?.domIncluded === true && flatElements.length >= 5e3) {
|
|
706
|
+
console.warn(
|
|
707
|
+
'The number of elements is too large, it may cause the prompt to be too long, please use domIncluded: "visible-only" to reduce the number of elements'
|
|
708
|
+
);
|
|
709
|
+
}
|
|
697
710
|
flatElements.forEach((element) => {
|
|
698
711
|
idElementMap[element.id] = element;
|
|
699
712
|
if (typeof element.indexId !== "undefined") {
|
|
@@ -701,12 +714,13 @@ async function describeUserPage(context, opt) {
|
|
|
701
714
|
}
|
|
702
715
|
});
|
|
703
716
|
let pageDescription = "";
|
|
717
|
+
const visibleOnly = opt?.visibleOnly ?? opt?.domIncluded === "visible-only";
|
|
704
718
|
if (opt?.domIncluded) {
|
|
705
719
|
const contentTree = await descriptionOfTree(
|
|
706
720
|
treeRoot,
|
|
707
721
|
opt?.truncateTextLength,
|
|
708
722
|
opt?.filterNonTextContent,
|
|
709
|
-
|
|
723
|
+
visibleOnly
|
|
710
724
|
);
|
|
711
725
|
const sizeDescription = describeSize({ width, height });
|
|
712
726
|
pageDescription = `The size of the page: ${sizeDescription}
|
|
@@ -1288,6 +1302,7 @@ async function call(messages, AIActionTypeValue, responseFormat) {
|
|
|
1288
1302
|
const model = getModelName();
|
|
1289
1303
|
let content;
|
|
1290
1304
|
let usage;
|
|
1305
|
+
let timeCost;
|
|
1291
1306
|
const commonConfig = {
|
|
1292
1307
|
temperature: vlLocateMode2() === "vlm-ui-tars" ? 0 : 0.1,
|
|
1293
1308
|
stream: false,
|
|
@@ -1300,12 +1315,14 @@ async function call(messages, AIActionTypeValue, responseFormat) {
|
|
|
1300
1315
|
debugCall(`sending request to ${model}`);
|
|
1301
1316
|
let result;
|
|
1302
1317
|
try {
|
|
1318
|
+
const startTime2 = Date.now();
|
|
1303
1319
|
result = await completion.create({
|
|
1304
1320
|
model,
|
|
1305
1321
|
messages,
|
|
1306
1322
|
response_format: responseFormat,
|
|
1307
1323
|
...commonConfig
|
|
1308
1324
|
});
|
|
1325
|
+
timeCost = Date.now() - startTime2;
|
|
1309
1326
|
} catch (e) {
|
|
1310
1327
|
const newError = new Error(
|
|
1311
1328
|
`failed to call AI model service: ${e.message}. Trouble shooting: https://midscenejs.com/model-provider.html`,
|
|
@@ -1343,6 +1360,7 @@ async function call(messages, AIActionTypeValue, responseFormat) {
|
|
|
1343
1360
|
}
|
|
1344
1361
|
return content2;
|
|
1345
1362
|
};
|
|
1363
|
+
const startTime2 = Date.now();
|
|
1346
1364
|
const result = await completion.create({
|
|
1347
1365
|
model,
|
|
1348
1366
|
system: "You are a versatile professional in software UI automation",
|
|
@@ -1353,11 +1371,20 @@ async function call(messages, AIActionTypeValue, responseFormat) {
|
|
|
1353
1371
|
response_format: responseFormat,
|
|
1354
1372
|
...commonConfig
|
|
1355
1373
|
});
|
|
1374
|
+
timeCost = Date.now() - startTime2;
|
|
1356
1375
|
content = result.content[0].text;
|
|
1357
1376
|
assert3(content, "empty content");
|
|
1358
1377
|
usage = result.usage;
|
|
1359
1378
|
}
|
|
1360
|
-
return {
|
|
1379
|
+
return {
|
|
1380
|
+
content: content || "",
|
|
1381
|
+
usage: {
|
|
1382
|
+
prompt_tokens: usage?.prompt_tokens ?? 0,
|
|
1383
|
+
completion_tokens: usage?.completion_tokens ?? 0,
|
|
1384
|
+
total_tokens: usage?.total_tokens ?? 0,
|
|
1385
|
+
time_cost: timeCost ?? 0
|
|
1386
|
+
}
|
|
1387
|
+
};
|
|
1361
1388
|
}
|
|
1362
1389
|
async function callToGetJSONObject(messages, AIActionTypeValue) {
|
|
1363
1390
|
let responseFormat;
|
|
@@ -1435,6 +1462,325 @@ function safeParseJson(input) {
|
|
|
1435
1462
|
throw Error(`failed to parse json response: ${input}`);
|
|
1436
1463
|
}
|
|
1437
1464
|
|
|
1465
|
+
// src/ai-model/prompt/playwright-generator.ts
|
|
1466
|
+
import { PLAYWRIGHT_EXAMPLE_CODE } from "@midscene/shared/constants";
|
|
1467
|
+
var getScreenshotsForLLM = (events, maxScreenshots = 1) => {
|
|
1468
|
+
const eventsWithScreenshots = events.filter(
|
|
1469
|
+
(event) => event.screenshotBefore || event.screenshotAfter || event.screenshotWithBox
|
|
1470
|
+
);
|
|
1471
|
+
const sortedEvents = [...eventsWithScreenshots].sort((a, b) => {
|
|
1472
|
+
if (a.type === "navigation" && b.type !== "navigation")
|
|
1473
|
+
return -1;
|
|
1474
|
+
if (a.type !== "navigation" && b.type === "navigation")
|
|
1475
|
+
return 1;
|
|
1476
|
+
if (a.type === "click" && b.type !== "click")
|
|
1477
|
+
return -1;
|
|
1478
|
+
if (a.type !== "click" && b.type === "click")
|
|
1479
|
+
return 1;
|
|
1480
|
+
return 0;
|
|
1481
|
+
});
|
|
1482
|
+
const screenshots = [];
|
|
1483
|
+
for (const event of sortedEvents) {
|
|
1484
|
+
const screenshot = event.screenshotWithBox || event.screenshotAfter || event.screenshotBefore;
|
|
1485
|
+
if (screenshot && !screenshots.includes(screenshot)) {
|
|
1486
|
+
screenshots.push(screenshot);
|
|
1487
|
+
if (screenshots.length >= maxScreenshots)
|
|
1488
|
+
break;
|
|
1489
|
+
}
|
|
1490
|
+
}
|
|
1491
|
+
return screenshots;
|
|
1492
|
+
};
|
|
1493
|
+
var filterEventsByType = (events) => {
|
|
1494
|
+
return {
|
|
1495
|
+
navigationEvents: events.filter((event) => event.type === "navigation"),
|
|
1496
|
+
clickEvents: events.filter((event) => event.type === "click"),
|
|
1497
|
+
inputEvents: events.filter((event) => event.type === "input"),
|
|
1498
|
+
scrollEvents: events.filter((event) => event.type === "scroll")
|
|
1499
|
+
};
|
|
1500
|
+
};
|
|
1501
|
+
var createEventCounts = (filteredEvents, totalEvents) => {
|
|
1502
|
+
return {
|
|
1503
|
+
navigation: filteredEvents.navigationEvents.length,
|
|
1504
|
+
click: filteredEvents.clickEvents.length,
|
|
1505
|
+
input: filteredEvents.inputEvents.length,
|
|
1506
|
+
scroll: filteredEvents.scrollEvents.length,
|
|
1507
|
+
total: totalEvents
|
|
1508
|
+
};
|
|
1509
|
+
};
|
|
1510
|
+
var extractInputDescriptions = (inputEvents) => {
|
|
1511
|
+
return inputEvents.map((event) => ({
|
|
1512
|
+
description: event.elementDescription || "",
|
|
1513
|
+
value: event.value || ""
|
|
1514
|
+
})).filter((item) => item.description && item.value);
|
|
1515
|
+
};
|
|
1516
|
+
var processEventsForLLM = (events) => {
|
|
1517
|
+
return events.map((event) => ({
|
|
1518
|
+
type: event.type,
|
|
1519
|
+
timestamp: event.timestamp,
|
|
1520
|
+
url: event.url,
|
|
1521
|
+
title: event.title,
|
|
1522
|
+
elementDescription: event.elementDescription,
|
|
1523
|
+
value: event.value,
|
|
1524
|
+
pageInfo: event.pageInfo,
|
|
1525
|
+
elementRect: event.elementRect
|
|
1526
|
+
}));
|
|
1527
|
+
};
|
|
1528
|
+
var prepareEventSummary = (events, options = {}) => {
|
|
1529
|
+
const filteredEvents = filterEventsByType(events);
|
|
1530
|
+
const eventCounts = createEventCounts(filteredEvents, events.length);
|
|
1531
|
+
const startUrl = filteredEvents.navigationEvents.length > 0 ? filteredEvents.navigationEvents[0].url || "" : "";
|
|
1532
|
+
const pageTitles = filteredEvents.navigationEvents.map((event) => event.title).filter((title) => Boolean(title)).slice(0, 5);
|
|
1533
|
+
const clickDescriptions = filteredEvents.clickEvents.map((event) => event.elementDescription).filter((desc) => Boolean(desc)).slice(0, 10);
|
|
1534
|
+
const inputDescriptions = extractInputDescriptions(
|
|
1535
|
+
filteredEvents.inputEvents
|
|
1536
|
+
).slice(0, 10);
|
|
1537
|
+
const urls = filteredEvents.navigationEvents.map((e) => e.url).filter((url) => Boolean(url)).slice(0, 5);
|
|
1538
|
+
const processedEvents = processEventsForLLM(events);
|
|
1539
|
+
return {
|
|
1540
|
+
testName: options.testName || "Automated test from recorded events",
|
|
1541
|
+
startUrl,
|
|
1542
|
+
eventCounts,
|
|
1543
|
+
pageTitles,
|
|
1544
|
+
urls,
|
|
1545
|
+
clickDescriptions,
|
|
1546
|
+
inputDescriptions,
|
|
1547
|
+
events: processedEvents
|
|
1548
|
+
};
|
|
1549
|
+
};
|
|
1550
|
+
var createMessageContent = (promptText, screenshots = [], includeScreenshots = true) => {
|
|
1551
|
+
const messageContent = [
|
|
1552
|
+
{
|
|
1553
|
+
type: "text",
|
|
1554
|
+
text: promptText
|
|
1555
|
+
}
|
|
1556
|
+
];
|
|
1557
|
+
if (includeScreenshots && screenshots.length > 0) {
|
|
1558
|
+
messageContent.unshift({
|
|
1559
|
+
type: "text",
|
|
1560
|
+
text: "Here are screenshots from the recording session to help you understand the context:"
|
|
1561
|
+
});
|
|
1562
|
+
screenshots.forEach((screenshot) => {
|
|
1563
|
+
messageContent.push({
|
|
1564
|
+
type: "image_url",
|
|
1565
|
+
image_url: {
|
|
1566
|
+
url: screenshot
|
|
1567
|
+
}
|
|
1568
|
+
});
|
|
1569
|
+
});
|
|
1570
|
+
}
|
|
1571
|
+
return messageContent;
|
|
1572
|
+
};
|
|
1573
|
+
var validateEvents = (events) => {
|
|
1574
|
+
if (!events.length) {
|
|
1575
|
+
throw new Error("No events provided for test generation");
|
|
1576
|
+
}
|
|
1577
|
+
};
|
|
1578
|
+
var generatePlaywrightTest = async (events, options = {}) => {
|
|
1579
|
+
validateEvents(events);
|
|
1580
|
+
const summary = prepareEventSummary(events, {
|
|
1581
|
+
testName: options.testName,
|
|
1582
|
+
maxScreenshots: options.maxScreenshots || 3
|
|
1583
|
+
});
|
|
1584
|
+
const playwrightSummary = {
|
|
1585
|
+
...summary,
|
|
1586
|
+
waitForNetworkIdle: options.waitForNetworkIdle !== false,
|
|
1587
|
+
waitForNetworkIdleTimeout: options.waitForNetworkIdleTimeout || 2e3,
|
|
1588
|
+
viewportSize: options.viewportSize || { width: 1280, height: 800 }
|
|
1589
|
+
};
|
|
1590
|
+
const screenshots = getScreenshotsForLLM(events, options.maxScreenshots || 3);
|
|
1591
|
+
const promptText = `Generate a Playwright test using @midscene/web/playwright that reproduces this recorded browser session. The test should be based on the following events and follow the structure of the example provided. Make the test descriptive with appropriate assertions and validations.
|
|
1592
|
+
|
|
1593
|
+
Event Summary:
|
|
1594
|
+
${JSON.stringify(playwrightSummary, null, 2)}
|
|
1595
|
+
|
|
1596
|
+
Generated code should:
|
|
1597
|
+
1. Import required dependencies
|
|
1598
|
+
2. Set up the test with proper configuration
|
|
1599
|
+
3. Include a beforeEach hook to navigate to the starting URL
|
|
1600
|
+
4. Implement a test that uses Midscene AI methods (aiTap, aiInput, aiAssert, etc.)
|
|
1601
|
+
5. Include appropriate assertions and validations
|
|
1602
|
+
6. Follow best practices for Playwright tests
|
|
1603
|
+
7. Be ready to execute without further modification
|
|
1604
|
+
|
|
1605
|
+
Respond ONLY with the complete Playwright test code, no explanations.`;
|
|
1606
|
+
const messageContent = createMessageContent(
|
|
1607
|
+
promptText,
|
|
1608
|
+
screenshots,
|
|
1609
|
+
options.includeScreenshots !== false
|
|
1610
|
+
);
|
|
1611
|
+
const systemPrompt = `You are an expert test automation engineer specializing in Playwright and Midscene.
|
|
1612
|
+
Your task is to generate a complete, executable Playwright test using @midscene/web/playwright that reproduces a recorded browser session.
|
|
1613
|
+
|
|
1614
|
+
${PLAYWRIGHT_EXAMPLE_CODE}`;
|
|
1615
|
+
const prompt = [
|
|
1616
|
+
{
|
|
1617
|
+
role: "system",
|
|
1618
|
+
content: systemPrompt
|
|
1619
|
+
},
|
|
1620
|
+
{
|
|
1621
|
+
role: "user",
|
|
1622
|
+
content: messageContent
|
|
1623
|
+
}
|
|
1624
|
+
];
|
|
1625
|
+
const response = await call(prompt, 2 /* EXTRACT_DATA */);
|
|
1626
|
+
if (response?.content && typeof response.content === "string") {
|
|
1627
|
+
return response.content;
|
|
1628
|
+
}
|
|
1629
|
+
throw new Error("Failed to generate Playwright test code");
|
|
1630
|
+
};
|
|
1631
|
+
|
|
1632
|
+
// src/ai-model/prompt/yaml-generator.ts
|
|
1633
|
+
import { YAML_EXAMPLE_CODE } from "@midscene/shared/constants";
|
|
1634
|
+
var getScreenshotsForLLM2 = (events, maxScreenshots = 1) => {
|
|
1635
|
+
const eventsWithScreenshots = events.filter(
|
|
1636
|
+
(event) => event.screenshotBefore || event.screenshotAfter || event.screenshotWithBox
|
|
1637
|
+
);
|
|
1638
|
+
const sortedEvents = [...eventsWithScreenshots].sort((a, b) => {
|
|
1639
|
+
if (a.type === "navigation" && b.type !== "navigation")
|
|
1640
|
+
return -1;
|
|
1641
|
+
if (a.type !== "navigation" && b.type === "navigation")
|
|
1642
|
+
return 1;
|
|
1643
|
+
if (a.type === "click" && b.type !== "click")
|
|
1644
|
+
return -1;
|
|
1645
|
+
if (a.type !== "click" && b.type === "click")
|
|
1646
|
+
return 1;
|
|
1647
|
+
return 0;
|
|
1648
|
+
});
|
|
1649
|
+
const screenshots = [];
|
|
1650
|
+
for (const event of sortedEvents) {
|
|
1651
|
+
const screenshot = event.screenshotWithBox || event.screenshotAfter || event.screenshotBefore;
|
|
1652
|
+
if (screenshot && !screenshots.includes(screenshot)) {
|
|
1653
|
+
screenshots.push(screenshot);
|
|
1654
|
+
if (screenshots.length >= maxScreenshots)
|
|
1655
|
+
break;
|
|
1656
|
+
}
|
|
1657
|
+
}
|
|
1658
|
+
return screenshots;
|
|
1659
|
+
};
|
|
1660
|
+
var filterEventsByType2 = (events) => {
|
|
1661
|
+
return {
|
|
1662
|
+
navigationEvents: events.filter((event) => event.type === "navigation"),
|
|
1663
|
+
clickEvents: events.filter((event) => event.type === "click"),
|
|
1664
|
+
inputEvents: events.filter((event) => event.type === "input"),
|
|
1665
|
+
scrollEvents: events.filter((event) => event.type === "scroll")
|
|
1666
|
+
};
|
|
1667
|
+
};
|
|
1668
|
+
var createEventCounts2 = (filteredEvents, totalEvents) => {
|
|
1669
|
+
return {
|
|
1670
|
+
navigation: filteredEvents.navigationEvents.length,
|
|
1671
|
+
click: filteredEvents.clickEvents.length,
|
|
1672
|
+
input: filteredEvents.inputEvents.length,
|
|
1673
|
+
scroll: filteredEvents.scrollEvents.length,
|
|
1674
|
+
total: totalEvents
|
|
1675
|
+
};
|
|
1676
|
+
};
|
|
1677
|
+
var extractInputDescriptions2 = (inputEvents) => {
|
|
1678
|
+
return inputEvents.map((event) => ({
|
|
1679
|
+
description: event.elementDescription || "",
|
|
1680
|
+
value: event.value || ""
|
|
1681
|
+
})).filter((item) => item.description && item.value);
|
|
1682
|
+
};
|
|
1683
|
+
var processEventsForLLM2 = (events) => {
|
|
1684
|
+
return events.map((event) => ({
|
|
1685
|
+
type: event.type,
|
|
1686
|
+
timestamp: event.timestamp,
|
|
1687
|
+
url: event.url,
|
|
1688
|
+
title: event.title,
|
|
1689
|
+
elementDescription: event.elementDescription,
|
|
1690
|
+
value: event.value,
|
|
1691
|
+
pageInfo: event.pageInfo,
|
|
1692
|
+
elementRect: event.elementRect
|
|
1693
|
+
}));
|
|
1694
|
+
};
|
|
1695
|
+
var prepareEventSummary2 = (events, options = {}) => {
|
|
1696
|
+
const filteredEvents = filterEventsByType2(events);
|
|
1697
|
+
const eventCounts = createEventCounts2(filteredEvents, events.length);
|
|
1698
|
+
const startUrl = filteredEvents.navigationEvents.length > 0 ? filteredEvents.navigationEvents[0].url || "" : "";
|
|
1699
|
+
const pageTitles = filteredEvents.navigationEvents.map((event) => event.title).filter((title) => Boolean(title)).slice(0, 5);
|
|
1700
|
+
const clickDescriptions = filteredEvents.clickEvents.map((event) => event.elementDescription).filter((desc) => Boolean(desc)).slice(0, 10);
|
|
1701
|
+
const inputDescriptions = extractInputDescriptions2(
|
|
1702
|
+
filteredEvents.inputEvents
|
|
1703
|
+
).slice(0, 10);
|
|
1704
|
+
const urls = filteredEvents.navigationEvents.map((e) => e.url).filter((url) => Boolean(url)).slice(0, 5);
|
|
1705
|
+
const processedEvents = processEventsForLLM2(events);
|
|
1706
|
+
return {
|
|
1707
|
+
testName: options.testName || "Automated test from recorded events",
|
|
1708
|
+
startUrl,
|
|
1709
|
+
eventCounts,
|
|
1710
|
+
pageTitles,
|
|
1711
|
+
urls,
|
|
1712
|
+
clickDescriptions,
|
|
1713
|
+
inputDescriptions,
|
|
1714
|
+
events: processedEvents
|
|
1715
|
+
};
|
|
1716
|
+
};
|
|
1717
|
+
var validateEvents2 = (events) => {
|
|
1718
|
+
if (!events.length) {
|
|
1719
|
+
throw new Error("No events provided for test generation");
|
|
1720
|
+
}
|
|
1721
|
+
};
|
|
1722
|
+
var generateYamlTest = async (events, options = {}) => {
|
|
1723
|
+
try {
|
|
1724
|
+
validateEvents2(events);
|
|
1725
|
+
const summary = prepareEventSummary2(events, {
|
|
1726
|
+
testName: options.testName,
|
|
1727
|
+
maxScreenshots: options.maxScreenshots || 3
|
|
1728
|
+
});
|
|
1729
|
+
const yamlSummary = {
|
|
1730
|
+
...summary,
|
|
1731
|
+
includeTimestamps: options.includeTimestamps || false
|
|
1732
|
+
};
|
|
1733
|
+
const screenshots = getScreenshotsForLLM2(
|
|
1734
|
+
events,
|
|
1735
|
+
options.maxScreenshots || 3
|
|
1736
|
+
);
|
|
1737
|
+
const prompt = [
|
|
1738
|
+
{
|
|
1739
|
+
role: "system",
|
|
1740
|
+
content: `You are an expert in Midscene.js YAML test generation. Generate clean, accurate YAML following these rules: ${YAML_EXAMPLE_CODE}`
|
|
1741
|
+
},
|
|
1742
|
+
{
|
|
1743
|
+
role: "user",
|
|
1744
|
+
content: `Generate YAML test for Midscene.js automation from recorded browser events.
|
|
1745
|
+
|
|
1746
|
+
Event Summary:
|
|
1747
|
+
${JSON.stringify(yamlSummary, null, 2)}
|
|
1748
|
+
|
|
1749
|
+
Convert events:
|
|
1750
|
+
- navigation → target.url
|
|
1751
|
+
- click → aiTap with element description
|
|
1752
|
+
- input → aiInput with value and locate
|
|
1753
|
+
- scroll → aiScroll with appropriate direction
|
|
1754
|
+
- Add aiAssert for important state changes
|
|
1755
|
+
|
|
1756
|
+
Respond with YAML only, no explanations.`
|
|
1757
|
+
}
|
|
1758
|
+
];
|
|
1759
|
+
if (screenshots.length > 0) {
|
|
1760
|
+
prompt.push({
|
|
1761
|
+
role: "user",
|
|
1762
|
+
content: "Here are screenshots from the recording session to help you understand the context:"
|
|
1763
|
+
});
|
|
1764
|
+
prompt.push({
|
|
1765
|
+
role: "user",
|
|
1766
|
+
content: screenshots.map((screenshot) => ({
|
|
1767
|
+
type: "image_url",
|
|
1768
|
+
image_url: {
|
|
1769
|
+
url: screenshot
|
|
1770
|
+
}
|
|
1771
|
+
}))
|
|
1772
|
+
});
|
|
1773
|
+
}
|
|
1774
|
+
const response = await call(prompt, 2 /* EXTRACT_DATA */);
|
|
1775
|
+
if (response?.content && typeof response.content === "string") {
|
|
1776
|
+
return response.content;
|
|
1777
|
+
}
|
|
1778
|
+
throw new Error("Failed to generate YAML test configuration");
|
|
1779
|
+
} catch (error) {
|
|
1780
|
+
throw new Error(`Failed to generate YAML test: ${error}`);
|
|
1781
|
+
}
|
|
1782
|
+
};
|
|
1783
|
+
|
|
1438
1784
|
// src/ai-model/inspect.ts
|
|
1439
1785
|
import {
|
|
1440
1786
|
MIDSCENE_USE_QWEN_VL as MIDSCENE_USE_QWEN_VL2,
|
|
@@ -2215,10 +2561,14 @@ export {
|
|
|
2215
2561
|
systemPromptToLocateElement,
|
|
2216
2562
|
elementByPositionWithElementInfo,
|
|
2217
2563
|
describeUserPage,
|
|
2564
|
+
call,
|
|
2218
2565
|
callToGetJSONObject,
|
|
2566
|
+
AIActionType,
|
|
2219
2567
|
callAiFn,
|
|
2220
2568
|
adaptBboxToRect,
|
|
2221
2569
|
expandSearchArea,
|
|
2570
|
+
generatePlaywrightTest,
|
|
2571
|
+
generateYamlTest,
|
|
2222
2572
|
AiLocateElement,
|
|
2223
2573
|
AiLocateSection,
|
|
2224
2574
|
AiExtractElementInfo,
|
|
@@ -2228,4 +2578,4 @@ export {
|
|
|
2228
2578
|
resizeImageForUiTars
|
|
2229
2579
|
};
|
|
2230
2580
|
|
|
2231
|
-
//# sourceMappingURL=chunk-
|
|
2581
|
+
//# sourceMappingURL=chunk-EYIL4AHP.js.map
|