misoai-web 1.0.3 → 1.0.4
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/agent.js +2574 -0
- package/dist/es/agent.js.map +1 -0
- package/dist/es/bridge-mode-browser.js +955 -0
- package/dist/es/bridge-mode-browser.js.map +1 -0
- package/dist/es/bridge-mode.js +2935 -0
- package/dist/es/bridge-mode.js.map +1 -0
- package/dist/es/chrome-extension.js +3322 -0
- package/dist/es/chrome-extension.js.map +1 -0
- package/dist/es/index.js +3190 -0
- package/dist/es/index.js.map +1 -0
- package/dist/es/midscene-playground.js +2910 -0
- package/dist/es/midscene-playground.js.map +1 -0
- package/dist/es/midscene-server.js +247 -0
- package/dist/es/midscene-server.js.map +1 -0
- package/dist/es/playground.js +2681 -0
- package/dist/es/playground.js.map +1 -0
- package/dist/es/playwright-report.js +120 -0
- package/dist/es/playwright-report.js.map +1 -0
- package/dist/es/playwright.js +3135 -0
- package/dist/es/playwright.js.map +1 -0
- package/dist/es/puppeteer-agent-launcher.js +3085 -0
- package/dist/es/puppeteer-agent-launcher.js.map +1 -0
- package/dist/es/puppeteer.js +2932 -0
- package/dist/es/puppeteer.js.map +1 -0
- package/dist/es/ui-utils.js +106 -0
- package/dist/es/ui-utils.js.map +1 -0
- package/dist/es/utils.js +197 -0
- package/dist/es/utils.js.map +1 -0
- package/dist/es/yaml.js +351 -0
- package/dist/es/yaml.js.map +1 -0
- package/dist/lib/agent.js +2589 -0
- package/dist/lib/agent.js.map +1 -0
- package/dist/lib/bridge-mode-browser.js +989 -0
- package/dist/lib/bridge-mode-browser.js.map +1 -0
- package/dist/lib/bridge-mode.js +2955 -0
- package/dist/lib/bridge-mode.js.map +1 -0
- package/dist/lib/chrome-extension.js +3339 -0
- package/dist/lib/chrome-extension.js.map +1 -0
- package/dist/lib/index.js +3206 -0
- package/dist/lib/index.js.map +1 -0
- package/dist/lib/midscene-playground.js +2914 -0
- package/dist/lib/midscene-playground.js.map +1 -0
- package/dist/lib/midscene-server.js +273 -0
- package/dist/lib/midscene-server.js.map +1 -0
- package/dist/lib/playground.js +2700 -0
- package/dist/lib/playground.js.map +1 -0
- package/dist/lib/playwright-report.js +148 -0
- package/dist/lib/playwright-report.js.map +1 -0
- package/dist/lib/playwright.js +3152 -0
- package/dist/lib/playwright.js.map +1 -0
- package/dist/lib/puppeteer-agent-launcher.js +3098 -0
- package/dist/lib/puppeteer-agent-launcher.js.map +1 -0
- package/dist/lib/puppeteer.js +2943 -0
- package/dist/lib/puppeteer.js.map +1 -0
- package/dist/lib/ui-utils.js +137 -0
- package/dist/lib/ui-utils.js.map +1 -0
- package/dist/lib/utils.js +235 -0
- package/dist/lib/utils.js.map +1 -0
- package/dist/lib/yaml.js +390 -0
- package/dist/lib/yaml.js.map +1 -0
- package/dist/types/agent.d.ts +264 -0
- package/dist/types/bridge-mode-browser.d.ts +9 -0
- package/dist/types/bridge-mode.d.ts +40 -0
- package/dist/types/browser-a1877d18.d.ts +37 -0
- package/dist/types/chrome-extension.d.ts +18 -0
- package/dist/types/index.d.ts +16 -0
- package/dist/types/midscene-playground.d.ts +2 -0
- package/dist/types/midscene-server.d.ts +31 -0
- package/dist/types/page-663ece08.d.ts +333 -0
- package/dist/types/playground.d.ts +17 -0
- package/dist/types/playwright-report.d.ts +11 -0
- package/dist/types/playwright.d.ts +87 -0
- package/dist/types/puppeteer-agent-launcher.d.ts +40 -0
- package/dist/types/puppeteer.d.ts +17 -0
- package/dist/types/ui-utils.d.ts +14 -0
- package/dist/types/utils-badc824e.d.ts +34 -0
- package/dist/types/utils.d.ts +8 -0
- package/dist/types/yaml.d.ts +15 -0
- package/iife-script/htmlElement.js +99 -37
- package/iife-script/htmlElementDebug.js +92 -9
- package/package.json +2 -2
@@ -0,0 +1,106 @@
|
|
1
|
+
// src/common/ui-utils.ts
|
2
|
+
function typeStr(task) {
|
3
|
+
return task.subType && task.subType !== "Plan" ? `${task.type} / ${task.subType || ""}` : task.type;
|
4
|
+
}
|
5
|
+
function getKeyCommands(value) {
|
6
|
+
const keys = Array.isArray(value) ? value : [value];
|
7
|
+
return keys.reduce((acc, k) => {
|
8
|
+
const includeMeta = keys.includes("Meta") || keys.includes("Control");
|
9
|
+
if (includeMeta && (k === "a" || k === "A")) {
|
10
|
+
return acc.concat([{ key: k, command: "SelectAll" }]);
|
11
|
+
}
|
12
|
+
if (includeMeta && (k === "c" || k === "C")) {
|
13
|
+
return acc.concat([{ key: k, command: "Copy" }]);
|
14
|
+
}
|
15
|
+
if (includeMeta && (k === "v" || k === "V")) {
|
16
|
+
return acc.concat([{ key: k, command: "Paste" }]);
|
17
|
+
}
|
18
|
+
return acc.concat([{ key: k }]);
|
19
|
+
}, []);
|
20
|
+
}
|
21
|
+
function locateParamStr(locate) {
|
22
|
+
if (!locate) {
|
23
|
+
return "";
|
24
|
+
}
|
25
|
+
if (typeof locate === "string") {
|
26
|
+
return locate;
|
27
|
+
}
|
28
|
+
return locate.prompt;
|
29
|
+
}
|
30
|
+
function scrollParamStr(scrollParam) {
|
31
|
+
if (!scrollParam) {
|
32
|
+
return "";
|
33
|
+
}
|
34
|
+
return `${scrollParam.direction || "down"}, ${scrollParam.scrollType || "once"}, ${scrollParam.distance || "distance-not-set"}`;
|
35
|
+
}
|
36
|
+
function taskTitleStr(type, prompt) {
|
37
|
+
if (prompt) {
|
38
|
+
return `${type} - ${prompt}`;
|
39
|
+
}
|
40
|
+
return type;
|
41
|
+
}
|
42
|
+
function paramStr(task) {
|
43
|
+
let value;
|
44
|
+
if (task.type === "Planning") {
|
45
|
+
value = task?.param?.userInstruction;
|
46
|
+
}
|
47
|
+
if (task.type === "Insight") {
|
48
|
+
value = task?.param?.prompt || task?.param?.id || task?.param?.dataDemand || task?.param?.assertion;
|
49
|
+
}
|
50
|
+
if (task.type === "Action") {
|
51
|
+
const locate = task?.locate;
|
52
|
+
const locateStr = locate ? locateParamStr(locate) : "";
|
53
|
+
value = task.thought || "";
|
54
|
+
if (typeof task?.param?.timeMs === "number") {
|
55
|
+
value = `${task?.param?.timeMs}ms`;
|
56
|
+
} else if (typeof task?.param?.scrollType === "string") {
|
57
|
+
value = scrollParamStr(task?.param);
|
58
|
+
} else if (typeof task?.param?.value !== "undefined") {
|
59
|
+
value = task?.param?.value;
|
60
|
+
}
|
61
|
+
if (locateStr) {
|
62
|
+
if (value) {
|
63
|
+
value = `${locateStr} - ${value}`;
|
64
|
+
} else {
|
65
|
+
value = locateStr;
|
66
|
+
}
|
67
|
+
}
|
68
|
+
}
|
69
|
+
if (typeof value === "undefined")
|
70
|
+
return "";
|
71
|
+
return typeof value === "string" ? value : JSON.stringify(value, void 0, 2);
|
72
|
+
}
|
73
|
+
var limitOpenNewTabScript = `
|
74
|
+
if (!window.__MIDSCENE_NEW_TAB_INTERCEPTOR_INITIALIZED__) {
|
75
|
+
window.__MIDSCENE_NEW_TAB_INTERCEPTOR_INITIALIZED__ = true;
|
76
|
+
|
77
|
+
// Intercept the window.open method (only once)
|
78
|
+
window.open = function(url) {
|
79
|
+
console.log('Blocked window.open:', url);
|
80
|
+
window.location.href = url;
|
81
|
+
return null;
|
82
|
+
};
|
83
|
+
|
84
|
+
// Block all a tag clicks with target="_blank" (only once)
|
85
|
+
document.addEventListener('click', function(e) {
|
86
|
+
const target = e.target.closest('a');
|
87
|
+
if (target && target.target === '_blank') {
|
88
|
+
e.preventDefault();
|
89
|
+
console.log('Blocked new tab:', target.href);
|
90
|
+
window.location.href = target.href;
|
91
|
+
target.removeAttribute('target');
|
92
|
+
}
|
93
|
+
}, true);
|
94
|
+
}
|
95
|
+
`;
|
96
|
+
export {
|
97
|
+
getKeyCommands,
|
98
|
+
limitOpenNewTabScript,
|
99
|
+
locateParamStr,
|
100
|
+
paramStr,
|
101
|
+
scrollParamStr,
|
102
|
+
taskTitleStr,
|
103
|
+
typeStr
|
104
|
+
};
|
105
|
+
|
106
|
+
//# sourceMappingURL=ui-utils.js.map
|
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"mappings":";AAWO,SAAS,QAAQ,MAAqB;AAC3C,SAAO,KAAK,WAAW,KAAK,YAAY,SACpC,GAAG,KAAK,IAAI,MAAM,KAAK,WAAW,EAAE,KACpC,KAAK;AACX;AAEO,SAAS,eACd,OAC0C;AAE1C,QAAM,OAAO,MAAM,QAAQ,KAAK,IAAI,QAAQ,CAAC,KAAK;AAIlD,SAAO,KAAK,OAAO,CAAC,KAA+C,MAAM;AACvE,UAAM,cAAc,KAAK,SAAS,MAAM,KAAK,KAAK,SAAS,SAAS;AACpE,QAAI,gBAAgB,MAAM,OAAO,MAAM,MAAM;AAC3C,aAAO,IAAI,OAAO,CAAC,EAAE,KAAK,GAAG,SAAS,YAAY,CAAC,CAAC;AAAA,IACtD;AACA,QAAI,gBAAgB,MAAM,OAAO,MAAM,MAAM;AAC3C,aAAO,IAAI,OAAO,CAAC,EAAE,KAAK,GAAG,SAAS,OAAO,CAAC,CAAC;AAAA,IACjD;AACA,QAAI,gBAAgB,MAAM,OAAO,MAAM,MAAM;AAC3C,aAAO,IAAI,OAAO,CAAC,EAAE,KAAK,GAAG,SAAS,QAAQ,CAAC,CAAC;AAAA,IAClD;AACA,WAAO,IAAI,OAAO,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC;AAAA,EAChC,GAAG,CAAC,CAAC;AACP;AAEO,SAAS,eAAe,QAA8B;AAC3D,MAAI,CAAC,QAAQ;AACX,WAAO;AAAA,EACT;AAEA,MAAI,OAAO,WAAW,UAAU;AAC9B,WAAO;AAAA,EACT;AAEA,SAAO,OAAO;AAChB;AAEO,SAAS,eAAe,aAAyC;AACtE,MAAI,CAAC,aAAa;AAChB,WAAO;AAAA,EACT;AACA,SAAO,GAAG,YAAY,aAAa,MAAM,KAAK,YAAY,cAAc,MAAM,KAAK,YAAY,YAAY,kBAAkB;AAC/H;AAEO,SAAS,aACd,MAcA,QACA;AACA,MAAI,QAAQ;AACV,WAAO,GAAG,IAAI,MAAM,MAAM;AAAA,EAC5B;AACA,SAAO;AACT;AAEO,SAAS,SAAS,MAAqB;AAC5C,MAAI;AACJ,MAAI,KAAK,SAAS,YAAY;AAC5B,YAAS,MAAgC,OAAO;AAAA,EAClD;AAEA,MAAI,KAAK,SAAS,WAAW;AAC3B,YACG,MAAqC,OAAO,UAC5C,MAAqC,OAAO,MAC5C,MAAoC,OAAO,cAC3C,MAAwC,OAAO;AAAA,EACpD;AAEA,MAAI,KAAK,SAAS,UAAU;AAC1B,UAAM,SAAU,MAA8B;AAC9C,UAAM,YAAY,SAAS,eAAe,MAAM,IAAI;AAEpD,YAAQ,KAAK,WAAW;AACxB,QAAI,OAAQ,MAA8B,OAAO,WAAW,UAAU;AACpE,cAAQ,GAAI,MAA8B,OAAO,MAAM;AAAA,IACzD,WACE,OAAQ,MAA8B,OAAO,eAAe,UAC5D;AACA,cAAQ,eAAgB,MAA8B,KAAK;AAAA,IAC7D,WACE,OAAQ,MAA8B,OAAO,UAAU,aACvD;AACA,cAAS,MAA8B,OAAO;AAAA,IAChD;AAEA,QAAI,WAAW;AACb,UAAI,OAAO;AACT,gBAAQ,GAAG,SAAS,MAAM,KAAK;AAAA,MACjC,OAAO;AACL,gBAAQ;AAAA,MACV;AAAA,IACF;AAAA,EACF;AAEA,MAAI,OAAO,UAAU;AAAa,WAAO;AACzC,SAAO,OAAO,UAAU,WACpB,QACA,KAAK,UAAU,OAAO,QAAW,CAAC;AACxC;AAEO,IAAM,wBAAwB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA","names":[],"ignoreList":[],"sources":["../../src/common/ui-utils.ts"],"sourcesContent":["import type {\r\n DetailedLocateParam,\r\n ExecutionTask,\r\n ExecutionTaskAction,\r\n ExecutionTaskInsightAssertion,\r\n ExecutionTaskInsightLocate,\r\n ExecutionTaskInsightQuery,\r\n ExecutionTaskPlanning,\r\n PlanningActionParamScroll,\r\n} from 'misoai-core';\r\n\r\nexport function typeStr(task: ExecutionTask) {\r\n return task.subType && task.subType !== 'Plan'\r\n ? `${task.type} / ${task.subType || ''}`\r\n : task.type;\r\n}\r\n\r\nexport function getKeyCommands(\r\n value: string | string[],\r\n): Array<{ key: string; command?: string }> {\r\n // Ensure value is an array of keys\r\n const keys = Array.isArray(value) ? value : [value];\r\n\r\n // Process each key to attach a corresponding command if needed, based on the presence of 'Meta' or 'Control' in the keys array.\r\n // ref: https://github.com/puppeteer/puppeteer/pull/9357/files#diff-32cf475237b000f980eb214a0a823e45a902bddb7d2426d677cae96397aa0ae4R94\r\n return keys.reduce((acc: Array<{ key: string; command?: string }>, k) => {\r\n const includeMeta = keys.includes('Meta') || keys.includes('Control');\r\n if (includeMeta && (k === 'a' || k === 'A')) {\r\n return acc.concat([{ key: k, command: 'SelectAll' }]);\r\n }\r\n if (includeMeta && (k === 'c' || k === 'C')) {\r\n return acc.concat([{ key: k, command: 'Copy' }]);\r\n }\r\n if (includeMeta && (k === 'v' || k === 'V')) {\r\n return acc.concat([{ key: k, command: 'Paste' }]);\r\n }\r\n return acc.concat([{ key: k }]);\r\n }, []);\r\n}\r\n\r\nexport function locateParamStr(locate?: DetailedLocateParam) {\r\n if (!locate) {\r\n return '';\r\n }\r\n\r\n if (typeof locate === 'string') {\r\n return locate;\r\n }\r\n\r\n return locate.prompt;\r\n}\r\n\r\nexport function scrollParamStr(scrollParam?: PlanningActionParamScroll) {\r\n if (!scrollParam) {\r\n return '';\r\n }\r\n return `${scrollParam.direction || 'down'}, ${scrollParam.scrollType || 'once'}, ${scrollParam.distance || 'distance-not-set'}`;\r\n}\r\n\r\nexport function taskTitleStr(\r\n type:\r\n | 'Tap'\r\n | 'Hover'\r\n | 'Input'\r\n | 'KeyboardPress'\r\n | 'Scroll'\r\n | 'Action'\r\n | 'Query'\r\n | 'Assert'\r\n | 'WaitFor'\r\n | 'Locate'\r\n | 'Boolean'\r\n | 'Number'\r\n | 'String',\r\n prompt: string,\r\n) {\r\n if (prompt) {\r\n return `${type} - ${prompt}`;\r\n }\r\n return type;\r\n}\r\n\r\nexport function paramStr(task: ExecutionTask) {\r\n let value: string | undefined | object;\r\n if (task.type === 'Planning') {\r\n value = (task as ExecutionTaskPlanning)?.param?.userInstruction;\r\n }\r\n\r\n if (task.type === 'Insight') {\r\n value =\r\n (task as ExecutionTaskInsightLocate)?.param?.prompt ||\r\n (task as ExecutionTaskInsightLocate)?.param?.id ||\r\n (task as ExecutionTaskInsightQuery)?.param?.dataDemand ||\r\n (task as ExecutionTaskInsightAssertion)?.param?.assertion;\r\n }\r\n\r\n if (task.type === 'Action') {\r\n const locate = (task as ExecutionTaskAction)?.locate;\r\n const locateStr = locate ? locateParamStr(locate) : '';\r\n\r\n value = task.thought || '';\r\n if (typeof (task as ExecutionTaskAction)?.param?.timeMs === 'number') {\r\n value = `${(task as ExecutionTaskAction)?.param?.timeMs}ms`;\r\n } else if (\r\n typeof (task as ExecutionTaskAction)?.param?.scrollType === 'string'\r\n ) {\r\n value = scrollParamStr((task as ExecutionTaskAction)?.param);\r\n } else if (\r\n typeof (task as ExecutionTaskAction)?.param?.value !== 'undefined'\r\n ) {\r\n value = (task as ExecutionTaskAction)?.param?.value;\r\n }\r\n\r\n if (locateStr) {\r\n if (value) {\r\n value = `${locateStr} - ${value}`;\r\n } else {\r\n value = locateStr;\r\n }\r\n }\r\n }\r\n\r\n if (typeof value === 'undefined') return '';\r\n return typeof value === 'string'\r\n ? value\r\n : JSON.stringify(value, undefined, 2);\r\n}\r\n\r\nexport const limitOpenNewTabScript = `\r\nif (!window.__MIDSCENE_NEW_TAB_INTERCEPTOR_INITIALIZED__) {\r\n window.__MIDSCENE_NEW_TAB_INTERCEPTOR_INITIALIZED__ = true;\r\n\r\n // Intercept the window.open method (only once)\r\n window.open = function(url) {\r\n console.log('Blocked window.open:', url);\r\n window.location.href = url;\r\n return null;\r\n };\r\n\r\n // Block all a tag clicks with target=\"_blank\" (only once)\r\n document.addEventListener('click', function(e) {\r\n const target = e.target.closest('a');\r\n if (target && target.target === '_blank') {\r\n e.preventDefault();\r\n console.log('Blocked new tab:', target.href);\r\n window.location.href = target.href;\r\n target.removeAttribute('target');\r\n }\r\n }, true);\r\n}\r\n`;\r\n"]}
|
package/dist/es/utils.js
ADDED
@@ -0,0 +1,197 @@
|
|
1
|
+
// src/common/utils.ts
|
2
|
+
import { elementByPositionWithElementInfo } from "misoai-core/ai-model";
|
3
|
+
import { uploadTestInfoToServer } from "misoai-core/utils";
|
4
|
+
import { MIDSCENE_REPORT_TAG_NAME, getAIConfig } from "misoai-shared/env";
|
5
|
+
import {
|
6
|
+
generateElementByPosition,
|
7
|
+
getNodeFromCacheList,
|
8
|
+
traverseTree,
|
9
|
+
treeToList
|
10
|
+
} from "misoai-shared/extractor";
|
11
|
+
import { resizeImgBase64 } from "misoai-shared/img";
|
12
|
+
import { assert, logMsg, uuid } from "misoai-shared/utils";
|
13
|
+
import dayjs from "dayjs";
|
14
|
+
|
15
|
+
// src/web-element.ts
|
16
|
+
var WebElementInfo = class {
|
17
|
+
constructor({
|
18
|
+
content,
|
19
|
+
rect,
|
20
|
+
// page,
|
21
|
+
locator,
|
22
|
+
id,
|
23
|
+
attributes,
|
24
|
+
indexId,
|
25
|
+
xpaths
|
26
|
+
}) {
|
27
|
+
this.content = content;
|
28
|
+
this.rect = rect;
|
29
|
+
this.center = [
|
30
|
+
Math.floor(rect.left + rect.width / 2),
|
31
|
+
Math.floor(rect.top + rect.height / 2)
|
32
|
+
];
|
33
|
+
this.locator = locator;
|
34
|
+
this.id = id;
|
35
|
+
this.attributes = attributes;
|
36
|
+
this.indexId = indexId;
|
37
|
+
this.xpaths = xpaths;
|
38
|
+
}
|
39
|
+
};
|
40
|
+
|
41
|
+
// src/common/utils.ts
|
42
|
+
async function parseContextFromWebPage(page, _opt) {
|
43
|
+
assert(page, "page is required");
|
44
|
+
if (page._forceUsePageContext) {
|
45
|
+
return await page._forceUsePageContext();
|
46
|
+
}
|
47
|
+
const url = await page.url();
|
48
|
+
uploadTestInfoToServer({ testUrl: url });
|
49
|
+
let screenshotBase64;
|
50
|
+
let tree;
|
51
|
+
await Promise.all([
|
52
|
+
page.screenshotBase64().then((base64) => {
|
53
|
+
screenshotBase64 = base64;
|
54
|
+
}),
|
55
|
+
page.getElementsNodeTree().then(async (treeRoot) => {
|
56
|
+
tree = treeRoot;
|
57
|
+
})
|
58
|
+
]);
|
59
|
+
const webTree = traverseTree(tree, (elementInfo) => {
|
60
|
+
const { rect, id, content, attributes, locator, indexId } = elementInfo;
|
61
|
+
return new WebElementInfo({
|
62
|
+
rect,
|
63
|
+
locator,
|
64
|
+
id,
|
65
|
+
content,
|
66
|
+
attributes,
|
67
|
+
indexId
|
68
|
+
});
|
69
|
+
});
|
70
|
+
assert(screenshotBase64, "screenshotBase64 is required");
|
71
|
+
const elementsInfo = treeToList(webTree);
|
72
|
+
const size = await page.size();
|
73
|
+
if (size.dpr && size.dpr > 1) {
|
74
|
+
screenshotBase64 = await resizeImgBase64(screenshotBase64, {
|
75
|
+
width: size.width,
|
76
|
+
height: size.height
|
77
|
+
});
|
78
|
+
}
|
79
|
+
return {
|
80
|
+
content: elementsInfo,
|
81
|
+
tree: webTree,
|
82
|
+
size,
|
83
|
+
screenshotBase64,
|
84
|
+
url
|
85
|
+
};
|
86
|
+
}
|
87
|
+
function reportFileName(tag = "web") {
|
88
|
+
const reportTagName = getAIConfig(MIDSCENE_REPORT_TAG_NAME);
|
89
|
+
const dateTimeInFileName = dayjs().format("YYYY-MM-DD_HH-mm-ss");
|
90
|
+
const uniqueId = uuid().substring(0, 8);
|
91
|
+
return `${reportTagName || tag}-${dateTimeInFileName}-${uniqueId}`;
|
92
|
+
}
|
93
|
+
function printReportMsg(filepath) {
|
94
|
+
logMsg(`Midscene - report file updated: ${filepath}`);
|
95
|
+
}
|
96
|
+
function getCurrentExecutionFile(trace) {
|
97
|
+
const error = new Error();
|
98
|
+
const stackTrace = trace || error.stack;
|
99
|
+
const pkgDir = process.cwd() || "";
|
100
|
+
if (stackTrace) {
|
101
|
+
const stackLines = stackTrace.split("\n");
|
102
|
+
for (const line of stackLines) {
|
103
|
+
if (line.includes(".spec.") || line.includes(".test.") || line.includes(".ts") || line.includes(".js")) {
|
104
|
+
const match = line.match(/(?:at\s+)?(.*?\.(?:spec|test)\.[jt]s)/);
|
105
|
+
if (match?.[1]) {
|
106
|
+
const targetFileName = match[1].replace(pkgDir, "").trim().replace("at ", "");
|
107
|
+
return targetFileName;
|
108
|
+
}
|
109
|
+
}
|
110
|
+
}
|
111
|
+
}
|
112
|
+
return false;
|
113
|
+
}
|
114
|
+
var testFileIndex = /* @__PURE__ */ new Map();
|
115
|
+
function generateCacheId(fileName) {
|
116
|
+
let taskFile = fileName || getCurrentExecutionFile();
|
117
|
+
if (!taskFile) {
|
118
|
+
taskFile = uuid();
|
119
|
+
console.warn(
|
120
|
+
"Midscene - using random UUID for cache id. Cache may be invalid."
|
121
|
+
);
|
122
|
+
}
|
123
|
+
if (testFileIndex.has(taskFile)) {
|
124
|
+
const currentIndex = testFileIndex.get(taskFile);
|
125
|
+
if (currentIndex !== void 0) {
|
126
|
+
testFileIndex.set(taskFile, currentIndex + 1);
|
127
|
+
}
|
128
|
+
} else {
|
129
|
+
testFileIndex.set(taskFile, 1);
|
130
|
+
}
|
131
|
+
return `${taskFile}-${testFileIndex.get(taskFile)}`;
|
132
|
+
}
|
133
|
+
var ERROR_CODE_NOT_IMPLEMENTED_AS_DESIGNED = "NOT_IMPLEMENTED_AS_DESIGNED";
|
134
|
+
function replaceIllegalPathCharsAndSpace(str) {
|
135
|
+
return str.replace(/[/\\:*?"<>| ]/g, "-");
|
136
|
+
}
|
137
|
+
function forceClosePopup(page, debug) {
|
138
|
+
page.on("popup", async (popup) => {
|
139
|
+
if (!popup) {
|
140
|
+
console.warn("got a popup event, but the popup is not ready yet, skip");
|
141
|
+
return;
|
142
|
+
}
|
143
|
+
const url = await popup.url();
|
144
|
+
console.log(`Popup opened: ${url}`);
|
145
|
+
if (!popup.isClosed()) {
|
146
|
+
try {
|
147
|
+
await popup.close();
|
148
|
+
} catch (error) {
|
149
|
+
debug(`failed to close popup ${url}, error: ${error}`);
|
150
|
+
}
|
151
|
+
} else {
|
152
|
+
debug(`popup is already closed, skip close ${url}`);
|
153
|
+
}
|
154
|
+
if (!page.isClosed()) {
|
155
|
+
try {
|
156
|
+
await page.goto(url);
|
157
|
+
} catch (error) {
|
158
|
+
debug(`failed to goto ${url}, error: ${error}`);
|
159
|
+
}
|
160
|
+
} else {
|
161
|
+
debug(`page is already closed, skip goto ${url}`);
|
162
|
+
}
|
163
|
+
});
|
164
|
+
}
|
165
|
+
function matchElementFromPlan(planLocateParam, tree) {
|
166
|
+
if (!planLocateParam) {
|
167
|
+
return void 0;
|
168
|
+
}
|
169
|
+
if (planLocateParam.id) {
|
170
|
+
return getNodeFromCacheList(planLocateParam.id);
|
171
|
+
}
|
172
|
+
if (planLocateParam.bbox) {
|
173
|
+
const centerPosition = {
|
174
|
+
x: Math.floor((planLocateParam.bbox[0] + planLocateParam.bbox[2]) / 2),
|
175
|
+
y: Math.floor((planLocateParam.bbox[1] + planLocateParam.bbox[3]) / 2)
|
176
|
+
};
|
177
|
+
let element = elementByPositionWithElementInfo(tree, centerPosition);
|
178
|
+
if (!element) {
|
179
|
+
element = generateElementByPosition(centerPosition);
|
180
|
+
}
|
181
|
+
return element;
|
182
|
+
}
|
183
|
+
return void 0;
|
184
|
+
}
|
185
|
+
export {
|
186
|
+
ERROR_CODE_NOT_IMPLEMENTED_AS_DESIGNED,
|
187
|
+
forceClosePopup,
|
188
|
+
generateCacheId,
|
189
|
+
getCurrentExecutionFile,
|
190
|
+
matchElementFromPlan,
|
191
|
+
parseContextFromWebPage,
|
192
|
+
printReportMsg,
|
193
|
+
replaceIllegalPathCharsAndSpace,
|
194
|
+
reportFileName
|
195
|
+
};
|
196
|
+
|
197
|
+
//# sourceMappingURL=utils.js.map
|
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"mappings":"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","names":[],"ignoreList":[],"sources":["../../src/common/utils.ts","../../src/web-element.ts"],"sourcesContent":["import type { StaticPage } from '@/playground';\r\nimport type {\r\n BaseElement,\r\n ElementTreeNode,\r\n PlanningLocateParam,\r\n PlaywrightParserOpt,\r\n UIContext,\r\n} from 'misoai-core';\r\nimport { elementByPositionWithElementInfo } from 'misoai-core/ai-model';\r\nimport { uploadTestInfoToServer } from 'misoai-core/utils';\r\nimport { MIDSCENE_REPORT_TAG_NAME, getAIConfig } from 'misoai-shared/env';\r\nimport type { ElementInfo } from 'misoai-shared/extractor';\r\nimport {\r\n generateElementByPosition,\r\n getNodeFromCacheList,\r\n traverseTree,\r\n treeToList,\r\n} from 'misoai-shared/extractor';\r\nimport { resizeImgBase64 } from 'misoai-shared/img';\r\nimport type { DebugFunction } from 'misoai-shared/logger';\r\nimport { assert, logMsg, uuid } from 'misoai-shared/utils';\r\nimport dayjs from 'dayjs';\r\nimport type { Page as PlaywrightPage } from 'playwright';\r\nimport type { Page as PuppeteerPage } from 'puppeteer';\r\nimport { WebElementInfo } from '../web-element';\r\nimport type { WebPage } from './page';\r\n\r\nexport type WebUIContext = UIContext<WebElementInfo> & {\r\n url: string;\r\n};\r\n\r\nexport async function parseContextFromWebPage(\r\n page: WebPage,\r\n _opt?: PlaywrightParserOpt,\r\n): Promise<WebUIContext> {\r\n assert(page, 'page is required');\r\n if ((page as StaticPage)._forceUsePageContext) {\r\n return await (page as any)._forceUsePageContext();\r\n }\r\n const url = await page.url();\r\n uploadTestInfoToServer({ testUrl: url });\r\n\r\n let screenshotBase64: string;\r\n let tree: ElementTreeNode<ElementInfo>;\r\n\r\n await Promise.all([\r\n page.screenshotBase64().then((base64) => {\r\n screenshotBase64 = base64;\r\n }),\r\n page.getElementsNodeTree().then(async (treeRoot) => {\r\n tree = treeRoot;\r\n }),\r\n ]);\r\n\r\n const webTree = traverseTree(tree!, (elementInfo) => {\r\n const { rect, id, content, attributes, locator, indexId } = elementInfo;\r\n return new WebElementInfo({\r\n rect,\r\n locator,\r\n id,\r\n content,\r\n attributes,\r\n indexId,\r\n });\r\n });\r\n\r\n assert(screenshotBase64!, 'screenshotBase64 is required');\r\n\r\n const elementsInfo = treeToList(webTree);\r\n const size = await page.size();\r\n\r\n if (size.dpr && size.dpr > 1) {\r\n // console.time('resizeImgBase64');\r\n screenshotBase64 = await resizeImgBase64(screenshotBase64, {\r\n width: size.width,\r\n height: size.height,\r\n });\r\n // console.timeEnd('resizeImgBase64');\r\n }\r\n\r\n return {\r\n content: elementsInfo!,\r\n tree: webTree,\r\n size,\r\n screenshotBase64: screenshotBase64!,\r\n url,\r\n };\r\n}\r\n\r\nexport function reportFileName(tag = 'web') {\r\n const reportTagName = getAIConfig(MIDSCENE_REPORT_TAG_NAME);\r\n const dateTimeInFileName = dayjs().format('YYYY-MM-DD_HH-mm-ss');\r\n // ensure uniqueness at the same time\r\n const uniqueId = uuid().substring(0, 8);\r\n return `${reportTagName || tag}-${dateTimeInFileName}-${uniqueId}`;\r\n}\r\n\r\nexport function printReportMsg(filepath: string) {\r\n logMsg(`Midscene - report file updated: ${filepath}`);\r\n}\r\n\r\n/**\r\n * Get the current execution file name\r\n * @returns The name of the current execution file\r\n */\r\nexport function getCurrentExecutionFile(trace?: string): string | false {\r\n const error = new Error();\r\n const stackTrace = trace || error.stack;\r\n const pkgDir = process.cwd() || '';\r\n if (stackTrace) {\r\n const stackLines = stackTrace.split('\\n');\r\n for (const line of stackLines) {\r\n if (\r\n line.includes('.spec.') ||\r\n line.includes('.test.') ||\r\n line.includes('.ts') ||\r\n line.includes('.js')\r\n ) {\r\n const match = line.match(/(?:at\\s+)?(.*?\\.(?:spec|test)\\.[jt]s)/);\r\n if (match?.[1]) {\r\n const targetFileName = match[1]\r\n .replace(pkgDir, '')\r\n .trim()\r\n .replace('at ', '');\r\n return targetFileName;\r\n }\r\n }\r\n }\r\n }\r\n return false;\r\n}\r\n\r\nconst testFileIndex = new Map<string, number>();\r\n\r\nexport function generateCacheId(fileName?: string): string {\r\n let taskFile = fileName || getCurrentExecutionFile();\r\n if (!taskFile) {\r\n taskFile = uuid();\r\n console.warn(\r\n 'Midscene - using random UUID for cache id. Cache may be invalid.',\r\n );\r\n }\r\n\r\n if (testFileIndex.has(taskFile)) {\r\n const currentIndex = testFileIndex.get(taskFile);\r\n if (currentIndex !== undefined) {\r\n testFileIndex.set(taskFile, currentIndex + 1);\r\n }\r\n } else {\r\n testFileIndex.set(taskFile, 1);\r\n }\r\n return `${taskFile}-${testFileIndex.get(taskFile)}`;\r\n}\r\n\r\nexport const ERROR_CODE_NOT_IMPLEMENTED_AS_DESIGNED =\r\n 'NOT_IMPLEMENTED_AS_DESIGNED';\r\n\r\nexport function replaceIllegalPathCharsAndSpace(str: string) {\r\n return str.replace(/[/\\\\:*?\"<>| ]/g, '-');\r\n}\r\n\r\nexport function forceClosePopup(\r\n page: PuppeteerPage | PlaywrightPage,\r\n debug: DebugFunction,\r\n) {\r\n page.on('popup', async (popup) => {\r\n if (!popup) {\r\n console.warn('got a popup event, but the popup is not ready yet, skip');\r\n return;\r\n }\r\n const url = await (popup as PuppeteerPage).url();\r\n console.log(`Popup opened: ${url}`);\r\n if (!(popup as PuppeteerPage).isClosed()) {\r\n try {\r\n await (popup as PuppeteerPage).close(); // Close the newly opened TAB\r\n } catch (error) {\r\n debug(`failed to close popup ${url}, error: ${error}`);\r\n }\r\n } else {\r\n debug(`popup is already closed, skip close ${url}`);\r\n }\r\n\r\n if (!page.isClosed()) {\r\n try {\r\n await page.goto(url);\r\n } catch (error) {\r\n debug(`failed to goto ${url}, error: ${error}`);\r\n }\r\n } else {\r\n debug(`page is already closed, skip goto ${url}`);\r\n }\r\n });\r\n}\r\n\r\nexport function matchElementFromPlan(\r\n planLocateParam: PlanningLocateParam,\r\n tree: ElementTreeNode<BaseElement>,\r\n) {\r\n if (!planLocateParam) {\r\n return undefined;\r\n }\r\n if (planLocateParam.id) {\r\n return getNodeFromCacheList(planLocateParam.id);\r\n }\r\n\r\n if (planLocateParam.bbox) {\r\n const centerPosition = {\r\n x: Math.floor((planLocateParam.bbox[0] + planLocateParam.bbox[2]) / 2),\r\n y: Math.floor((planLocateParam.bbox[1] + planLocateParam.bbox[3]) / 2),\r\n };\r\n let element = elementByPositionWithElementInfo(tree, centerPosition);\r\n\r\n if (!element) {\r\n element = generateElementByPosition(centerPosition) as BaseElement;\r\n }\r\n\r\n return element;\r\n }\r\n\r\n return undefined;\r\n}\r\n","import type { BaseElement, Rect } from 'misoai-core';\r\nimport type { NodeType } from 'misoai-shared/constants';\r\nexport interface WebElementInfoType extends BaseElement {\r\n id: string;\r\n locator: string;\r\n attributes: {\r\n nodeType: NodeType;\r\n [key: string]: string;\r\n };\r\n}\r\n\r\nexport class WebElementInfo implements BaseElement {\r\n content: string;\r\n\r\n locator?: string;\r\n\r\n rect: Rect;\r\n\r\n center: [number, number];\r\n\r\n // page: WebPage;\r\n\r\n id: string;\r\n\r\n indexId: number;\r\n\r\n attributes: {\r\n nodeType: NodeType;\r\n [key: string]: string;\r\n };\r\n\r\n xpaths?: string[];\r\n\r\n constructor({\r\n content,\r\n rect,\r\n // page,\r\n locator,\r\n id,\r\n attributes,\r\n indexId,\r\n xpaths,\r\n }: {\r\n content: string;\r\n rect: Rect;\r\n // page: WebPage;\r\n locator?: string;\r\n id: string;\r\n attributes: {\r\n nodeType: NodeType;\r\n [key: string]: string;\r\n };\r\n indexId: number;\r\n xpaths?: string[];\r\n }) {\r\n this.content = content;\r\n this.rect = rect;\r\n this.center = [\r\n Math.floor(rect.left + rect.width / 2),\r\n Math.floor(rect.top + rect.height / 2),\r\n ];\r\n // this.page = page;\r\n this.locator = locator;\r\n this.id = id;\r\n this.attributes = attributes;\r\n this.indexId = indexId;\r\n this.xpaths = xpaths;\r\n }\r\n}\r\n"]}
|