misoai-web 1.0.1 → 1.0.3
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/LICENSE +21 -0
- package/README.md +8 -8
- package/bin/midscene-playground +2 -2
- package/package.json +23 -24
- package/dist/es/agent.js +0 -2451
- package/dist/es/agent.js.map +0 -1
- package/dist/es/bridge-mode-browser.js +0 -908
- package/dist/es/bridge-mode-browser.js.map +0 -1
- package/dist/es/bridge-mode.js +0 -2812
- package/dist/es/bridge-mode.js.map +0 -1
- package/dist/es/chrome-extension.js +0 -3152
- package/dist/es/chrome-extension.js.map +0 -1
- package/dist/es/index.js +0 -3052
- package/dist/es/index.js.map +0 -1
- package/dist/es/midscene-playground.js +0 -2781
- package/dist/es/midscene-playground.js.map +0 -1
- package/dist/es/midscene-server.js +0 -247
- package/dist/es/midscene-server.js.map +0 -1
- package/dist/es/playground.js +0 -2552
- package/dist/es/playground.js.map +0 -1
- package/dist/es/playwright-report.js +0 -120
- package/dist/es/playwright-report.js.map +0 -1
- package/dist/es/playwright.js +0 -2997
- package/dist/es/playwright.js.map +0 -1
- package/dist/es/puppeteer-agent-launcher.js +0 -2947
- package/dist/es/puppeteer-agent-launcher.js.map +0 -1
- package/dist/es/puppeteer.js +0 -2794
- package/dist/es/puppeteer.js.map +0 -1
- package/dist/es/ui-utils.js +0 -106
- package/dist/es/ui-utils.js.map +0 -1
- package/dist/es/utils.js +0 -197
- package/dist/es/utils.js.map +0 -1
- package/dist/es/yaml.js +0 -333
- package/dist/es/yaml.js.map +0 -1
- package/dist/lib/agent.js +0 -2466
- package/dist/lib/agent.js.map +0 -1
- package/dist/lib/bridge-mode-browser.js +0 -942
- package/dist/lib/bridge-mode-browser.js.map +0 -1
- package/dist/lib/bridge-mode.js +0 -2832
- package/dist/lib/bridge-mode.js.map +0 -1
- package/dist/lib/chrome-extension.js +0 -3169
- package/dist/lib/chrome-extension.js.map +0 -1
- package/dist/lib/index.js +0 -3071
- package/dist/lib/index.js.map +0 -1
- package/dist/lib/midscene-playground.js +0 -2785
- package/dist/lib/midscene-playground.js.map +0 -1
- package/dist/lib/midscene-server.js +0 -273
- package/dist/lib/midscene-server.js.map +0 -1
- package/dist/lib/playground.js +0 -2571
- package/dist/lib/playground.js.map +0 -1
- package/dist/lib/playwright-report.js +0 -148
- package/dist/lib/playwright-report.js.map +0 -1
- package/dist/lib/playwright.js +0 -3017
- package/dist/lib/playwright.js.map +0 -1
- package/dist/lib/puppeteer-agent-launcher.js +0 -2963
- package/dist/lib/puppeteer-agent-launcher.js.map +0 -1
- package/dist/lib/puppeteer.js +0 -2808
- package/dist/lib/puppeteer.js.map +0 -1
- package/dist/lib/ui-utils.js +0 -137
- package/dist/lib/ui-utils.js.map +0 -1
- package/dist/lib/utils.js +0 -235
- package/dist/lib/utils.js.map +0 -1
- package/dist/lib/yaml.js +0 -372
- package/dist/lib/yaml.js.map +0 -1
- package/dist/types/agent.d.ts +0 -254
- package/dist/types/bridge-mode-browser.d.ts +0 -9
- package/dist/types/bridge-mode.d.ts +0 -40
- package/dist/types/browser-d447695b.d.ts +0 -37
- package/dist/types/chrome-extension.d.ts +0 -18
- package/dist/types/index.d.ts +0 -16
- package/dist/types/midscene-playground.d.ts +0 -2
- package/dist/types/midscene-server.d.ts +0 -31
- package/dist/types/page-b8ada1f3.d.ts +0 -322
- package/dist/types/playground.d.ts +0 -17
- package/dist/types/playwright-report.d.ts +0 -11
- package/dist/types/playwright.d.ts +0 -87
- package/dist/types/puppeteer-agent-launcher.d.ts +0 -40
- package/dist/types/puppeteer.d.ts +0 -17
- package/dist/types/ui-utils.d.ts +0 -14
- package/dist/types/utils-badc824e.d.ts +0 -34
- package/dist/types/utils.d.ts +0 -8
- package/dist/types/yaml.d.ts +0 -15
@@ -1,247 +0,0 @@
|
|
1
|
-
// src/playground/server.ts
|
2
|
-
import { randomUUID } from "crypto";
|
3
|
-
import { existsSync, readFileSync, writeFileSync } from "fs";
|
4
|
-
import { join } from "path";
|
5
|
-
|
6
|
-
// src/common/utils.ts
|
7
|
-
import { elementByPositionWithElementInfo } from "misoai-core/ai-model";
|
8
|
-
import { uploadTestInfoToServer } from "misoai-core/utils";
|
9
|
-
import { MIDSCENE_REPORT_TAG_NAME, getAIConfig } from "misoai-shared/env";
|
10
|
-
import {
|
11
|
-
generateElementByPosition,
|
12
|
-
getNodeFromCacheList,
|
13
|
-
traverseTree,
|
14
|
-
treeToList
|
15
|
-
} from "misoai-shared/extractor";
|
16
|
-
import { resizeImgBase64 } from "misoai-shared/img";
|
17
|
-
import { assert, logMsg, uuid } from "misoai-shared/utils";
|
18
|
-
import dayjs from "dayjs";
|
19
|
-
var ERROR_CODE_NOT_IMPLEMENTED_AS_DESIGNED = "NOT_IMPLEMENTED_AS_DESIGNED";
|
20
|
-
|
21
|
-
// src/playground/server.ts
|
22
|
-
import { getTmpDir } from "misoai-core/utils";
|
23
|
-
import { PLAYGROUND_SERVER_PORT } from "misoai-shared/constants";
|
24
|
-
import { overrideAIConfig } from "misoai-shared/env";
|
25
|
-
import { ifInBrowser } from "misoai-shared/utils";
|
26
|
-
import cors from "cors";
|
27
|
-
import dotenv from "dotenv";
|
28
|
-
import express from "express";
|
29
|
-
var defaultPort = PLAYGROUND_SERVER_PORT;
|
30
|
-
var errorHandler = (err, req, res, next) => {
|
31
|
-
console.error(err);
|
32
|
-
res.status(500).json({
|
33
|
-
error: err.message
|
34
|
-
});
|
35
|
-
};
|
36
|
-
var setup = async () => {
|
37
|
-
if (!ifInBrowser) {
|
38
|
-
const { parsed } = dotenv.config();
|
39
|
-
if (parsed) {
|
40
|
-
overrideAIConfig(parsed);
|
41
|
-
}
|
42
|
-
}
|
43
|
-
};
|
44
|
-
var PlaygroundServer = class {
|
45
|
-
constructor(pageClass, agentClass, staticPath) {
|
46
|
-
this.app = express();
|
47
|
-
this.tmpDir = getTmpDir();
|
48
|
-
this.pageClass = pageClass;
|
49
|
-
this.agentClass = agentClass;
|
50
|
-
this.staticPath = staticPath;
|
51
|
-
this.taskProgressTips = {};
|
52
|
-
setup();
|
53
|
-
}
|
54
|
-
filePathForUuid(uuid2) {
|
55
|
-
return join(this.tmpDir, `${uuid2}.json`);
|
56
|
-
}
|
57
|
-
saveContextFile(uuid2, context) {
|
58
|
-
const tmpFile = this.filePathForUuid(uuid2);
|
59
|
-
console.log(`save context file: ${tmpFile}`);
|
60
|
-
writeFileSync(tmpFile, context);
|
61
|
-
return tmpFile;
|
62
|
-
}
|
63
|
-
async launch(port) {
|
64
|
-
this.port = port || defaultPort;
|
65
|
-
this.app.use(errorHandler);
|
66
|
-
this.app.use(
|
67
|
-
cors({
|
68
|
-
origin: "*",
|
69
|
-
credentials: true
|
70
|
-
})
|
71
|
-
);
|
72
|
-
this.app.get("/status", cors(), async (req, res) => {
|
73
|
-
res.send({
|
74
|
-
status: "ok"
|
75
|
-
});
|
76
|
-
});
|
77
|
-
this.app.get("/context/:uuid", async (req, res) => {
|
78
|
-
const { uuid: uuid2 } = req.params;
|
79
|
-
const contextFile = this.filePathForUuid(uuid2);
|
80
|
-
if (!existsSync(contextFile)) {
|
81
|
-
return res.status(404).json({
|
82
|
-
error: "Context not found"
|
83
|
-
});
|
84
|
-
}
|
85
|
-
const context = readFileSync(contextFile, "utf8");
|
86
|
-
res.json({
|
87
|
-
context
|
88
|
-
});
|
89
|
-
});
|
90
|
-
this.app.get("/task-progress/:requestId", cors(), async (req, res) => {
|
91
|
-
const { requestId } = req.params;
|
92
|
-
res.json({
|
93
|
-
tip: this.taskProgressTips[requestId] || ""
|
94
|
-
});
|
95
|
-
});
|
96
|
-
this.app.post(
|
97
|
-
"/playground-with-context",
|
98
|
-
express.json({ limit: "50mb" }),
|
99
|
-
async (req, res) => {
|
100
|
-
const context = req.body.context;
|
101
|
-
if (!context) {
|
102
|
-
return res.status(400).json({
|
103
|
-
error: "context is required"
|
104
|
-
});
|
105
|
-
}
|
106
|
-
const uuid2 = randomUUID();
|
107
|
-
this.saveContextFile(uuid2, context);
|
108
|
-
return res.json({
|
109
|
-
location: `/playground/${uuid2}`,
|
110
|
-
uuid: uuid2
|
111
|
-
});
|
112
|
-
}
|
113
|
-
);
|
114
|
-
this.app.post(
|
115
|
-
"/execute",
|
116
|
-
express.json({ limit: "30mb" }),
|
117
|
-
async (req, res) => {
|
118
|
-
const { context, type, prompt, requestId, deepThink } = req.body;
|
119
|
-
if (!context) {
|
120
|
-
return res.status(400).json({
|
121
|
-
error: "context is required"
|
122
|
-
});
|
123
|
-
}
|
124
|
-
if (!type) {
|
125
|
-
return res.status(400).json({
|
126
|
-
error: "type is required"
|
127
|
-
});
|
128
|
-
}
|
129
|
-
if (!prompt) {
|
130
|
-
return res.status(400).json({
|
131
|
-
error: "prompt is required"
|
132
|
-
});
|
133
|
-
}
|
134
|
-
const page = new this.pageClass(context);
|
135
|
-
const agent = new this.agentClass(page);
|
136
|
-
if (requestId) {
|
137
|
-
this.taskProgressTips[requestId] = "";
|
138
|
-
agent.onTaskStartTip = (tip) => {
|
139
|
-
this.taskProgressTips[requestId] = tip;
|
140
|
-
};
|
141
|
-
}
|
142
|
-
const response = {
|
143
|
-
result: null,
|
144
|
-
dump: null,
|
145
|
-
error: null,
|
146
|
-
reportHTML: null,
|
147
|
-
requestId
|
148
|
-
};
|
149
|
-
const startTime = Date.now();
|
150
|
-
try {
|
151
|
-
if (type === "aiQuery") {
|
152
|
-
response.result = await agent.aiQuery(prompt);
|
153
|
-
} else if (type === "aiAction") {
|
154
|
-
response.result = await agent.aiAction(prompt);
|
155
|
-
} else if (type === "aiAssert") {
|
156
|
-
response.result = await agent.aiAssert(prompt, void 0, {
|
157
|
-
keepRawResponse: true
|
158
|
-
});
|
159
|
-
} else if (type === "aiTap") {
|
160
|
-
response.result = await agent.aiTap(prompt, {
|
161
|
-
deepThink
|
162
|
-
});
|
163
|
-
} else {
|
164
|
-
response.error = `Unknown type: ${type}`;
|
165
|
-
}
|
166
|
-
} catch (error) {
|
167
|
-
if (!error.message.includes(ERROR_CODE_NOT_IMPLEMENTED_AS_DESIGNED)) {
|
168
|
-
response.error = error.message;
|
169
|
-
}
|
170
|
-
}
|
171
|
-
try {
|
172
|
-
response.dump = JSON.parse(agent.dumpDataString());
|
173
|
-
response.reportHTML = agent.reportHTMLString() || null;
|
174
|
-
agent.writeOutActionDumps();
|
175
|
-
} catch (error) {
|
176
|
-
console.error(
|
177
|
-
`write out dump failed: requestId: ${requestId}, ${error.message}`
|
178
|
-
);
|
179
|
-
}
|
180
|
-
res.send(response);
|
181
|
-
const timeCost = Date.now() - startTime;
|
182
|
-
if (response.error) {
|
183
|
-
console.error(
|
184
|
-
`handle request failed after ${timeCost}ms: requestId: ${requestId}, ${response.error}`
|
185
|
-
);
|
186
|
-
} else {
|
187
|
-
console.log(
|
188
|
-
`handle request done after ${timeCost}ms: requestId: ${requestId}`
|
189
|
-
);
|
190
|
-
}
|
191
|
-
}
|
192
|
-
);
|
193
|
-
this.app.post(
|
194
|
-
"/config",
|
195
|
-
express.json({ limit: "1mb" }),
|
196
|
-
async (req, res) => {
|
197
|
-
const { aiConfig } = req.body;
|
198
|
-
if (!aiConfig || typeof aiConfig !== "object") {
|
199
|
-
return res.status(400).json({
|
200
|
-
error: "aiConfig is required and must be an object"
|
201
|
-
});
|
202
|
-
}
|
203
|
-
try {
|
204
|
-
overrideAIConfig(aiConfig);
|
205
|
-
return res.json({
|
206
|
-
status: "ok",
|
207
|
-
message: "AI config updated successfully"
|
208
|
-
});
|
209
|
-
} catch (error) {
|
210
|
-
console.error(`Failed to update AI config: ${error.message}`);
|
211
|
-
return res.status(500).json({
|
212
|
-
error: `Failed to update AI config: ${error.message}`
|
213
|
-
});
|
214
|
-
}
|
215
|
-
}
|
216
|
-
);
|
217
|
-
if (this.staticPath) {
|
218
|
-
this.app.get("/", (req, res) => {
|
219
|
-
res.redirect("/index.html");
|
220
|
-
});
|
221
|
-
this.app.get("*", (req, res) => {
|
222
|
-
const requestedPath = join(this.staticPath, req.path);
|
223
|
-
if (existsSync(requestedPath)) {
|
224
|
-
res.sendFile(requestedPath);
|
225
|
-
} else {
|
226
|
-
res.sendFile(join(this.staticPath, "index.html"));
|
227
|
-
}
|
228
|
-
});
|
229
|
-
}
|
230
|
-
return new Promise((resolve, reject) => {
|
231
|
-
const port2 = this.port;
|
232
|
-
this.server = this.app.listen(port2, () => {
|
233
|
-
resolve(this);
|
234
|
-
});
|
235
|
-
});
|
236
|
-
}
|
237
|
-
close() {
|
238
|
-
if (this.server) {
|
239
|
-
return this.server.close();
|
240
|
-
}
|
241
|
-
}
|
242
|
-
};
|
243
|
-
export {
|
244
|
-
PlaygroundServer as default
|
245
|
-
};
|
246
|
-
|
247
|
-
//# sourceMappingURL=midscene-server.js.map
|
@@ -1 +0,0 @@
|
|
1
|
-
{"version":3,"mappings":"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","names":["uuid","port"],"ignoreList":[],"sources":["../../src/playground/server.ts","../../src/common/utils.ts"],"sourcesContent":["import { randomUUID } from 'node:crypto';\nimport { existsSync, readFileSync, writeFileSync } from 'node:fs';\nimport type { Server } from 'node:http';\nimport { join } from 'node:path';\nimport { ERROR_CODE_NOT_IMPLEMENTED_AS_DESIGNED } from '@/common/utils';\nimport { getTmpDir } from 'misoai-core/utils';\nimport { PLAYGROUND_SERVER_PORT } from 'misoai-shared/constants';\nimport { overrideAIConfig } from 'misoai-shared/env';\nimport { ifInBrowser } from 'misoai-shared/utils';\nimport cors from 'cors';\nimport dotenv from 'dotenv';\nimport express from 'express';\nimport type { PageAgent } from '../common/agent';\nimport type { AbstractPage } from '../page';\n\nconst defaultPort = PLAYGROUND_SERVER_PORT;\n// const staticPath = join(__dirname, '../../static');\n\nconst errorHandler = (err: any, req: any, res: any, next: any) => {\n console.error(err);\n res.status(500).json({\n error: err.message,\n });\n};\n\nconst setup = async () => {\n if (!ifInBrowser) {\n const { parsed } = dotenv.config();\n\n if (parsed) {\n overrideAIConfig(parsed);\n }\n }\n};\n\nexport default class PlaygroundServer {\n app: express.Application;\n tmpDir: string;\n server?: Server;\n port?: number | null;\n pageClass: new (\n ...args: any[]\n ) => AbstractPage;\n agentClass: new (\n ...args: any[]\n ) => PageAgent;\n staticPath?: string;\n taskProgressTips: Record<string, string>;\n\n constructor(\n pageClass: new (...args: any[]) => AbstractPage,\n agentClass: new (...args: any[]) => PageAgent,\n staticPath?: string,\n ) {\n this.app = express();\n this.tmpDir = getTmpDir()!;\n this.pageClass = pageClass;\n this.agentClass = agentClass;\n this.staticPath = staticPath;\n this.taskProgressTips = {};\n setup();\n }\n\n filePathForUuid(uuid: string) {\n return join(this.tmpDir, `${uuid}.json`);\n }\n\n saveContextFile(uuid: string, context: string) {\n const tmpFile = this.filePathForUuid(uuid);\n console.log(`save context file: ${tmpFile}`);\n writeFileSync(tmpFile, context);\n return tmpFile;\n }\n\n async launch(port?: number) {\n this.port = port || defaultPort;\n this.app.use(errorHandler);\n\n this.app.use(\n cors({\n origin: '*',\n credentials: true,\n }),\n );\n\n this.app.get('/status', cors(), async (req, res) => {\n // const modelName = g\n res.send({\n status: 'ok',\n });\n });\n\n // this.app.get('/playground/:uuid', async (req, res) => {\n // res.sendFile(join(staticPath, 'index.html'));\n // });\n\n this.app.get('/context/:uuid', async (req, res) => {\n const { uuid } = req.params;\n const contextFile = this.filePathForUuid(uuid);\n\n if (!existsSync(contextFile)) {\n return res.status(404).json({\n error: 'Context not found',\n });\n }\n\n const context = readFileSync(contextFile, 'utf8');\n res.json({\n context,\n });\n });\n\n this.app.get('/task-progress/:requestId', cors(), async (req, res) => {\n const { requestId } = req.params;\n res.json({\n tip: this.taskProgressTips[requestId] || '',\n });\n });\n\n // -------------------------\n // actions from report file\n this.app.post(\n '/playground-with-context',\n express.json({ limit: '50mb' }),\n async (req, res) => {\n const context = req.body.context;\n\n if (!context) {\n return res.status(400).json({\n error: 'context is required',\n });\n }\n\n const uuid = randomUUID();\n this.saveContextFile(uuid, context);\n return res.json({\n location: `/playground/${uuid}`,\n uuid,\n });\n },\n );\n\n this.app.post(\n '/execute',\n express.json({ limit: '30mb' }),\n async (req, res) => {\n const { context, type, prompt, requestId, deepThink } = req.body;\n\n if (!context) {\n return res.status(400).json({\n error: 'context is required',\n });\n }\n\n if (!type) {\n return res.status(400).json({\n error: 'type is required',\n });\n }\n\n if (!prompt) {\n return res.status(400).json({\n error: 'prompt is required',\n });\n }\n\n // build an agent with context\n const page = new this.pageClass(context);\n const agent = new this.agentClass(page);\n\n if (requestId) {\n this.taskProgressTips[requestId] = '';\n\n agent.onTaskStartTip = (tip: string) => {\n this.taskProgressTips[requestId] = tip;\n };\n }\n\n const response: {\n result: any;\n dump: string | null;\n error: string | null;\n reportHTML: string | null;\n requestId?: string;\n } = {\n result: null,\n dump: null,\n error: null,\n reportHTML: null,\n requestId,\n };\n\n const startTime = Date.now();\n try {\n if (type === 'aiQuery') {\n response.result = await agent.aiQuery(prompt);\n } else if (type === 'aiAction') {\n response.result = await agent.aiAction(prompt);\n } else if (type === 'aiAssert') {\n response.result = await agent.aiAssert(prompt, undefined, {\n keepRawResponse: true,\n });\n } else if (type === 'aiTap') {\n response.result = await agent.aiTap(prompt, {\n deepThink,\n });\n } else {\n response.error = `Unknown type: ${type}`;\n }\n } catch (error: any) {\n if (!error.message.includes(ERROR_CODE_NOT_IMPLEMENTED_AS_DESIGNED)) {\n response.error = error.message;\n }\n }\n\n try {\n response.dump = JSON.parse(agent.dumpDataString());\n response.reportHTML = agent.reportHTMLString() || null;\n\n agent.writeOutActionDumps();\n } catch (error: any) {\n console.error(\n `write out dump failed: requestId: ${requestId}, ${error.message}`,\n );\n }\n\n res.send(response);\n const timeCost = Date.now() - startTime;\n\n if (response.error) {\n console.error(\n `handle request failed after ${timeCost}ms: requestId: ${requestId}, ${response.error}`,\n );\n } else {\n console.log(\n `handle request done after ${timeCost}ms: requestId: ${requestId}`,\n );\n }\n },\n );\n\n this.app.post(\n '/config',\n express.json({ limit: '1mb' }),\n async (req, res) => {\n const { aiConfig } = req.body;\n\n if (!aiConfig || typeof aiConfig !== 'object') {\n return res.status(400).json({\n error: 'aiConfig is required and must be an object',\n });\n }\n\n try {\n overrideAIConfig(aiConfig);\n\n return res.json({\n status: 'ok',\n message: 'AI config updated successfully',\n });\n } catch (error: any) {\n console.error(`Failed to update AI config: ${error.message}`);\n return res.status(500).json({\n error: `Failed to update AI config: ${error.message}`,\n });\n }\n },\n );\n\n // Set up static file serving after all API routes are defined\n if (this.staticPath) {\n this.app.get('/', (req, res) => {\n // compatible with windows\n res.redirect('/index.html');\n });\n\n this.app.get('*', (req, res) => {\n const requestedPath = join(this.staticPath!, req.path);\n if (existsSync(requestedPath)) {\n res.sendFile(requestedPath);\n } else {\n res.sendFile(join(this.staticPath!, 'index.html'));\n }\n });\n }\n\n return new Promise((resolve, reject) => {\n const port = this.port;\n this.server = this.app.listen(port, () => {\n resolve(this);\n });\n });\n }\n\n close() {\n // close the server\n if (this.server) {\n return this.server.close();\n }\n }\n}\n","import type { StaticPage } from '@/playground';\nimport type {\n BaseElement,\n ElementTreeNode,\n PlanningLocateParam,\n PlaywrightParserOpt,\n UIContext,\n} from 'misoai-core';\nimport { elementByPositionWithElementInfo } from 'misoai-core/ai-model';\nimport { uploadTestInfoToServer } from 'misoai-core/utils';\nimport { MIDSCENE_REPORT_TAG_NAME, getAIConfig } from 'misoai-shared/env';\nimport type { ElementInfo } from 'misoai-shared/extractor';\nimport {\n generateElementByPosition,\n getNodeFromCacheList,\n traverseTree,\n treeToList,\n} from 'misoai-shared/extractor';\nimport { resizeImgBase64 } from 'misoai-shared/img';\nimport type { DebugFunction } from 'misoai-shared/logger';\nimport { assert, logMsg, uuid } from 'misoai-shared/utils';\nimport dayjs from 'dayjs';\nimport type { Page as PlaywrightPage } from 'playwright';\nimport type { Page as PuppeteerPage } from 'puppeteer';\nimport { WebElementInfo } from '../web-element';\nimport type { WebPage } from './page';\n\nexport type WebUIContext = UIContext<WebElementInfo> & {\n url: string;\n};\n\nexport async function parseContextFromWebPage(\n page: WebPage,\n _opt?: PlaywrightParserOpt,\n): Promise<WebUIContext> {\n assert(page, 'page is required');\n if ((page as StaticPage)._forceUsePageContext) {\n return await (page as any)._forceUsePageContext();\n }\n const url = await page.url();\n uploadTestInfoToServer({ testUrl: url });\n\n let screenshotBase64: string;\n let tree: ElementTreeNode<ElementInfo>;\n\n await Promise.all([\n page.screenshotBase64().then((base64) => {\n screenshotBase64 = base64;\n }),\n page.getElementsNodeTree().then(async (treeRoot) => {\n tree = treeRoot;\n }),\n ]);\n\n const webTree = traverseTree(tree!, (elementInfo) => {\n const { rect, id, content, attributes, locator, indexId } = elementInfo;\n return new WebElementInfo({\n rect,\n locator,\n id,\n content,\n attributes,\n indexId,\n });\n });\n\n assert(screenshotBase64!, 'screenshotBase64 is required');\n\n const elementsInfo = treeToList(webTree);\n const size = await page.size();\n\n if (size.dpr && size.dpr > 1) {\n // console.time('resizeImgBase64');\n screenshotBase64 = await resizeImgBase64(screenshotBase64, {\n width: size.width,\n height: size.height,\n });\n // console.timeEnd('resizeImgBase64');\n }\n\n return {\n content: elementsInfo!,\n tree: webTree,\n size,\n screenshotBase64: screenshotBase64!,\n url,\n };\n}\n\nexport function reportFileName(tag = 'web') {\n const reportTagName = getAIConfig(MIDSCENE_REPORT_TAG_NAME);\n const dateTimeInFileName = dayjs().format('YYYY-MM-DD_HH-mm-ss');\n // ensure uniqueness at the same time\n const uniqueId = uuid().substring(0, 8);\n return `${reportTagName || tag}-${dateTimeInFileName}-${uniqueId}`;\n}\n\nexport function printReportMsg(filepath: string) {\n logMsg(`Midscene - report file updated: ${filepath}`);\n}\n\n/**\n * Get the current execution file name\n * @returns The name of the current execution file\n */\nexport function getCurrentExecutionFile(trace?: string): string | false {\n const error = new Error();\n const stackTrace = trace || error.stack;\n const pkgDir = process.cwd() || '';\n if (stackTrace) {\n const stackLines = stackTrace.split('\\n');\n for (const line of stackLines) {\n if (\n line.includes('.spec.') ||\n line.includes('.test.') ||\n line.includes('.ts') ||\n line.includes('.js')\n ) {\n const match = line.match(/(?:at\\s+)?(.*?\\.(?:spec|test)\\.[jt]s)/);\n if (match?.[1]) {\n const targetFileName = match[1]\n .replace(pkgDir, '')\n .trim()\n .replace('at ', '');\n return targetFileName;\n }\n }\n }\n }\n return false;\n}\n\nconst testFileIndex = new Map<string, number>();\n\nexport function generateCacheId(fileName?: string): string {\n let taskFile = fileName || getCurrentExecutionFile();\n if (!taskFile) {\n taskFile = uuid();\n console.warn(\n 'Midscene - using random UUID for cache id. Cache may be invalid.',\n );\n }\n\n if (testFileIndex.has(taskFile)) {\n const currentIndex = testFileIndex.get(taskFile);\n if (currentIndex !== undefined) {\n testFileIndex.set(taskFile, currentIndex + 1);\n }\n } else {\n testFileIndex.set(taskFile, 1);\n }\n return `${taskFile}-${testFileIndex.get(taskFile)}`;\n}\n\nexport const ERROR_CODE_NOT_IMPLEMENTED_AS_DESIGNED =\n 'NOT_IMPLEMENTED_AS_DESIGNED';\n\nexport function replaceIllegalPathCharsAndSpace(str: string) {\n return str.replace(/[/\\\\:*?\"<>| ]/g, '-');\n}\n\nexport function forceClosePopup(\n page: PuppeteerPage | PlaywrightPage,\n debug: DebugFunction,\n) {\n page.on('popup', async (popup) => {\n if (!popup) {\n console.warn('got a popup event, but the popup is not ready yet, skip');\n return;\n }\n const url = await (popup as PuppeteerPage).url();\n console.log(`Popup opened: ${url}`);\n if (!(popup as PuppeteerPage).isClosed()) {\n try {\n await (popup as PuppeteerPage).close(); // Close the newly opened TAB\n } catch (error) {\n debug(`failed to close popup ${url}, error: ${error}`);\n }\n } else {\n debug(`popup is already closed, skip close ${url}`);\n }\n\n if (!page.isClosed()) {\n try {\n await page.goto(url);\n } catch (error) {\n debug(`failed to goto ${url}, error: ${error}`);\n }\n } else {\n debug(`page is already closed, skip goto ${url}`);\n }\n });\n}\n\nexport function matchElementFromPlan(\n planLocateParam: PlanningLocateParam,\n tree: ElementTreeNode<BaseElement>,\n) {\n if (!planLocateParam) {\n return undefined;\n }\n if (planLocateParam.id) {\n return getNodeFromCacheList(planLocateParam.id);\n }\n\n if (planLocateParam.bbox) {\n const centerPosition = {\n x: Math.floor((planLocateParam.bbox[0] + planLocateParam.bbox[2]) / 2),\n y: Math.floor((planLocateParam.bbox[1] + planLocateParam.bbox[3]) / 2),\n };\n let element = elementByPositionWithElementInfo(tree, centerPosition);\n\n if (!element) {\n element = generateElementByPosition(centerPosition) as BaseElement;\n }\n\n return element;\n }\n\n return undefined;\n}\n"]}
|