@midscene/web 0.3.0 → 0.3.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/LICENSE +1 -1
- package/dist/es/index.js +343 -41
- package/dist/es/playwright-report.js +0 -1
- package/dist/lib/index.js +344 -40
- package/dist/script/htmlElement.js +73 -52
- package/dist/script/htmlElementDebug.js +871 -0
- package/dist/script/types/htmlElement.d.ts +4 -2
- package/dist/script/types/htmlElementDebug.d.ts +2 -0
- package/dist/types/index.d.ts +18 -2
- package/package.json +9 -6
package/LICENSE
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
MIT License
|
|
2
2
|
|
|
3
|
-
Copyright (c)
|
|
3
|
+
Copyright (c) 2024-present Bytedance, Inc. and its affiliates.
|
|
4
4
|
|
|
5
5
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
6
|
of this software and associated documentation files (the "Software"), to deal
|
package/dist/es/index.js
CHANGED
|
@@ -21,7 +21,7 @@ var __spreadValues = (a, b) => {
|
|
|
21
21
|
return a;
|
|
22
22
|
};
|
|
23
23
|
var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
|
|
24
|
-
var __commonJS = (cb, mod) => function
|
|
24
|
+
var __commonJS = (cb, mod) => function __require2() {
|
|
25
25
|
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
|
|
26
26
|
};
|
|
27
27
|
var __copyProps = (to, from, except, desc) => {
|
|
@@ -416,7 +416,6 @@ import assert from "assert";
|
|
|
416
416
|
import fs, { readFileSync } from "fs";
|
|
417
417
|
import path from "path";
|
|
418
418
|
import {
|
|
419
|
-
alignCoordByTrim,
|
|
420
419
|
base64Encoded,
|
|
421
420
|
imageInfoOfBase64
|
|
422
421
|
} from "@midscene/core/image";
|
|
@@ -483,17 +482,16 @@ async function alignElements(screenshotBuffer, elements, page) {
|
|
|
483
482
|
return item.rect.height >= sizeThreshold && item.rect.width >= sizeThreshold;
|
|
484
483
|
});
|
|
485
484
|
for (const item of validElements) {
|
|
486
|
-
const { rect } = item;
|
|
487
|
-
const aligned = await alignCoordByTrim(screenshotBuffer, rect);
|
|
488
|
-
item.rect = aligned;
|
|
489
|
-
item.center = [
|
|
490
|
-
Math.round(aligned.left + aligned.width / 2),
|
|
491
|
-
Math.round(aligned.top + aligned.height / 2)
|
|
492
|
-
];
|
|
485
|
+
const { rect, id, content, attributes, locator } = item;
|
|
493
486
|
textsAligned.push(
|
|
494
|
-
new WebElementInfo(
|
|
487
|
+
new WebElementInfo({
|
|
488
|
+
rect,
|
|
489
|
+
locator,
|
|
490
|
+
id,
|
|
491
|
+
content,
|
|
492
|
+
attributes,
|
|
495
493
|
page
|
|
496
|
-
})
|
|
494
|
+
})
|
|
497
495
|
);
|
|
498
496
|
}
|
|
499
497
|
return textsAligned;
|
|
@@ -617,7 +615,7 @@ var PageTaskExecutor = class {
|
|
|
617
615
|
};
|
|
618
616
|
return taskFind;
|
|
619
617
|
}
|
|
620
|
-
if (plan2.type === "Assert") {
|
|
618
|
+
if (plan2.type === "Assert" || plan2.type === "AssertWithoutThrow") {
|
|
621
619
|
const assertPlan = plan2;
|
|
622
620
|
const taskAssert = {
|
|
623
621
|
type: "Insight",
|
|
@@ -634,13 +632,16 @@ var PageTaskExecutor = class {
|
|
|
634
632
|
assertPlan.param.assertion
|
|
635
633
|
);
|
|
636
634
|
if (!assertion.pass) {
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
635
|
+
if (plan2.type === "Assert") {
|
|
636
|
+
task.output = assertion;
|
|
637
|
+
task.log = {
|
|
638
|
+
dump: insightDump
|
|
639
|
+
};
|
|
640
|
+
throw new Error(
|
|
641
|
+
assertion.thought || "Assertion failed without reason"
|
|
642
|
+
);
|
|
643
|
+
}
|
|
644
|
+
task.error = assertion.thought;
|
|
644
645
|
}
|
|
645
646
|
return {
|
|
646
647
|
output: assertion,
|
|
@@ -756,7 +757,19 @@ var PageTaskExecutor = class {
|
|
|
756
757
|
return taskActionSleep;
|
|
757
758
|
}
|
|
758
759
|
if (plan2.type === "Error") {
|
|
759
|
-
|
|
760
|
+
const taskActionError = {
|
|
761
|
+
type: "Action",
|
|
762
|
+
subType: "Error",
|
|
763
|
+
param: plan2.param,
|
|
764
|
+
executor: async (taskParam) => {
|
|
765
|
+
assert2(
|
|
766
|
+
taskParam.thought,
|
|
767
|
+
"An error occurred, but no thought provided"
|
|
768
|
+
);
|
|
769
|
+
throw new Error(taskParam.thought);
|
|
770
|
+
}
|
|
771
|
+
};
|
|
772
|
+
return taskActionError;
|
|
760
773
|
}
|
|
761
774
|
throw new Error(`Unknown or Unsupported task type: ${plan2.type}`);
|
|
762
775
|
}).map((task) => {
|
|
@@ -766,7 +779,6 @@ var PageTaskExecutor = class {
|
|
|
766
779
|
}
|
|
767
780
|
async action(userPrompt) {
|
|
768
781
|
const taskExecutor = new Executor(userPrompt);
|
|
769
|
-
taskExecutor.description = userPrompt;
|
|
770
782
|
let plans = [];
|
|
771
783
|
const planningTask = {
|
|
772
784
|
type: "Planning",
|
|
@@ -826,7 +838,6 @@ var PageTaskExecutor = class {
|
|
|
826
838
|
async query(demand) {
|
|
827
839
|
const description = typeof demand === "string" ? demand : JSON.stringify(demand);
|
|
828
840
|
const taskExecutor = new Executor(description);
|
|
829
|
-
taskExecutor.description = description;
|
|
830
841
|
const queryTask = {
|
|
831
842
|
type: "Insight",
|
|
832
843
|
subType: "Query",
|
|
@@ -854,9 +865,8 @@ var PageTaskExecutor = class {
|
|
|
854
865
|
};
|
|
855
866
|
}
|
|
856
867
|
async assert(assertion) {
|
|
857
|
-
const description = assertion
|
|
868
|
+
const description = `assert: ${assertion}`;
|
|
858
869
|
const taskExecutor = new Executor(description);
|
|
859
|
-
taskExecutor.description = description;
|
|
860
870
|
const assertionPlan = {
|
|
861
871
|
type: "Assert",
|
|
862
872
|
param: {
|
|
@@ -871,6 +881,64 @@ var PageTaskExecutor = class {
|
|
|
871
881
|
executor: taskExecutor
|
|
872
882
|
};
|
|
873
883
|
}
|
|
884
|
+
async waitFor(assertion, opt) {
|
|
885
|
+
const description = `waitFor: ${assertion}`;
|
|
886
|
+
const taskExecutor = new Executor(description);
|
|
887
|
+
const { timeoutMs, checkIntervalMs } = opt;
|
|
888
|
+
assert2(assertion, "No assertion for waitFor");
|
|
889
|
+
assert2(timeoutMs, "No timeoutMs for waitFor");
|
|
890
|
+
assert2(checkIntervalMs, "No checkIntervalMs for waitFor");
|
|
891
|
+
const overallStartTime = Date.now();
|
|
892
|
+
let startTime = Date.now();
|
|
893
|
+
let errorThought = "";
|
|
894
|
+
while (Date.now() - overallStartTime < timeoutMs) {
|
|
895
|
+
startTime = Date.now();
|
|
896
|
+
const assertPlan = {
|
|
897
|
+
type: "AssertWithoutThrow",
|
|
898
|
+
param: {
|
|
899
|
+
assertion
|
|
900
|
+
}
|
|
901
|
+
};
|
|
902
|
+
const assertTask = await this.convertPlanToExecutable([assertPlan]);
|
|
903
|
+
await taskExecutor.append(this.wrapExecutorWithScreenshot(assertTask[0]));
|
|
904
|
+
const output = await taskExecutor.flush();
|
|
905
|
+
if (output.pass) {
|
|
906
|
+
return {
|
|
907
|
+
output: void 0,
|
|
908
|
+
executor: taskExecutor
|
|
909
|
+
};
|
|
910
|
+
}
|
|
911
|
+
errorThought = output.thought;
|
|
912
|
+
const now = Date.now();
|
|
913
|
+
if (now - startTime < checkIntervalMs) {
|
|
914
|
+
const timeRemaining = checkIntervalMs - (now - startTime);
|
|
915
|
+
const sleepPlan = {
|
|
916
|
+
type: "Sleep",
|
|
917
|
+
param: {
|
|
918
|
+
timeMs: timeRemaining
|
|
919
|
+
}
|
|
920
|
+
};
|
|
921
|
+
const sleepTask = await this.convertPlanToExecutable([sleepPlan]);
|
|
922
|
+
await taskExecutor.append(
|
|
923
|
+
this.wrapExecutorWithScreenshot(sleepTask[0])
|
|
924
|
+
);
|
|
925
|
+
await taskExecutor.flush();
|
|
926
|
+
}
|
|
927
|
+
}
|
|
928
|
+
const errorPlan = {
|
|
929
|
+
type: "Error",
|
|
930
|
+
param: {
|
|
931
|
+
thought: `waitFor timeout: ${errorThought}`
|
|
932
|
+
}
|
|
933
|
+
};
|
|
934
|
+
const errorTask = await this.convertPlanToExecutable([errorPlan]);
|
|
935
|
+
await taskExecutor.append(errorTask[0]);
|
|
936
|
+
await taskExecutor.flush();
|
|
937
|
+
return {
|
|
938
|
+
output: void 0,
|
|
939
|
+
executor: taskExecutor
|
|
940
|
+
};
|
|
941
|
+
}
|
|
874
942
|
};
|
|
875
943
|
|
|
876
944
|
// src/common/agent.ts
|
|
@@ -880,6 +948,7 @@ var PageAgent = class {
|
|
|
880
948
|
this.opts = Object.assign(
|
|
881
949
|
{
|
|
882
950
|
generateReport: true,
|
|
951
|
+
autoPrintReportMsg: true,
|
|
883
952
|
groupName: "Midscene Report",
|
|
884
953
|
groupDescription: ""
|
|
885
954
|
},
|
|
@@ -905,7 +974,7 @@ var PageAgent = class {
|
|
|
905
974
|
return stringifyDumpData(this.dump);
|
|
906
975
|
}
|
|
907
976
|
writeOutActionDumps() {
|
|
908
|
-
const generateReport = this.opts
|
|
977
|
+
const { generateReport, autoPrintReportMsg } = this.opts;
|
|
909
978
|
this.reportFile = writeLogFile({
|
|
910
979
|
fileName: this.reportFileName,
|
|
911
980
|
fileExt: groupedActionDumpFileExt,
|
|
@@ -913,7 +982,7 @@ var PageAgent = class {
|
|
|
913
982
|
type: "dump",
|
|
914
983
|
generateReport
|
|
915
984
|
});
|
|
916
|
-
if (generateReport) {
|
|
985
|
+
if (generateReport && autoPrintReportMsg) {
|
|
917
986
|
printReportMsg(this.reportFile);
|
|
918
987
|
}
|
|
919
988
|
}
|
|
@@ -949,6 +1018,20 @@ ${errorTask == null ? void 0 : errorTask.errorStack}`);
|
|
|
949
1018
|
${reasonMsg}`);
|
|
950
1019
|
}
|
|
951
1020
|
}
|
|
1021
|
+
async aiWaitFor(assertion, opt) {
|
|
1022
|
+
const { executor } = await this.taskExecutor.waitFor(assertion, {
|
|
1023
|
+
timeoutMs: (opt == null ? void 0 : opt.timeoutMs) || 15 * 1e3,
|
|
1024
|
+
checkIntervalMs: (opt == null ? void 0 : opt.checkIntervalMs) || 3 * 1e3,
|
|
1025
|
+
assertion
|
|
1026
|
+
});
|
|
1027
|
+
this.appendExecutionDump(executor.dump());
|
|
1028
|
+
this.writeOutActionDumps();
|
|
1029
|
+
if (executor.isInErrorState()) {
|
|
1030
|
+
const errorTask = executor.latestErrorTask();
|
|
1031
|
+
throw new Error(`${errorTask == null ? void 0 : errorTask.error}
|
|
1032
|
+
${errorTask == null ? void 0 : errorTask.errorStack}`);
|
|
1033
|
+
}
|
|
1034
|
+
}
|
|
952
1035
|
async ai(taskPrompt, type = "action") {
|
|
953
1036
|
if (type === "action") {
|
|
954
1037
|
return this.aiAction(taskPrompt);
|
|
@@ -965,6 +1048,9 @@ ${reasonMsg}`);
|
|
|
965
1048
|
}
|
|
966
1049
|
};
|
|
967
1050
|
|
|
1051
|
+
// src/playwright/index.ts
|
|
1052
|
+
import { test } from "@playwright/test";
|
|
1053
|
+
|
|
968
1054
|
// src/playwright/cache.ts
|
|
969
1055
|
import fs2 from "fs";
|
|
970
1056
|
import path2, { join } from "path";
|
|
@@ -1070,14 +1156,14 @@ var PlaywrightAiFixture = () => {
|
|
|
1070
1156
|
}
|
|
1071
1157
|
return pageAgentMap[idForPage];
|
|
1072
1158
|
};
|
|
1073
|
-
const updateDumpAnnotation = (
|
|
1074
|
-
const currentAnnotation =
|
|
1159
|
+
const updateDumpAnnotation = (test2, dump) => {
|
|
1160
|
+
const currentAnnotation = test2.annotations.find((item) => {
|
|
1075
1161
|
return item.type === midsceneDumpAnnotationId;
|
|
1076
1162
|
});
|
|
1077
1163
|
if (currentAnnotation) {
|
|
1078
1164
|
currentAnnotation.description = dump;
|
|
1079
1165
|
} else {
|
|
1080
|
-
|
|
1166
|
+
test2.annotations.push({
|
|
1081
1167
|
type: midsceneDumpAnnotationId,
|
|
1082
1168
|
description: dump
|
|
1083
1169
|
});
|
|
@@ -1089,10 +1175,14 @@ var PlaywrightAiFixture = () => {
|
|
|
1089
1175
|
const agent = agentForPage(page, testInfo);
|
|
1090
1176
|
await use(
|
|
1091
1177
|
async (taskPrompt, opts) => {
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1178
|
+
return new Promise((resolve, reject) => {
|
|
1179
|
+
test.step(`ai - ${taskPrompt}`, async () => {
|
|
1180
|
+
await page.waitForLoadState("networkidle");
|
|
1181
|
+
const actionType = (opts == null ? void 0 : opts.type) || "action";
|
|
1182
|
+
const result = await agent.ai(taskPrompt, actionType);
|
|
1183
|
+
resolve(result);
|
|
1184
|
+
});
|
|
1185
|
+
});
|
|
1096
1186
|
}
|
|
1097
1187
|
);
|
|
1098
1188
|
const taskCacheJson = agent.taskExecutor.taskCache.generateTaskCache();
|
|
@@ -1103,31 +1193,243 @@ var PlaywrightAiFixture = () => {
|
|
|
1103
1193
|
const { taskFile, taskTitle } = groupAndCaseForTest(testInfo);
|
|
1104
1194
|
const agent = agentForPage(page, testInfo);
|
|
1105
1195
|
await use(async (taskPrompt) => {
|
|
1106
|
-
|
|
1107
|
-
|
|
1196
|
+
test.step(`aiAction - ${taskPrompt}`, async () => {
|
|
1197
|
+
await page.waitForLoadState("networkidle");
|
|
1198
|
+
await agent.aiAction(taskPrompt);
|
|
1199
|
+
});
|
|
1108
1200
|
});
|
|
1109
1201
|
updateDumpAnnotation(testInfo, agent.dumpDataString());
|
|
1110
1202
|
},
|
|
1111
1203
|
aiQuery: async ({ page }, use, testInfo) => {
|
|
1112
1204
|
const agent = agentForPage(page, testInfo);
|
|
1113
1205
|
await use(async (demand) => {
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1206
|
+
return new Promise((resolve, reject) => {
|
|
1207
|
+
test.step(`aiQuery - ${JSON.stringify(demand)}`, async () => {
|
|
1208
|
+
await page.waitForLoadState("networkidle");
|
|
1209
|
+
const result = await agent.aiQuery(demand);
|
|
1210
|
+
resolve(result);
|
|
1211
|
+
});
|
|
1212
|
+
});
|
|
1117
1213
|
});
|
|
1118
1214
|
updateDumpAnnotation(testInfo, agent.dumpDataString());
|
|
1119
1215
|
},
|
|
1120
1216
|
aiAssert: async ({ page }, use, testInfo) => {
|
|
1121
1217
|
const agent = agentForPage(page, testInfo);
|
|
1122
1218
|
await use(async (assertion, errorMsg) => {
|
|
1123
|
-
|
|
1124
|
-
|
|
1219
|
+
return new Promise((resolve, reject) => {
|
|
1220
|
+
test.step(`aiAssert - ${assertion}`, async () => {
|
|
1221
|
+
await page.waitForLoadState("networkidle");
|
|
1222
|
+
await agent.aiAssert(assertion, errorMsg);
|
|
1223
|
+
resolve(null);
|
|
1224
|
+
});
|
|
1225
|
+
});
|
|
1226
|
+
});
|
|
1227
|
+
updateDumpAnnotation(testInfo, agent.dumpDataString());
|
|
1228
|
+
},
|
|
1229
|
+
aiWaitFor: async ({ page }, use, testInfo) => {
|
|
1230
|
+
const agent = agentForPage(page, testInfo);
|
|
1231
|
+
await use(async (assertion, opt) => {
|
|
1232
|
+
return new Promise((resolve, reject) => {
|
|
1233
|
+
test.step(`aiWaitFor - ${assertion}`, async () => {
|
|
1234
|
+
await agent.aiWaitFor(assertion, opt);
|
|
1235
|
+
resolve(null);
|
|
1236
|
+
});
|
|
1237
|
+
});
|
|
1125
1238
|
});
|
|
1126
1239
|
updateDumpAnnotation(testInfo, agent.dumpDataString());
|
|
1127
1240
|
}
|
|
1128
1241
|
};
|
|
1129
1242
|
};
|
|
1243
|
+
|
|
1244
|
+
// src/debug/index.ts
|
|
1245
|
+
import { existsSync, mkdirSync, writeFileSync } from "fs";
|
|
1246
|
+
import path3 from "path";
|
|
1247
|
+
|
|
1248
|
+
// src/img/img.ts
|
|
1249
|
+
import assert3 from "assert";
|
|
1250
|
+
import { Buffer as Buffer2 } from "buffer";
|
|
1251
|
+
import sharp from "sharp";
|
|
1252
|
+
var createSvgOverlay = (elements, imageWidth, imageHeight) => {
|
|
1253
|
+
let svgContent = `<svg width="${imageWidth}" height="${imageHeight}" xmlns="http://www.w3.org/2000/svg">`;
|
|
1254
|
+
const colors = [
|
|
1255
|
+
{ rect: "blue", text: "white" },
|
|
1256
|
+
{ rect: "green", text: "white" }
|
|
1257
|
+
];
|
|
1258
|
+
svgContent += "<defs>";
|
|
1259
|
+
elements.forEach((element, index) => {
|
|
1260
|
+
svgContent += `
|
|
1261
|
+
<clipPath id="clip${index}">
|
|
1262
|
+
<rect x="${element.x}" y="${element.y}" width="${element.width}" height="${element.height}" />
|
|
1263
|
+
</clipPath>
|
|
1264
|
+
`;
|
|
1265
|
+
});
|
|
1266
|
+
svgContent += "</defs>";
|
|
1267
|
+
elements.forEach((element, index) => {
|
|
1268
|
+
const textWidth = element.label.length * 8;
|
|
1269
|
+
const textHeight = 12;
|
|
1270
|
+
const rectWidth = textWidth + 5;
|
|
1271
|
+
const rectHeight = textHeight + 4;
|
|
1272
|
+
let rectX = element.x - rectWidth;
|
|
1273
|
+
let rectY = element.y + element.height / 2 - textHeight / 2 - 2;
|
|
1274
|
+
let textX = rectX + rectWidth / 2;
|
|
1275
|
+
let textY = rectY + rectHeight / 2 + 6;
|
|
1276
|
+
if (rectX < 0) {
|
|
1277
|
+
rectX = element.x;
|
|
1278
|
+
rectY = element.y - rectHeight;
|
|
1279
|
+
textX = rectX + rectWidth / 2;
|
|
1280
|
+
textY = rectY + rectHeight / 2 + 6;
|
|
1281
|
+
}
|
|
1282
|
+
const color = colors[index % colors.length];
|
|
1283
|
+
svgContent += `
|
|
1284
|
+
<rect x="${element.x}" y="${element.y}" width="${element.width}" height="${element.height}"
|
|
1285
|
+
style="fill:none;stroke:${color.rect};stroke-width:4" clip-path="url(#clip${index})" />
|
|
1286
|
+
<rect x="${rectX}" y="${rectY}" width="${rectWidth}" height="${rectHeight}" style="fill:${color.rect};" />
|
|
1287
|
+
<text x="${textX}" y="${textY}"
|
|
1288
|
+
text-anchor="middle" dominant-baseline="middle" style="fill:${color.text};font-size:12px;font-weight:bold;">
|
|
1289
|
+
${element.label}
|
|
1290
|
+
</text>
|
|
1291
|
+
`;
|
|
1292
|
+
});
|
|
1293
|
+
svgContent += "</svg>";
|
|
1294
|
+
return Buffer2.from(svgContent);
|
|
1295
|
+
};
|
|
1296
|
+
var processImageElementInfo = async (options) => {
|
|
1297
|
+
const base64Image = options.inputImgBase64.split(";base64,").pop();
|
|
1298
|
+
assert3(base64Image, "base64Image is undefined");
|
|
1299
|
+
const imageBuffer = Buffer2.from(base64Image, "base64");
|
|
1300
|
+
const metadata = await sharp(imageBuffer).metadata();
|
|
1301
|
+
const { width, height } = metadata;
|
|
1302
|
+
if (width && height) {
|
|
1303
|
+
const svgOverlay = createSvgOverlay(
|
|
1304
|
+
options.elementsPositionInfo,
|
|
1305
|
+
width,
|
|
1306
|
+
height
|
|
1307
|
+
);
|
|
1308
|
+
const svgOverlayWithoutText = createSvgOverlay(
|
|
1309
|
+
options.elementsPositionInfoWithoutText,
|
|
1310
|
+
width,
|
|
1311
|
+
height
|
|
1312
|
+
);
|
|
1313
|
+
const compositeElementInfoImgBase64 = await sharp(imageBuffer).composite([{ input: svgOverlay, blend: "over" }]).toBuffer().then((data) => {
|
|
1314
|
+
return data.toString("base64");
|
|
1315
|
+
}).catch((err) => {
|
|
1316
|
+
throw err;
|
|
1317
|
+
});
|
|
1318
|
+
const compositeElementInfoImgWithoutTextBase64 = await sharp(imageBuffer).composite([{ input: svgOverlayWithoutText, blend: "over" }]).toBuffer().then((data) => {
|
|
1319
|
+
return data.toString("base64");
|
|
1320
|
+
}).catch((err) => {
|
|
1321
|
+
throw err;
|
|
1322
|
+
});
|
|
1323
|
+
return {
|
|
1324
|
+
compositeElementInfoImgBase64,
|
|
1325
|
+
compositeElementInfoImgWithoutTextBase64
|
|
1326
|
+
};
|
|
1327
|
+
}
|
|
1328
|
+
throw Error("Image processing failed because width or height is undefined");
|
|
1329
|
+
};
|
|
1330
|
+
|
|
1331
|
+
// src/img/util.ts
|
|
1332
|
+
async function getElementInfos(page) {
|
|
1333
|
+
const captureElementSnapshot = await getElementInfosFromPage(page);
|
|
1334
|
+
const elementsPositionInfo = captureElementSnapshot.map((elementInfo) => {
|
|
1335
|
+
return {
|
|
1336
|
+
label: elementInfo.indexId.toString(),
|
|
1337
|
+
x: elementInfo.rect.left,
|
|
1338
|
+
y: elementInfo.rect.top,
|
|
1339
|
+
width: elementInfo.rect.width,
|
|
1340
|
+
height: elementInfo.rect.height,
|
|
1341
|
+
attributes: elementInfo.attributes
|
|
1342
|
+
};
|
|
1343
|
+
});
|
|
1344
|
+
const elementsPositionInfoWithoutText = elementsPositionInfo.filter(
|
|
1345
|
+
(elementInfo) => {
|
|
1346
|
+
if (elementInfo.attributes.nodeType === "TEXT Node" /* TEXT */) {
|
|
1347
|
+
return false;
|
|
1348
|
+
}
|
|
1349
|
+
return true;
|
|
1350
|
+
}
|
|
1351
|
+
);
|
|
1352
|
+
return {
|
|
1353
|
+
elementsPositionInfo,
|
|
1354
|
+
captureElementSnapshot,
|
|
1355
|
+
elementsPositionInfoWithoutText
|
|
1356
|
+
};
|
|
1357
|
+
}
|
|
1358
|
+
|
|
1359
|
+
// src/debug/index.ts
|
|
1360
|
+
import { resizeImg, saveBase64Image } from "@midscene/core/image";
|
|
1361
|
+
async function generateExtractData(page, targetDir, saveImgType) {
|
|
1362
|
+
const buffer = await page.screenshot({
|
|
1363
|
+
encoding: "base64"
|
|
1364
|
+
});
|
|
1365
|
+
const inputImgBase64 = buffer.toString("base64");
|
|
1366
|
+
const {
|
|
1367
|
+
elementsPositionInfo,
|
|
1368
|
+
captureElementSnapshot,
|
|
1369
|
+
elementsPositionInfoWithoutText
|
|
1370
|
+
} = await getElementInfos(page);
|
|
1371
|
+
const inputImagePath = path3.join(targetDir, "input.png");
|
|
1372
|
+
const outputImagePath = path3.join(targetDir, "output.png");
|
|
1373
|
+
const outputWithoutTextImgPath = path3.join(
|
|
1374
|
+
targetDir,
|
|
1375
|
+
"output_without_text.png"
|
|
1376
|
+
);
|
|
1377
|
+
const resizeOutputImgPath = path3.join(targetDir, "resize-output.png");
|
|
1378
|
+
const snapshotJsonPath = path3.join(targetDir, "element-snapshot.json");
|
|
1379
|
+
const {
|
|
1380
|
+
compositeElementInfoImgBase64,
|
|
1381
|
+
compositeElementInfoImgWithoutTextBase64
|
|
1382
|
+
} = await processImageElementInfo({
|
|
1383
|
+
elementsPositionInfo,
|
|
1384
|
+
elementsPositionInfoWithoutText,
|
|
1385
|
+
inputImgBase64
|
|
1386
|
+
});
|
|
1387
|
+
const resizeImgBase64 = await resizeImg(inputImgBase64);
|
|
1388
|
+
if (!(saveImgType == null ? void 0 : saveImgType.disableSnapshot)) {
|
|
1389
|
+
writeFileSyncWithDir(
|
|
1390
|
+
snapshotJsonPath,
|
|
1391
|
+
JSON.stringify(captureElementSnapshot, null, 2)
|
|
1392
|
+
);
|
|
1393
|
+
}
|
|
1394
|
+
if (!(saveImgType == null ? void 0 : saveImgType.disableInputImage)) {
|
|
1395
|
+
await saveBase64Image({
|
|
1396
|
+
base64Data: inputImgBase64,
|
|
1397
|
+
outputPath: inputImagePath
|
|
1398
|
+
});
|
|
1399
|
+
}
|
|
1400
|
+
if (!(saveImgType == null ? void 0 : saveImgType.disableOutputImage)) {
|
|
1401
|
+
await saveBase64Image({
|
|
1402
|
+
base64Data: compositeElementInfoImgBase64,
|
|
1403
|
+
outputPath: outputImagePath
|
|
1404
|
+
});
|
|
1405
|
+
}
|
|
1406
|
+
if (!(saveImgType == null ? void 0 : saveImgType.disableOutputWithoutTextImg)) {
|
|
1407
|
+
await saveBase64Image({
|
|
1408
|
+
base64Data: compositeElementInfoImgWithoutTextBase64,
|
|
1409
|
+
outputPath: outputWithoutTextImgPath
|
|
1410
|
+
});
|
|
1411
|
+
}
|
|
1412
|
+
if (!(saveImgType == null ? void 0 : saveImgType.disableResizeOutputImg)) {
|
|
1413
|
+
await saveBase64Image({
|
|
1414
|
+
base64Data: resizeImgBase64,
|
|
1415
|
+
outputPath: resizeOutputImgPath
|
|
1416
|
+
});
|
|
1417
|
+
}
|
|
1418
|
+
}
|
|
1419
|
+
function ensureDirectoryExistence(filePath) {
|
|
1420
|
+
const dirname2 = path3.dirname(filePath);
|
|
1421
|
+
if (existsSync(dirname2)) {
|
|
1422
|
+
return;
|
|
1423
|
+
}
|
|
1424
|
+
ensureDirectoryExistence(dirname2);
|
|
1425
|
+
mkdirSync(dirname2);
|
|
1426
|
+
}
|
|
1427
|
+
function writeFileSyncWithDir(filePath, content, options = {}) {
|
|
1428
|
+
ensureDirectoryExistence(filePath);
|
|
1429
|
+
writeFileSync(filePath, content, options);
|
|
1430
|
+
}
|
|
1130
1431
|
export {
|
|
1131
1432
|
PlaywrightAiFixture,
|
|
1132
|
-
PageAgent as PuppeteerAgent
|
|
1433
|
+
PageAgent as PuppeteerAgent,
|
|
1434
|
+
generateExtractData
|
|
1133
1435
|
};
|