@midscene/web 0.7.1 → 0.7.2-beta-20241024094141.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/browser/playground.js +8438 -0
- package/dist/browser/types/playground.d.ts +313 -0
- package/dist/es/appium.js +680 -604
- package/dist/es/debug.js +95 -73
- package/dist/es/index.js +939 -797
- package/dist/es/midscene-playground.js +678 -609
- package/dist/es/playground.js +593 -1024
- package/dist/es/playwright-report.js +29 -11
- package/dist/es/playwright.js +705 -597
- package/dist/es/puppeteer.js +636 -552
- package/dist/lib/appium.js +688 -609
- package/dist/lib/debug.js +95 -73
- package/dist/lib/index.js +950 -804
- package/dist/lib/midscene-playground.js +687 -615
- package/dist/lib/playground.js +586 -1007
- package/dist/lib/playwright-report.js +30 -9
- package/dist/lib/playwright.js +713 -602
- package/dist/lib/puppeteer.js +644 -557
- package/dist/script/htmlElement.js +11 -10
- package/dist/script/htmlElementDebug.js +11 -10
- package/dist/types/appium.d.ts +2 -3
- package/dist/types/debug.d.ts +1 -1
- package/dist/types/index.d.ts +3 -3
- package/dist/types/{page-ad820b3c.d.ts → page-8117b0ad.d.ts} +8 -7
- package/dist/types/playground.d.ts +6 -21
- package/dist/types/playwright.d.ts +3 -4
- package/dist/types/puppeteer.d.ts +2 -3
- package/dist/types/{tasks-82c1054b.d.ts → tasks-cb6bf758.d.ts} +6 -6
- package/package.json +11 -5
- package/static/index.html +1 -1
package/dist/es/index.js
CHANGED
|
@@ -47,6 +47,26 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
47
47
|
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
48
48
|
mod
|
|
49
49
|
));
|
|
50
|
+
var __async = (__this, __arguments, generator) => {
|
|
51
|
+
return new Promise((resolve, reject) => {
|
|
52
|
+
var fulfilled = (value) => {
|
|
53
|
+
try {
|
|
54
|
+
step(generator.next(value));
|
|
55
|
+
} catch (e) {
|
|
56
|
+
reject(e);
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
var rejected = (value) => {
|
|
60
|
+
try {
|
|
61
|
+
step(generator.throw(value));
|
|
62
|
+
} catch (e) {
|
|
63
|
+
reject(e);
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected);
|
|
67
|
+
step((generator = generator.apply(__this, __arguments)).next());
|
|
68
|
+
});
|
|
69
|
+
};
|
|
50
70
|
|
|
51
71
|
// ../../node_modules/.pnpm/dayjs@1.11.11/node_modules/dayjs/dayjs.min.js
|
|
52
72
|
var require_dayjs_min = __commonJS({
|
|
@@ -863,7 +883,7 @@ var require_sha256 = __commonJS({
|
|
|
863
883
|
});
|
|
864
884
|
|
|
865
885
|
// src/playwright/ai-fixture.ts
|
|
866
|
-
import { randomUUID
|
|
886
|
+
import { randomUUID } from "crypto";
|
|
867
887
|
|
|
868
888
|
// src/common/agent.ts
|
|
869
889
|
import {
|
|
@@ -880,7 +900,6 @@ import {
|
|
|
880
900
|
plan
|
|
881
901
|
} from "@midscene/core";
|
|
882
902
|
import { sleep } from "@midscene/core/utils";
|
|
883
|
-
import { base64Encoded as base64Encoded2 } from "@midscene/shared/img";
|
|
884
903
|
|
|
885
904
|
// src/common/task-cache.ts
|
|
886
905
|
import { existsSync, readFileSync as readFileSync2 } from "fs";
|
|
@@ -890,21 +909,19 @@ import {
|
|
|
890
909
|
stringifyDumpData,
|
|
891
910
|
writeLogFile
|
|
892
911
|
} from "@midscene/core/utils";
|
|
893
|
-
import {
|
|
912
|
+
import { getRunningPkgInfo } from "@midscene/shared/fs";
|
|
913
|
+
import { ifInBrowser } from "@midscene/shared/utils";
|
|
894
914
|
|
|
895
915
|
// src/common/utils.ts
|
|
896
916
|
var import_dayjs = __toESM(require_dayjs_min());
|
|
897
917
|
import assert from "assert";
|
|
898
|
-
import { randomUUID } from "crypto";
|
|
899
918
|
import { readFileSync } from "fs";
|
|
900
919
|
import path from "path";
|
|
901
920
|
import { NodeType } from "@midscene/shared/constants";
|
|
902
921
|
import { findNearestPackageJson } from "@midscene/shared/fs";
|
|
903
|
-
import {
|
|
904
|
-
base64Encoded,
|
|
905
|
-
imageInfoOfBase64
|
|
906
|
-
} from "@midscene/shared/img";
|
|
922
|
+
import { imageInfoOfBase64 } from "@midscene/shared/img";
|
|
907
923
|
import { compositeElementInfoImg } from "@midscene/shared/img";
|
|
924
|
+
import { uuid } from "@midscene/shared/utils";
|
|
908
925
|
|
|
909
926
|
// src/web-element.ts
|
|
910
927
|
var WebElementInfo = class {
|
|
@@ -932,63 +949,68 @@ var WebElementInfo = class {
|
|
|
932
949
|
};
|
|
933
950
|
|
|
934
951
|
// src/common/utils.ts
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
const url = page.url();
|
|
941
|
-
const file = await page.screenshot();
|
|
942
|
-
const screenshotBase64 = base64Encoded(file);
|
|
943
|
-
const captureElementSnapshot = await page.getElementInfos();
|
|
944
|
-
const elementsInfo = await alignElements(captureElementSnapshot, page);
|
|
945
|
-
const elementsPositionInfoWithoutText = elementsInfo.filter((elementInfo) => {
|
|
946
|
-
if (elementInfo.attributes.nodeType === NodeType.TEXT) {
|
|
947
|
-
return false;
|
|
952
|
+
function parseContextFromWebPage(page, _opt) {
|
|
953
|
+
return __async(this, null, function* () {
|
|
954
|
+
assert(page, "page is required");
|
|
955
|
+
if (page._forceUsePageContext) {
|
|
956
|
+
return yield page._forceUsePageContext();
|
|
948
957
|
}
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
958
|
+
const url = page.url();
|
|
959
|
+
const screenshotBase64 = yield page.screenshotBase64();
|
|
960
|
+
const captureElementSnapshot = yield page.getElementInfos();
|
|
961
|
+
const elementsInfo = yield alignElements(captureElementSnapshot, page);
|
|
962
|
+
const elementsPositionInfoWithoutText = elementsInfo.filter((elementInfo) => {
|
|
963
|
+
if (elementInfo.attributes.nodeType === NodeType.TEXT) {
|
|
964
|
+
return false;
|
|
965
|
+
}
|
|
966
|
+
return true;
|
|
967
|
+
});
|
|
968
|
+
const size = yield imageInfoOfBase64(screenshotBase64);
|
|
969
|
+
const screenshotBase64WithElementMarker = yield compositeElementInfoImg({
|
|
970
|
+
inputImgBase64: screenshotBase64,
|
|
971
|
+
elementsPositionInfo: elementsPositionInfoWithoutText
|
|
972
|
+
});
|
|
973
|
+
return {
|
|
974
|
+
content: elementsInfo,
|
|
975
|
+
size,
|
|
976
|
+
screenshotBase64,
|
|
977
|
+
screenshotBase64WithElementMarker: `data:image/png;base64,${screenshotBase64WithElementMarker}`,
|
|
978
|
+
url
|
|
979
|
+
};
|
|
955
980
|
});
|
|
956
|
-
return {
|
|
957
|
-
content: elementsInfo,
|
|
958
|
-
size,
|
|
959
|
-
screenshotBase64,
|
|
960
|
-
screenshotBase64WithElementMarker: `data:image/png;base64,${screenshotBase64WithElementMarker}`,
|
|
961
|
-
url
|
|
962
|
-
};
|
|
963
981
|
}
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
982
|
+
function getExtraReturnLogic() {
|
|
983
|
+
return __async(this, null, function* () {
|
|
984
|
+
const pathDir = findNearestPackageJson(__dirname);
|
|
985
|
+
assert(pathDir, `can't find pathDir, with ${__dirname}`);
|
|
986
|
+
const scriptPath = path.join(pathDir, "./dist/script/htmlElement.js");
|
|
987
|
+
const elementInfosScriptContent = readFileSync(scriptPath, "utf-8");
|
|
988
|
+
return `${elementInfosScriptContent}midscene_element_inspector.webExtractTextWithPosition()`;
|
|
989
|
+
});
|
|
970
990
|
}
|
|
971
991
|
var sizeThreshold = 3;
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
992
|
+
function alignElements(elements, page) {
|
|
993
|
+
return __async(this, null, function* () {
|
|
994
|
+
const validElements = elements.filter((item) => {
|
|
995
|
+
return item.rect.height >= sizeThreshold && item.rect.width >= sizeThreshold;
|
|
996
|
+
});
|
|
997
|
+
const textsAligned = [];
|
|
998
|
+
for (const item of validElements) {
|
|
999
|
+
const { rect, id, content, attributes, locator, indexId } = item;
|
|
1000
|
+
textsAligned.push(
|
|
1001
|
+
new WebElementInfo({
|
|
1002
|
+
rect,
|
|
1003
|
+
locator,
|
|
1004
|
+
id,
|
|
1005
|
+
content,
|
|
1006
|
+
attributes,
|
|
1007
|
+
page,
|
|
1008
|
+
indexId
|
|
1009
|
+
})
|
|
1010
|
+
);
|
|
1011
|
+
}
|
|
1012
|
+
return textsAligned;
|
|
975
1013
|
});
|
|
976
|
-
const textsAligned = [];
|
|
977
|
-
for (const item of validElements) {
|
|
978
|
-
const { rect, id, content, attributes, locator, indexId } = item;
|
|
979
|
-
textsAligned.push(
|
|
980
|
-
new WebElementInfo({
|
|
981
|
-
rect,
|
|
982
|
-
locator,
|
|
983
|
-
id,
|
|
984
|
-
content,
|
|
985
|
-
attributes,
|
|
986
|
-
page,
|
|
987
|
-
indexId
|
|
988
|
-
})
|
|
989
|
-
);
|
|
990
|
-
}
|
|
991
|
-
return textsAligned;
|
|
992
1014
|
}
|
|
993
1015
|
function reportFileName(tag = "web") {
|
|
994
1016
|
const dateTimeInFileName = (0, import_dayjs.default)().format("YYYY-MM-DD_HH-mm-ss-SSS");
|
|
@@ -1019,7 +1041,7 @@ var testFileIndex = /* @__PURE__ */ new Map();
|
|
|
1019
1041
|
function generateCacheId(fileName) {
|
|
1020
1042
|
let taskFile = fileName || getCurrentExecutionFile();
|
|
1021
1043
|
if (!taskFile) {
|
|
1022
|
-
taskFile =
|
|
1044
|
+
taskFile = uuid();
|
|
1023
1045
|
console.warn(
|
|
1024
1046
|
"Midscene - using random UUID for cache id. Cache may be invalid."
|
|
1025
1047
|
);
|
|
@@ -1038,7 +1060,7 @@ function generateCacheId(fileName) {
|
|
|
1038
1060
|
// src/common/task-cache.ts
|
|
1039
1061
|
var TaskCache = class {
|
|
1040
1062
|
constructor(opts) {
|
|
1041
|
-
this.midscenePkgInfo =
|
|
1063
|
+
this.midscenePkgInfo = getRunningPkgInfo();
|
|
1042
1064
|
this.cacheId = generateCacheId(opts == null ? void 0 : opts.fileName);
|
|
1043
1065
|
this.cache = this.readCacheFromFile() || {
|
|
1044
1066
|
aiTasks: []
|
|
@@ -1120,11 +1142,17 @@ var TaskCache = class {
|
|
|
1120
1142
|
return this.newCache;
|
|
1121
1143
|
}
|
|
1122
1144
|
readCacheFromFile() {
|
|
1145
|
+
if (ifInBrowser) {
|
|
1146
|
+
return void 0;
|
|
1147
|
+
}
|
|
1123
1148
|
const cacheFile = join(getLogDirByType("cache"), `${this.cacheId}.json`);
|
|
1124
1149
|
if (process.env.MIDSCENE_CACHE === "true" && existsSync(cacheFile)) {
|
|
1125
1150
|
try {
|
|
1126
1151
|
const data = readFileSync2(cacheFile, "utf8");
|
|
1127
1152
|
const jsonData = JSON.parse(data);
|
|
1153
|
+
if (!this.midscenePkgInfo) {
|
|
1154
|
+
return void 0;
|
|
1155
|
+
}
|
|
1128
1156
|
if (jsonData.pkgName !== this.midscenePkgInfo.name || jsonData.pkgVersion !== this.midscenePkgInfo.version) {
|
|
1129
1157
|
return void 0;
|
|
1130
1158
|
}
|
|
@@ -1136,7 +1164,10 @@ var TaskCache = class {
|
|
|
1136
1164
|
return void 0;
|
|
1137
1165
|
}
|
|
1138
1166
|
writeCacheToFile() {
|
|
1139
|
-
const midscenePkgInfo =
|
|
1167
|
+
const midscenePkgInfo = getRunningPkgInfo();
|
|
1168
|
+
if (!midscenePkgInfo) {
|
|
1169
|
+
return;
|
|
1170
|
+
}
|
|
1140
1171
|
writeLogFile({
|
|
1141
1172
|
fileName: `${this.cacheId}`,
|
|
1142
1173
|
fileExt: "json",
|
|
@@ -1157,423 +1188,435 @@ var TaskCache = class {
|
|
|
1157
1188
|
var PageTaskExecutor = class {
|
|
1158
1189
|
constructor(page, opts) {
|
|
1159
1190
|
this.page = page;
|
|
1160
|
-
this.insight = new Insight(
|
|
1161
|
-
return
|
|
1162
|
-
});
|
|
1191
|
+
this.insight = new Insight(() => __async(this, null, function* () {
|
|
1192
|
+
return yield parseContextFromWebPage(page);
|
|
1193
|
+
}));
|
|
1163
1194
|
this.taskCache = new TaskCache({
|
|
1164
1195
|
fileName: opts == null ? void 0 : opts.cacheId
|
|
1165
1196
|
});
|
|
1166
1197
|
}
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1198
|
+
recordScreenshot(timing) {
|
|
1199
|
+
return __async(this, null, function* () {
|
|
1200
|
+
const base64 = yield this.page.screenshotBase64();
|
|
1201
|
+
const item = {
|
|
1202
|
+
type: "screenshot",
|
|
1203
|
+
ts: Date.now(),
|
|
1204
|
+
screenshot: base64,
|
|
1205
|
+
timing
|
|
1206
|
+
};
|
|
1207
|
+
return item;
|
|
1208
|
+
});
|
|
1176
1209
|
}
|
|
1177
1210
|
wrapExecutorWithScreenshot(taskApply) {
|
|
1178
1211
|
const taskWithScreenshot = __spreadProps(__spreadValues({}, taskApply), {
|
|
1179
|
-
executor:
|
|
1212
|
+
executor: (param, context, ...args) => __async(this, null, function* () {
|
|
1180
1213
|
const recorder = [];
|
|
1181
1214
|
const { task } = context;
|
|
1182
1215
|
task.recorder = recorder;
|
|
1183
|
-
const shot =
|
|
1216
|
+
const shot = yield this.recordScreenshot(`before ${task.type}`);
|
|
1184
1217
|
recorder.push(shot);
|
|
1185
|
-
const result =
|
|
1218
|
+
const result = yield taskApply.executor(param, context, ...args);
|
|
1186
1219
|
if (taskApply.type === "Action") {
|
|
1187
|
-
|
|
1188
|
-
const shot2 =
|
|
1220
|
+
yield sleep(1e3);
|
|
1221
|
+
const shot2 = yield this.recordScreenshot("after Action");
|
|
1189
1222
|
recorder.push(shot2);
|
|
1190
1223
|
}
|
|
1191
1224
|
return result;
|
|
1192
|
-
}
|
|
1225
|
+
})
|
|
1193
1226
|
});
|
|
1194
1227
|
return taskWithScreenshot;
|
|
1195
1228
|
}
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
this.insight.onceDumpUpdatedFn = dumpCollector;
|
|
1210
|
-
const pageContext = await this.insight.contextRetrieverFn();
|
|
1211
|
-
const locateCache = cacheGroup == null ? void 0 : cacheGroup.readCache(
|
|
1212
|
-
pageContext,
|
|
1213
|
-
"locate",
|
|
1214
|
-
param.prompt
|
|
1215
|
-
);
|
|
1216
|
-
let locateResult;
|
|
1217
|
-
const callAI = this.insight.aiVendorFn;
|
|
1218
|
-
const element = await this.insight.locate(param.prompt, {
|
|
1219
|
-
quickAnswer: plan2.quickAnswer,
|
|
1220
|
-
callAI: async (...message) => {
|
|
1221
|
-
if (locateCache) {
|
|
1222
|
-
locateResult = locateCache;
|
|
1223
|
-
return Promise.resolve(locateCache);
|
|
1224
|
-
}
|
|
1225
|
-
locateResult = await callAI(...message);
|
|
1226
|
-
assert2(locateResult);
|
|
1227
|
-
return locateResult;
|
|
1228
|
-
}
|
|
1229
|
-
});
|
|
1230
|
-
if (locateResult) {
|
|
1231
|
-
cacheGroup == null ? void 0 : cacheGroup.saveCache({
|
|
1232
|
-
type: "locate",
|
|
1233
|
-
pageContext: {
|
|
1234
|
-
url: pageContext.url,
|
|
1235
|
-
size: pageContext.size
|
|
1236
|
-
},
|
|
1237
|
-
prompt: param.prompt,
|
|
1238
|
-
response: locateResult
|
|
1239
|
-
});
|
|
1240
|
-
}
|
|
1241
|
-
if (!element) {
|
|
1242
|
-
task.log = {
|
|
1243
|
-
dump: insightDump
|
|
1229
|
+
convertPlanToExecutable(plans, cacheGroup) {
|
|
1230
|
+
return __async(this, null, function* () {
|
|
1231
|
+
const tasks = plans.map((plan2) => {
|
|
1232
|
+
if (plan2.type === "Locate") {
|
|
1233
|
+
const taskFind = {
|
|
1234
|
+
type: "Insight",
|
|
1235
|
+
subType: "Locate",
|
|
1236
|
+
param: plan2.param,
|
|
1237
|
+
executor: (param, taskContext) => __async(this, null, function* () {
|
|
1238
|
+
const { task } = taskContext;
|
|
1239
|
+
let insightDump;
|
|
1240
|
+
const dumpCollector = (dump) => {
|
|
1241
|
+
insightDump = dump;
|
|
1244
1242
|
};
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1243
|
+
this.insight.onceDumpUpdatedFn = dumpCollector;
|
|
1244
|
+
const pageContext = yield this.insight.contextRetrieverFn();
|
|
1245
|
+
const locateCache = cacheGroup == null ? void 0 : cacheGroup.readCache(
|
|
1246
|
+
pageContext,
|
|
1247
|
+
"locate",
|
|
1248
|
+
param.prompt
|
|
1249
|
+
);
|
|
1250
|
+
let locateResult;
|
|
1251
|
+
const callAI = this.insight.aiVendorFn;
|
|
1252
|
+
const element = yield this.insight.locate(param.prompt, {
|
|
1253
|
+
quickAnswer: plan2.quickAnswer,
|
|
1254
|
+
callAI: (...message) => __async(this, null, function* () {
|
|
1255
|
+
if (locateCache) {
|
|
1256
|
+
locateResult = locateCache;
|
|
1257
|
+
return Promise.resolve(locateCache);
|
|
1258
|
+
}
|
|
1259
|
+
locateResult = yield callAI(...message);
|
|
1260
|
+
assert2(locateResult);
|
|
1261
|
+
return locateResult;
|
|
1262
|
+
})
|
|
1263
|
+
});
|
|
1264
|
+
if (locateResult) {
|
|
1265
|
+
cacheGroup == null ? void 0 : cacheGroup.saveCache({
|
|
1266
|
+
type: "locate",
|
|
1267
|
+
pageContext: {
|
|
1268
|
+
url: pageContext.url,
|
|
1269
|
+
size: pageContext.size
|
|
1270
|
+
},
|
|
1271
|
+
prompt: param.prompt,
|
|
1272
|
+
response: locateResult
|
|
1273
|
+
});
|
|
1256
1274
|
}
|
|
1257
|
-
|
|
1258
|
-
}
|
|
1259
|
-
};
|
|
1260
|
-
return taskFind;
|
|
1261
|
-
}
|
|
1262
|
-
if (plan2.type === "Assert" || plan2.type === "AssertWithoutThrow") {
|
|
1263
|
-
const assertPlan = plan2;
|
|
1264
|
-
const taskAssert = {
|
|
1265
|
-
type: "Insight",
|
|
1266
|
-
subType: "Assert",
|
|
1267
|
-
param: assertPlan.param,
|
|
1268
|
-
executor: async (param, taskContext) => {
|
|
1269
|
-
const { task } = taskContext;
|
|
1270
|
-
let insightDump;
|
|
1271
|
-
const dumpCollector = (dump) => {
|
|
1272
|
-
insightDump = dump;
|
|
1273
|
-
};
|
|
1274
|
-
this.insight.onceDumpUpdatedFn = dumpCollector;
|
|
1275
|
-
const assertion = await this.insight.assert(
|
|
1276
|
-
assertPlan.param.assertion
|
|
1277
|
-
);
|
|
1278
|
-
if (!assertion.pass) {
|
|
1279
|
-
if (plan2.type === "Assert") {
|
|
1280
|
-
task.output = assertion;
|
|
1275
|
+
if (!element) {
|
|
1281
1276
|
task.log = {
|
|
1282
1277
|
dump: insightDump
|
|
1283
1278
|
};
|
|
1284
|
-
throw new Error(
|
|
1285
|
-
assertion.thought || "Assertion failed without reason"
|
|
1286
|
-
);
|
|
1279
|
+
throw new Error(`Element not found: ${param.prompt}`);
|
|
1287
1280
|
}
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1281
|
+
return {
|
|
1282
|
+
output: {
|
|
1283
|
+
element
|
|
1284
|
+
},
|
|
1285
|
+
log: {
|
|
1286
|
+
dump: insightDump
|
|
1287
|
+
},
|
|
1288
|
+
cache: {
|
|
1289
|
+
hit: Boolean(locateCache)
|
|
1290
|
+
}
|
|
1291
|
+
};
|
|
1292
|
+
})
|
|
1293
|
+
};
|
|
1294
|
+
return taskFind;
|
|
1295
|
+
}
|
|
1296
|
+
if (plan2.type === "Assert" || plan2.type === "AssertWithoutThrow") {
|
|
1297
|
+
const assertPlan = plan2;
|
|
1298
|
+
const taskAssert = {
|
|
1299
|
+
type: "Insight",
|
|
1300
|
+
subType: "Assert",
|
|
1301
|
+
param: assertPlan.param,
|
|
1302
|
+
executor: (param, taskContext) => __async(this, null, function* () {
|
|
1303
|
+
const { task } = taskContext;
|
|
1304
|
+
let insightDump;
|
|
1305
|
+
const dumpCollector = (dump) => {
|
|
1306
|
+
insightDump = dump;
|
|
1307
|
+
};
|
|
1308
|
+
this.insight.onceDumpUpdatedFn = dumpCollector;
|
|
1309
|
+
const assertion = yield this.insight.assert(
|
|
1310
|
+
assertPlan.param.assertion
|
|
1311
|
+
);
|
|
1312
|
+
if (!assertion.pass) {
|
|
1313
|
+
if (plan2.type === "Assert") {
|
|
1314
|
+
task.output = assertion;
|
|
1315
|
+
task.log = {
|
|
1316
|
+
dump: insightDump
|
|
1317
|
+
};
|
|
1318
|
+
throw new Error(
|
|
1319
|
+
assertion.thought || "Assertion failed without reason"
|
|
1320
|
+
);
|
|
1321
|
+
}
|
|
1322
|
+
task.error = assertion.thought;
|
|
1294
1323
|
}
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1324
|
+
return {
|
|
1325
|
+
output: assertion,
|
|
1326
|
+
log: {
|
|
1327
|
+
dump: insightDump
|
|
1328
|
+
}
|
|
1329
|
+
};
|
|
1330
|
+
})
|
|
1331
|
+
};
|
|
1332
|
+
return taskAssert;
|
|
1333
|
+
}
|
|
1334
|
+
if (plan2.type === "Input") {
|
|
1335
|
+
const taskActionInput = {
|
|
1336
|
+
type: "Action",
|
|
1337
|
+
subType: "Input",
|
|
1338
|
+
param: plan2.param,
|
|
1339
|
+
executor: (_0, _1) => __async(this, [_0, _1], function* (taskParam, { element }) {
|
|
1340
|
+
if (element) {
|
|
1341
|
+
yield this.page.clearInput(element);
|
|
1342
|
+
if (taskParam.value === "") {
|
|
1343
|
+
return;
|
|
1344
|
+
}
|
|
1345
|
+
yield this.page.keyboard.type(taskParam.value);
|
|
1310
1346
|
}
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
}
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
}
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
}
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
}
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
}
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
}
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
)
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
}
|
|
1411
|
-
|
|
1412
|
-
}
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
return
|
|
1347
|
+
})
|
|
1348
|
+
};
|
|
1349
|
+
return taskActionInput;
|
|
1350
|
+
}
|
|
1351
|
+
if (plan2.type === "KeyboardPress") {
|
|
1352
|
+
const taskActionKeyboardPress = {
|
|
1353
|
+
type: "Action",
|
|
1354
|
+
subType: "KeyboardPress",
|
|
1355
|
+
param: plan2.param,
|
|
1356
|
+
executor: (taskParam) => __async(this, null, function* () {
|
|
1357
|
+
assert2(taskParam.value, "No key to press");
|
|
1358
|
+
yield this.page.keyboard.press(taskParam.value);
|
|
1359
|
+
})
|
|
1360
|
+
};
|
|
1361
|
+
return taskActionKeyboardPress;
|
|
1362
|
+
}
|
|
1363
|
+
if (plan2.type === "Tap") {
|
|
1364
|
+
const taskActionTap = {
|
|
1365
|
+
type: "Action",
|
|
1366
|
+
subType: "Tap",
|
|
1367
|
+
executor: (_0, _1) => __async(this, [_0, _1], function* (param, { element }) {
|
|
1368
|
+
assert2(element, "Element not found, cannot tap");
|
|
1369
|
+
yield this.page.mouse.click(
|
|
1370
|
+
element.center[0],
|
|
1371
|
+
element.center[1]
|
|
1372
|
+
);
|
|
1373
|
+
})
|
|
1374
|
+
};
|
|
1375
|
+
return taskActionTap;
|
|
1376
|
+
}
|
|
1377
|
+
if (plan2.type === "Hover") {
|
|
1378
|
+
const taskActionHover = {
|
|
1379
|
+
type: "Action",
|
|
1380
|
+
subType: "Hover",
|
|
1381
|
+
executor: (_0, _1) => __async(this, [_0, _1], function* (param, { element }) {
|
|
1382
|
+
assert2(element, "Element not found, cannot hover");
|
|
1383
|
+
yield this.page.mouse.move(
|
|
1384
|
+
element.center[0],
|
|
1385
|
+
element.center[1]
|
|
1386
|
+
);
|
|
1387
|
+
})
|
|
1388
|
+
};
|
|
1389
|
+
return taskActionHover;
|
|
1390
|
+
}
|
|
1391
|
+
if (plan2.type === "Scroll") {
|
|
1392
|
+
const taskActionScroll = {
|
|
1393
|
+
type: "Action",
|
|
1394
|
+
subType: "Scroll",
|
|
1395
|
+
param: plan2.param,
|
|
1396
|
+
executor: (taskParam) => __async(this, null, function* () {
|
|
1397
|
+
const scrollToEventName = taskParam.scrollType;
|
|
1398
|
+
switch (scrollToEventName) {
|
|
1399
|
+
case "scrollUntilTop":
|
|
1400
|
+
yield this.page.scrollUntilTop();
|
|
1401
|
+
break;
|
|
1402
|
+
case "scrollUntilBottom":
|
|
1403
|
+
yield this.page.scrollUntilBottom();
|
|
1404
|
+
break;
|
|
1405
|
+
case "scrollUpOneScreen":
|
|
1406
|
+
yield this.page.scrollUpOneScreen();
|
|
1407
|
+
break;
|
|
1408
|
+
case "scrollDownOneScreen":
|
|
1409
|
+
yield this.page.scrollDownOneScreen();
|
|
1410
|
+
break;
|
|
1411
|
+
default:
|
|
1412
|
+
console.error(
|
|
1413
|
+
"Unknown scroll event type:",
|
|
1414
|
+
scrollToEventName
|
|
1415
|
+
);
|
|
1416
|
+
}
|
|
1417
|
+
})
|
|
1418
|
+
};
|
|
1419
|
+
return taskActionScroll;
|
|
1420
|
+
}
|
|
1421
|
+
if (plan2.type === "Sleep") {
|
|
1422
|
+
const taskActionSleep = {
|
|
1423
|
+
type: "Action",
|
|
1424
|
+
subType: "Sleep",
|
|
1425
|
+
param: plan2.param,
|
|
1426
|
+
executor: (taskParam) => __async(this, null, function* () {
|
|
1427
|
+
yield sleep(taskParam.timeMs || 3e3);
|
|
1428
|
+
})
|
|
1429
|
+
};
|
|
1430
|
+
return taskActionSleep;
|
|
1431
|
+
}
|
|
1432
|
+
if (plan2.type === "Error") {
|
|
1433
|
+
const taskActionError = {
|
|
1434
|
+
type: "Action",
|
|
1435
|
+
subType: "Error",
|
|
1436
|
+
param: plan2.param,
|
|
1437
|
+
executor: (taskParam) => __async(this, null, function* () {
|
|
1438
|
+
assert2(
|
|
1439
|
+
taskParam.thought,
|
|
1440
|
+
"An error occurred, but no thought provided"
|
|
1441
|
+
);
|
|
1442
|
+
throw new Error(taskParam.thought);
|
|
1443
|
+
})
|
|
1444
|
+
};
|
|
1445
|
+
return taskActionError;
|
|
1446
|
+
}
|
|
1447
|
+
throw new Error(`Unknown or Unsupported task type: ${plan2.type}`);
|
|
1448
|
+
}).map((task) => {
|
|
1449
|
+
return this.wrapExecutorWithScreenshot(task);
|
|
1450
|
+
});
|
|
1451
|
+
return tasks;
|
|
1416
1452
|
});
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1453
|
+
}
|
|
1454
|
+
action(userPrompt) {
|
|
1455
|
+
return __async(this, null, function* () {
|
|
1456
|
+
const taskExecutor = new Executor(userPrompt);
|
|
1457
|
+
const cacheGroup = this.taskCache.getCacheGroupByPrompt(userPrompt);
|
|
1458
|
+
let plans = [];
|
|
1459
|
+
const planningTask = {
|
|
1460
|
+
type: "Planning",
|
|
1461
|
+
param: {
|
|
1462
|
+
userPrompt
|
|
1463
|
+
},
|
|
1464
|
+
executor: (param) => __async(this, null, function* () {
|
|
1465
|
+
const pageContext = yield this.insight.contextRetrieverFn();
|
|
1466
|
+
let planResult;
|
|
1467
|
+
const planCache = cacheGroup.readCache(pageContext, "plan", userPrompt);
|
|
1468
|
+
if (planCache) {
|
|
1469
|
+
planResult = planCache;
|
|
1470
|
+
} else {
|
|
1471
|
+
planResult = yield plan(param.userPrompt, {
|
|
1472
|
+
context: pageContext
|
|
1473
|
+
});
|
|
1474
|
+
}
|
|
1475
|
+
assert2(planResult.plans.length > 0, "No plans found");
|
|
1476
|
+
plans = planResult.plans;
|
|
1477
|
+
cacheGroup.saveCache({
|
|
1478
|
+
type: "plan",
|
|
1479
|
+
pageContext: {
|
|
1480
|
+
url: pageContext.url,
|
|
1481
|
+
size: pageContext.size
|
|
1482
|
+
},
|
|
1483
|
+
prompt: userPrompt,
|
|
1484
|
+
response: planResult
|
|
1437
1485
|
});
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
});
|
|
1486
|
+
return {
|
|
1487
|
+
output: planResult,
|
|
1488
|
+
cache: {
|
|
1489
|
+
hit: Boolean(planCache)
|
|
1490
|
+
}
|
|
1491
|
+
};
|
|
1492
|
+
})
|
|
1493
|
+
};
|
|
1494
|
+
yield taskExecutor.append(this.wrapExecutorWithScreenshot(planningTask));
|
|
1495
|
+
let output = yield taskExecutor.flush();
|
|
1496
|
+
if (taskExecutor.isInErrorState()) {
|
|
1450
1497
|
return {
|
|
1451
|
-
output
|
|
1452
|
-
|
|
1453
|
-
hit: Boolean(planCache)
|
|
1454
|
-
}
|
|
1498
|
+
output,
|
|
1499
|
+
executor: taskExecutor
|
|
1455
1500
|
};
|
|
1456
1501
|
}
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
if (taskExecutor.isInErrorState()) {
|
|
1502
|
+
const executables = yield this.convertPlanToExecutable(plans, cacheGroup);
|
|
1503
|
+
yield taskExecutor.append(executables);
|
|
1504
|
+
output = yield taskExecutor.flush();
|
|
1461
1505
|
return {
|
|
1462
1506
|
output,
|
|
1463
1507
|
executor: taskExecutor
|
|
1464
1508
|
};
|
|
1465
|
-
}
|
|
1466
|
-
const executables = await this.convertPlanToExecutable(plans, cacheGroup);
|
|
1467
|
-
await taskExecutor.append(executables);
|
|
1468
|
-
output = await taskExecutor.flush();
|
|
1469
|
-
return {
|
|
1470
|
-
output,
|
|
1471
|
-
executor: taskExecutor
|
|
1472
|
-
};
|
|
1473
|
-
}
|
|
1474
|
-
async query(demand) {
|
|
1475
|
-
const description = typeof demand === "string" ? demand : JSON.stringify(demand);
|
|
1476
|
-
const taskExecutor = new Executor(description);
|
|
1477
|
-
const queryTask = {
|
|
1478
|
-
type: "Insight",
|
|
1479
|
-
subType: "Query",
|
|
1480
|
-
param: {
|
|
1481
|
-
dataDemand: demand
|
|
1482
|
-
},
|
|
1483
|
-
executor: async (param) => {
|
|
1484
|
-
let insightDump;
|
|
1485
|
-
const dumpCollector = (dump) => {
|
|
1486
|
-
insightDump = dump;
|
|
1487
|
-
};
|
|
1488
|
-
this.insight.onceDumpUpdatedFn = dumpCollector;
|
|
1489
|
-
const data = await this.insight.extract(param.dataDemand);
|
|
1490
|
-
return {
|
|
1491
|
-
output: data,
|
|
1492
|
-
log: { dump: insightDump }
|
|
1493
|
-
};
|
|
1494
|
-
}
|
|
1495
|
-
};
|
|
1496
|
-
await taskExecutor.append(this.wrapExecutorWithScreenshot(queryTask));
|
|
1497
|
-
const output = await taskExecutor.flush();
|
|
1498
|
-
return {
|
|
1499
|
-
output,
|
|
1500
|
-
executor: taskExecutor
|
|
1501
|
-
};
|
|
1509
|
+
});
|
|
1502
1510
|
}
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1511
|
+
query(demand) {
|
|
1512
|
+
return __async(this, null, function* () {
|
|
1513
|
+
const description = typeof demand === "string" ? demand : JSON.stringify(demand);
|
|
1514
|
+
const taskExecutor = new Executor(description);
|
|
1515
|
+
const queryTask = {
|
|
1516
|
+
type: "Insight",
|
|
1517
|
+
subType: "Query",
|
|
1518
|
+
param: {
|
|
1519
|
+
dataDemand: demand
|
|
1520
|
+
},
|
|
1521
|
+
executor: (param) => __async(this, null, function* () {
|
|
1522
|
+
let insightDump;
|
|
1523
|
+
const dumpCollector = (dump) => {
|
|
1524
|
+
insightDump = dump;
|
|
1525
|
+
};
|
|
1526
|
+
this.insight.onceDumpUpdatedFn = dumpCollector;
|
|
1527
|
+
const data = yield this.insight.extract(param.dataDemand);
|
|
1528
|
+
return {
|
|
1529
|
+
output: data,
|
|
1530
|
+
log: { dump: insightDump }
|
|
1531
|
+
};
|
|
1532
|
+
})
|
|
1533
|
+
};
|
|
1534
|
+
yield taskExecutor.append(this.wrapExecutorWithScreenshot(queryTask));
|
|
1535
|
+
const output = yield taskExecutor.flush();
|
|
1536
|
+
return {
|
|
1537
|
+
output,
|
|
1538
|
+
executor: taskExecutor
|
|
1539
|
+
};
|
|
1540
|
+
});
|
|
1519
1541
|
}
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
assert2(checkIntervalMs, "No checkIntervalMs for waitFor");
|
|
1527
|
-
const overallStartTime = Date.now();
|
|
1528
|
-
let startTime = Date.now();
|
|
1529
|
-
let errorThought = "";
|
|
1530
|
-
while (Date.now() - overallStartTime < timeoutMs) {
|
|
1531
|
-
startTime = Date.now();
|
|
1532
|
-
const assertPlan = {
|
|
1533
|
-
type: "AssertWithoutThrow",
|
|
1542
|
+
assert(assertion) {
|
|
1543
|
+
return __async(this, null, function* () {
|
|
1544
|
+
const description = `assert: ${assertion}`;
|
|
1545
|
+
const taskExecutor = new Executor(description);
|
|
1546
|
+
const assertionPlan = {
|
|
1547
|
+
type: "Assert",
|
|
1534
1548
|
param: {
|
|
1535
1549
|
assertion
|
|
1536
1550
|
}
|
|
1537
1551
|
};
|
|
1538
|
-
const assertTask =
|
|
1539
|
-
|
|
1540
|
-
const output =
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1552
|
+
const assertTask = yield this.convertPlanToExecutable([assertionPlan]);
|
|
1553
|
+
yield taskExecutor.append(this.wrapExecutorWithScreenshot(assertTask[0]));
|
|
1554
|
+
const output = yield taskExecutor.flush();
|
|
1555
|
+
return {
|
|
1556
|
+
output,
|
|
1557
|
+
executor: taskExecutor
|
|
1558
|
+
};
|
|
1559
|
+
});
|
|
1560
|
+
}
|
|
1561
|
+
waitFor(assertion, opt) {
|
|
1562
|
+
return __async(this, null, function* () {
|
|
1563
|
+
const description = `waitFor: ${assertion}`;
|
|
1564
|
+
const taskExecutor = new Executor(description);
|
|
1565
|
+
const { timeoutMs, checkIntervalMs } = opt;
|
|
1566
|
+
assert2(assertion, "No assertion for waitFor");
|
|
1567
|
+
assert2(timeoutMs, "No timeoutMs for waitFor");
|
|
1568
|
+
assert2(checkIntervalMs, "No checkIntervalMs for waitFor");
|
|
1569
|
+
const overallStartTime = Date.now();
|
|
1570
|
+
let startTime = Date.now();
|
|
1571
|
+
let errorThought = "";
|
|
1572
|
+
while (Date.now() - overallStartTime < timeoutMs) {
|
|
1573
|
+
startTime = Date.now();
|
|
1574
|
+
const assertPlan = {
|
|
1575
|
+
type: "AssertWithoutThrow",
|
|
1553
1576
|
param: {
|
|
1554
|
-
|
|
1577
|
+
assertion
|
|
1555
1578
|
}
|
|
1556
1579
|
};
|
|
1557
|
-
const
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
)
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1580
|
+
const assertTask = yield this.convertPlanToExecutable([assertPlan]);
|
|
1581
|
+
yield taskExecutor.append(this.wrapExecutorWithScreenshot(assertTask[0]));
|
|
1582
|
+
const output = yield taskExecutor.flush();
|
|
1583
|
+
if (output == null ? void 0 : output.pass) {
|
|
1584
|
+
return {
|
|
1585
|
+
output: void 0,
|
|
1586
|
+
executor: taskExecutor
|
|
1587
|
+
};
|
|
1588
|
+
}
|
|
1589
|
+
errorThought = (output == null ? void 0 : output.thought) || "unknown error";
|
|
1590
|
+
const now = Date.now();
|
|
1591
|
+
if (now - startTime < checkIntervalMs) {
|
|
1592
|
+
const timeRemaining = checkIntervalMs - (now - startTime);
|
|
1593
|
+
const sleepPlan = {
|
|
1594
|
+
type: "Sleep",
|
|
1595
|
+
param: {
|
|
1596
|
+
timeMs: timeRemaining
|
|
1597
|
+
}
|
|
1598
|
+
};
|
|
1599
|
+
const sleepTask = yield this.convertPlanToExecutable([sleepPlan]);
|
|
1600
|
+
yield taskExecutor.append(
|
|
1601
|
+
this.wrapExecutorWithScreenshot(sleepTask[0])
|
|
1602
|
+
);
|
|
1603
|
+
yield taskExecutor.flush();
|
|
1604
|
+
}
|
|
1568
1605
|
}
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1606
|
+
const errorPlan = {
|
|
1607
|
+
type: "Error",
|
|
1608
|
+
param: {
|
|
1609
|
+
thought: `waitFor timeout: ${errorThought}`
|
|
1610
|
+
}
|
|
1611
|
+
};
|
|
1612
|
+
const errorTask = yield this.convertPlanToExecutable([errorPlan]);
|
|
1613
|
+
yield taskExecutor.append(errorTask[0]);
|
|
1614
|
+
yield taskExecutor.flush();
|
|
1615
|
+
return {
|
|
1616
|
+
output: void 0,
|
|
1617
|
+
executor: taskExecutor
|
|
1618
|
+
};
|
|
1619
|
+
});
|
|
1577
1620
|
}
|
|
1578
1621
|
};
|
|
1579
1622
|
|
|
@@ -1618,72 +1661,83 @@ var PageAgent = class {
|
|
|
1618
1661
|
type: "dump",
|
|
1619
1662
|
generateReport
|
|
1620
1663
|
});
|
|
1621
|
-
if (generateReport && autoPrintReportMsg) {
|
|
1664
|
+
if (generateReport && autoPrintReportMsg && this.reportFile) {
|
|
1622
1665
|
printReportMsg(this.reportFile);
|
|
1623
1666
|
}
|
|
1624
1667
|
}
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1668
|
+
aiAction(taskPrompt) {
|
|
1669
|
+
return __async(this, null, function* () {
|
|
1670
|
+
const { executor } = yield this.taskExecutor.action(taskPrompt);
|
|
1671
|
+
this.appendExecutionDump(executor.dump());
|
|
1672
|
+
this.writeOutActionDumps();
|
|
1673
|
+
if (executor.isInErrorState()) {
|
|
1674
|
+
const errorTask = executor.latestErrorTask();
|
|
1675
|
+
throw new Error(`${errorTask == null ? void 0 : errorTask.error}
|
|
1632
1676
|
${errorTask == null ? void 0 : errorTask.errorStack}`);
|
|
1633
|
-
|
|
1677
|
+
}
|
|
1678
|
+
});
|
|
1634
1679
|
}
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1680
|
+
aiQuery(demand) {
|
|
1681
|
+
return __async(this, null, function* () {
|
|
1682
|
+
const { output, executor } = yield this.taskExecutor.query(demand);
|
|
1683
|
+
this.appendExecutionDump(executor.dump());
|
|
1684
|
+
this.writeOutActionDumps();
|
|
1685
|
+
if (executor.isInErrorState()) {
|
|
1686
|
+
const errorTask = executor.latestErrorTask();
|
|
1687
|
+
throw new Error(`${errorTask == null ? void 0 : errorTask.error}
|
|
1642
1688
|
${errorTask == null ? void 0 : errorTask.errorStack}`);
|
|
1643
|
-
|
|
1644
|
-
return output;
|
|
1645
|
-
}
|
|
1646
|
-
async aiAssert(assertion, msg, opt) {
|
|
1647
|
-
const { output, executor } = await this.taskExecutor.assert(assertion);
|
|
1648
|
-
this.appendExecutionDump(executor.dump());
|
|
1649
|
-
this.writeOutActionDumps();
|
|
1650
|
-
if (opt == null ? void 0 : opt.keepRawResponse) {
|
|
1689
|
+
}
|
|
1651
1690
|
return output;
|
|
1652
|
-
}
|
|
1653
|
-
if (!(output == null ? void 0 : output.pass)) {
|
|
1654
|
-
const errMsg = msg || `Assertion failed: ${assertion}`;
|
|
1655
|
-
const reasonMsg = `Reason: ${(output == null ? void 0 : output.thought) || "(no_reason)"}`;
|
|
1656
|
-
throw new Error(`${errMsg}
|
|
1657
|
-
${reasonMsg}`);
|
|
1658
|
-
}
|
|
1691
|
+
});
|
|
1659
1692
|
}
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1693
|
+
aiAssert(assertion, msg, opt) {
|
|
1694
|
+
return __async(this, null, function* () {
|
|
1695
|
+
var _a;
|
|
1696
|
+
const { output, executor } = yield this.taskExecutor.assert(assertion);
|
|
1697
|
+
this.appendExecutionDump(executor.dump());
|
|
1698
|
+
this.writeOutActionDumps();
|
|
1699
|
+
if (opt == null ? void 0 : opt.keepRawResponse) {
|
|
1700
|
+
return output;
|
|
1701
|
+
}
|
|
1702
|
+
if (!(output == null ? void 0 : output.pass)) {
|
|
1703
|
+
const errMsg = msg || `Assertion failed: ${assertion}`;
|
|
1704
|
+
const reasonMsg = `Reason: ${(output == null ? void 0 : output.thought) || ((_a = executor.latestErrorTask()) == null ? void 0 : _a.error) || "(no_reason)"}`;
|
|
1705
|
+
throw new Error(`${errMsg}
|
|
1706
|
+
${reasonMsg}`);
|
|
1707
|
+
}
|
|
1665
1708
|
});
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
const
|
|
1670
|
-
|
|
1709
|
+
}
|
|
1710
|
+
aiWaitFor(assertion, opt) {
|
|
1711
|
+
return __async(this, null, function* () {
|
|
1712
|
+
const { executor } = yield this.taskExecutor.waitFor(assertion, {
|
|
1713
|
+
timeoutMs: (opt == null ? void 0 : opt.timeoutMs) || 15 * 1e3,
|
|
1714
|
+
checkIntervalMs: (opt == null ? void 0 : opt.checkIntervalMs) || 3 * 1e3,
|
|
1715
|
+
assertion
|
|
1716
|
+
});
|
|
1717
|
+
this.appendExecutionDump(executor.dump());
|
|
1718
|
+
this.writeOutActionDumps();
|
|
1719
|
+
if (executor.isInErrorState()) {
|
|
1720
|
+
const errorTask = executor.latestErrorTask();
|
|
1721
|
+
throw new Error(`${errorTask == null ? void 0 : errorTask.error}
|
|
1671
1722
|
${errorTask == null ? void 0 : errorTask.errorStack}`);
|
|
1672
|
-
|
|
1723
|
+
}
|
|
1724
|
+
});
|
|
1673
1725
|
}
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1726
|
+
ai(taskPrompt, type = "action") {
|
|
1727
|
+
return __async(this, null, function* () {
|
|
1728
|
+
if (type === "action") {
|
|
1729
|
+
return this.aiAction(taskPrompt);
|
|
1730
|
+
}
|
|
1731
|
+
if (type === "query") {
|
|
1732
|
+
return this.aiQuery(taskPrompt);
|
|
1733
|
+
}
|
|
1734
|
+
if (type === "assert") {
|
|
1735
|
+
return this.aiAssert(taskPrompt);
|
|
1736
|
+
}
|
|
1737
|
+
throw new Error(
|
|
1738
|
+
`Unknown type: ${type}, only support 'action', 'query', 'assert'`
|
|
1739
|
+
);
|
|
1740
|
+
});
|
|
1687
1741
|
}
|
|
1688
1742
|
};
|
|
1689
1743
|
|
|
@@ -1712,7 +1766,7 @@ var PlaywrightAiFixture = () => {
|
|
|
1712
1766
|
const agentForPage = (page, testInfo) => {
|
|
1713
1767
|
let idForPage = page[midsceneAgentKeyId];
|
|
1714
1768
|
if (!idForPage) {
|
|
1715
|
-
idForPage =
|
|
1769
|
+
idForPage = randomUUID();
|
|
1716
1770
|
page[midsceneAgentKeyId] = idForPage;
|
|
1717
1771
|
const { testId } = testInfo;
|
|
1718
1772
|
const { taskFile, taskTitle } = groupAndCaseForTest(testInfo);
|
|
@@ -1741,70 +1795,94 @@ var PlaywrightAiFixture = () => {
|
|
|
1741
1795
|
}
|
|
1742
1796
|
};
|
|
1743
1797
|
return {
|
|
1744
|
-
ai:
|
|
1798
|
+
ai: (_0, _1, _2) => __async(void 0, [_0, _1, _2], function* ({ page }, use, testInfo) {
|
|
1745
1799
|
const agent = agentForPage(page, testInfo);
|
|
1746
|
-
|
|
1747
|
-
|
|
1800
|
+
yield use(
|
|
1801
|
+
(taskPrompt, opts) => __async(void 0, null, function* () {
|
|
1748
1802
|
return new Promise((resolve, reject) => {
|
|
1749
|
-
test.step(`ai - ${taskPrompt}`,
|
|
1750
|
-
|
|
1803
|
+
test.step(`ai - ${taskPrompt}`, () => __async(void 0, null, function* () {
|
|
1804
|
+
yield waitForNetworkIdle(page);
|
|
1751
1805
|
const actionType = (opts == null ? void 0 : opts.type) || "action";
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1806
|
+
try {
|
|
1807
|
+
const result = yield agent.ai(taskPrompt, actionType);
|
|
1808
|
+
resolve(result);
|
|
1809
|
+
} catch (error) {
|
|
1810
|
+
reject(error);
|
|
1811
|
+
}
|
|
1812
|
+
}));
|
|
1755
1813
|
});
|
|
1756
|
-
}
|
|
1814
|
+
})
|
|
1757
1815
|
);
|
|
1758
1816
|
updateDumpAnnotation(testInfo, agent.dumpDataString());
|
|
1759
|
-
},
|
|
1760
|
-
aiAction:
|
|
1817
|
+
}),
|
|
1818
|
+
aiAction: (_0, _1, _2) => __async(void 0, [_0, _1, _2], function* ({ page }, use, testInfo) {
|
|
1761
1819
|
const agent = agentForPage(page, testInfo);
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1820
|
+
yield use((taskPrompt) => __async(void 0, null, function* () {
|
|
1821
|
+
return new Promise((resolve, reject) => {
|
|
1822
|
+
test.step(`aiAction - ${taskPrompt}`, () => __async(void 0, null, function* () {
|
|
1823
|
+
yield waitForNetworkIdle(page);
|
|
1824
|
+
try {
|
|
1825
|
+
const result = yield agent.aiAction(taskPrompt);
|
|
1826
|
+
resolve(result);
|
|
1827
|
+
} catch (error) {
|
|
1828
|
+
reject(error);
|
|
1829
|
+
}
|
|
1830
|
+
}));
|
|
1766
1831
|
});
|
|
1767
|
-
});
|
|
1832
|
+
}));
|
|
1768
1833
|
updateDumpAnnotation(testInfo, agent.dumpDataString());
|
|
1769
|
-
},
|
|
1770
|
-
aiQuery:
|
|
1834
|
+
}),
|
|
1835
|
+
aiQuery: (_0, _1, _2) => __async(void 0, [_0, _1, _2], function* ({ page }, use, testInfo) {
|
|
1771
1836
|
const agent = agentForPage(page, testInfo);
|
|
1772
|
-
|
|
1837
|
+
yield use((demand) => __async(void 0, null, function* () {
|
|
1773
1838
|
return new Promise((resolve, reject) => {
|
|
1774
|
-
test.step(`aiQuery - ${JSON.stringify(demand)}`,
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1839
|
+
test.step(`aiQuery - ${JSON.stringify(demand)}`, () => __async(void 0, null, function* () {
|
|
1840
|
+
yield waitForNetworkIdle(page);
|
|
1841
|
+
try {
|
|
1842
|
+
const result = yield agent.aiQuery(demand);
|
|
1843
|
+
resolve(result);
|
|
1844
|
+
} catch (error) {
|
|
1845
|
+
reject(error);
|
|
1846
|
+
}
|
|
1847
|
+
}));
|
|
1779
1848
|
});
|
|
1780
|
-
});
|
|
1849
|
+
}));
|
|
1781
1850
|
updateDumpAnnotation(testInfo, agent.dumpDataString());
|
|
1782
|
-
},
|
|
1783
|
-
aiAssert:
|
|
1851
|
+
}),
|
|
1852
|
+
aiAssert: (_0, _1, _2) => __async(void 0, [_0, _1, _2], function* ({ page }, use, testInfo) {
|
|
1784
1853
|
const agent = agentForPage(page, testInfo);
|
|
1785
|
-
|
|
1854
|
+
yield use((assertion, errorMsg) => __async(void 0, null, function* () {
|
|
1786
1855
|
return new Promise((resolve, reject) => {
|
|
1787
|
-
test.step(`aiAssert - ${assertion}`,
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1856
|
+
test.step(`aiAssert - ${assertion}`, () => __async(void 0, null, function* () {
|
|
1857
|
+
yield waitForNetworkIdle(page);
|
|
1858
|
+
try {
|
|
1859
|
+
yield agent.aiAssert(assertion, errorMsg);
|
|
1860
|
+
resolve(null);
|
|
1861
|
+
} catch (error) {
|
|
1862
|
+
reject(error);
|
|
1863
|
+
}
|
|
1864
|
+
}));
|
|
1792
1865
|
});
|
|
1793
|
-
});
|
|
1866
|
+
}));
|
|
1794
1867
|
updateDumpAnnotation(testInfo, agent.dumpDataString());
|
|
1795
|
-
},
|
|
1796
|
-
aiWaitFor:
|
|
1868
|
+
}),
|
|
1869
|
+
aiWaitFor: (_0, _1, _2) => __async(void 0, [_0, _1, _2], function* ({ page }, use, testInfo) {
|
|
1797
1870
|
const agent = agentForPage(page, testInfo);
|
|
1798
|
-
|
|
1871
|
+
yield use((assertion, opt) => __async(void 0, null, function* () {
|
|
1799
1872
|
return new Promise((resolve, reject) => {
|
|
1800
|
-
test.step(`aiWaitFor - ${assertion}`,
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1873
|
+
test.step(`aiWaitFor - ${assertion}`, () => __async(void 0, null, function* () {
|
|
1874
|
+
yield waitForNetworkIdle(page);
|
|
1875
|
+
try {
|
|
1876
|
+
yield agent.aiWaitFor(assertion, opt);
|
|
1877
|
+
resolve(null);
|
|
1878
|
+
} catch (error) {
|
|
1879
|
+
reject(error);
|
|
1880
|
+
}
|
|
1881
|
+
}));
|
|
1804
1882
|
});
|
|
1805
|
-
});
|
|
1883
|
+
}));
|
|
1806
1884
|
updateDumpAnnotation(testInfo, agent.dumpDataString());
|
|
1807
|
-
}
|
|
1885
|
+
})
|
|
1808
1886
|
};
|
|
1809
1887
|
};
|
|
1810
1888
|
function waitForNetworkIdle(page) {
|
|
@@ -1818,86 +1896,112 @@ function waitForNetworkIdle(page) {
|
|
|
1818
1896
|
// src/puppeteer/base-page.ts
|
|
1819
1897
|
import { readFileSync as readFileSync3, writeFileSync } from "fs";
|
|
1820
1898
|
import { getTmpFile } from "@midscene/core/utils";
|
|
1821
|
-
import { resizeImg } from "@midscene/shared/img";
|
|
1899
|
+
import { base64Encoded, resizeImg } from "@midscene/shared/img";
|
|
1822
1900
|
var Page = class {
|
|
1823
1901
|
evaluate(pageFunction, arg) {
|
|
1824
1902
|
if (this.pageType === "puppeteer") {
|
|
1825
|
-
return this.
|
|
1903
|
+
return this.underlyingPage.evaluate(pageFunction, arg);
|
|
1826
1904
|
}
|
|
1827
|
-
return this.
|
|
1905
|
+
return this.underlyingPage.evaluate(pageFunction, arg);
|
|
1828
1906
|
}
|
|
1829
|
-
constructor(
|
|
1830
|
-
this.
|
|
1907
|
+
constructor(underlyingPage, pageType) {
|
|
1908
|
+
this.underlyingPage = underlyingPage;
|
|
1831
1909
|
this.pageType = pageType;
|
|
1832
1910
|
}
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
async screenshot() {
|
|
1839
|
-
const viewportSize = await this.evaluate(() => {
|
|
1840
|
-
return {
|
|
1841
|
-
width: document.documentElement.clientWidth,
|
|
1842
|
-
height: document.documentElement.clientHeight,
|
|
1843
|
-
deviceScaleFactor: window.devicePixelRatio
|
|
1844
|
-
};
|
|
1911
|
+
getElementInfos() {
|
|
1912
|
+
return __async(this, null, function* () {
|
|
1913
|
+
const scripts = yield getExtraReturnLogic();
|
|
1914
|
+
const captureElementSnapshot = yield this.evaluate(scripts);
|
|
1915
|
+
return captureElementSnapshot;
|
|
1845
1916
|
});
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
height: viewportSize.height
|
|
1917
|
+
}
|
|
1918
|
+
screenshotBase64() {
|
|
1919
|
+
return __async(this, null, function* () {
|
|
1920
|
+
const viewportSize = yield this.evaluate(() => {
|
|
1921
|
+
return {
|
|
1922
|
+
width: document.documentElement.clientWidth,
|
|
1923
|
+
height: document.documentElement.clientHeight,
|
|
1924
|
+
deviceScaleFactor: window.devicePixelRatio
|
|
1925
|
+
};
|
|
1856
1926
|
});
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1927
|
+
const path3 = getTmpFile("png");
|
|
1928
|
+
yield this.underlyingPage.screenshot({
|
|
1929
|
+
path: path3,
|
|
1930
|
+
type: "png"
|
|
1931
|
+
});
|
|
1932
|
+
let buf;
|
|
1933
|
+
if (viewportSize.deviceScaleFactor > 1) {
|
|
1934
|
+
buf = yield resizeImg(readFileSync3(path3), {
|
|
1935
|
+
width: viewportSize.width,
|
|
1936
|
+
height: viewportSize.height
|
|
1937
|
+
});
|
|
1938
|
+
writeFileSync(path3, buf);
|
|
1939
|
+
}
|
|
1940
|
+
return base64Encoded(path3, true);
|
|
1941
|
+
});
|
|
1860
1942
|
}
|
|
1861
1943
|
url() {
|
|
1862
|
-
return this.
|
|
1944
|
+
return this.underlyingPage.url();
|
|
1863
1945
|
}
|
|
1864
1946
|
get mouse() {
|
|
1865
1947
|
return {
|
|
1866
|
-
click:
|
|
1867
|
-
|
|
1948
|
+
click: (x, y, options) => __async(this, null, function* () {
|
|
1949
|
+
return this.underlyingPage.mouse.click(x, y, {
|
|
1950
|
+
button: (options == null ? void 0 : options.button) || "left"
|
|
1951
|
+
});
|
|
1952
|
+
}),
|
|
1953
|
+
wheel: (deltaX, deltaY) => __async(this, null, function* () {
|
|
1868
1954
|
if (this.pageType === "puppeteer") {
|
|
1869
|
-
|
|
1955
|
+
yield this.underlyingPage.mouse.wheel({
|
|
1956
|
+
deltaX,
|
|
1957
|
+
deltaY
|
|
1958
|
+
});
|
|
1870
1959
|
} else if (this.pageType === "playwright") {
|
|
1871
|
-
|
|
1960
|
+
yield this.underlyingPage.mouse.wheel(
|
|
1961
|
+
deltaX,
|
|
1962
|
+
deltaY
|
|
1963
|
+
);
|
|
1872
1964
|
}
|
|
1873
|
-
},
|
|
1874
|
-
move:
|
|
1965
|
+
}),
|
|
1966
|
+
move: (x, y) => __async(this, null, function* () {
|
|
1967
|
+
return this.underlyingPage.mouse.move(x, y);
|
|
1968
|
+
})
|
|
1875
1969
|
};
|
|
1876
1970
|
}
|
|
1877
1971
|
get keyboard() {
|
|
1878
1972
|
return {
|
|
1879
|
-
type:
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1973
|
+
type: (text) => __async(this, null, function* () {
|
|
1974
|
+
return this.underlyingPage.keyboard.type(text);
|
|
1975
|
+
}),
|
|
1976
|
+
press: (key) => __async(this, null, function* () {
|
|
1977
|
+
return this.underlyingPage.keyboard.press(key);
|
|
1978
|
+
}),
|
|
1979
|
+
down: (key) => __async(this, null, function* () {
|
|
1980
|
+
return this.underlyingPage.keyboard.down(key);
|
|
1981
|
+
}),
|
|
1982
|
+
up: (key) => __async(this, null, function* () {
|
|
1983
|
+
return this.underlyingPage.keyboard.up(key);
|
|
1984
|
+
})
|
|
1883
1985
|
};
|
|
1884
1986
|
}
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1987
|
+
clearInput(element) {
|
|
1988
|
+
return __async(this, null, function* () {
|
|
1989
|
+
if (!element) {
|
|
1990
|
+
return;
|
|
1991
|
+
}
|
|
1992
|
+
yield this.mouse.click(element.center[0], element.center[1]);
|
|
1993
|
+
const isMac = process.platform === "darwin";
|
|
1994
|
+
if (isMac) {
|
|
1995
|
+
yield this.underlyingPage.keyboard.down("Meta");
|
|
1996
|
+
yield this.underlyingPage.keyboard.press("a");
|
|
1997
|
+
yield this.underlyingPage.keyboard.up("Meta");
|
|
1998
|
+
} else {
|
|
1999
|
+
yield this.underlyingPage.keyboard.down("Control");
|
|
2000
|
+
yield this.underlyingPage.keyboard.press("a");
|
|
2001
|
+
yield this.underlyingPage.keyboard.up("Control");
|
|
2002
|
+
}
|
|
2003
|
+
yield this.keyboard.press("Backspace");
|
|
2004
|
+
});
|
|
1901
2005
|
}
|
|
1902
2006
|
scrollUntilTop() {
|
|
1903
2007
|
return this.mouse.wheel(0, -9999999);
|
|
@@ -1905,15 +2009,19 @@ var Page = class {
|
|
|
1905
2009
|
scrollUntilBottom() {
|
|
1906
2010
|
return this.mouse.wheel(0, 9999999);
|
|
1907
2011
|
}
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
2012
|
+
scrollUpOneScreen() {
|
|
2013
|
+
return __async(this, null, function* () {
|
|
2014
|
+
const innerHeight = yield this.evaluate(() => window.innerHeight);
|
|
2015
|
+
const distance = innerHeight * 0.7;
|
|
2016
|
+
yield this.mouse.wheel(0, -distance);
|
|
2017
|
+
});
|
|
1912
2018
|
}
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
2019
|
+
scrollDownOneScreen() {
|
|
2020
|
+
return __async(this, null, function* () {
|
|
2021
|
+
const innerHeight = yield this.evaluate(() => window.innerHeight);
|
|
2022
|
+
const distance = innerHeight * 0.7;
|
|
2023
|
+
yield this.mouse.wheel(0, distance);
|
|
2024
|
+
});
|
|
1917
2025
|
}
|
|
1918
2026
|
};
|
|
1919
2027
|
|
|
@@ -1942,7 +2050,7 @@ var PuppeteerAgent = class extends PageAgent {
|
|
|
1942
2050
|
// src/appium/page.ts
|
|
1943
2051
|
import fs from "fs";
|
|
1944
2052
|
import { getTmpFile as getTmpFile2 } from "@midscene/core/utils";
|
|
1945
|
-
import { resizeImg as resizeImg2 } from "@midscene/shared/img";
|
|
2053
|
+
import { base64Encoded as base64Encoded2, resizeImg as resizeImg2 } from "@midscene/shared/img";
|
|
1946
2054
|
import { DOMParser } from "@xmldom/xmldom";
|
|
1947
2055
|
|
|
1948
2056
|
// src/extractor/web-extractor.ts
|
|
@@ -2145,26 +2253,30 @@ var Page2 = class {
|
|
|
2145
2253
|
this.pageType = "appium";
|
|
2146
2254
|
this.browser = browser;
|
|
2147
2255
|
}
|
|
2148
|
-
|
|
2149
|
-
|
|
2150
|
-
|
|
2151
|
-
|
|
2152
|
-
|
|
2153
|
-
|
|
2154
|
-
|
|
2155
|
-
|
|
2156
|
-
|
|
2157
|
-
|
|
2158
|
-
|
|
2159
|
-
|
|
2160
|
-
|
|
2161
|
-
|
|
2162
|
-
|
|
2163
|
-
|
|
2164
|
-
|
|
2256
|
+
getElementInfos() {
|
|
2257
|
+
return __async(this, null, function* () {
|
|
2258
|
+
const pageSource = yield this.browser.getPageSource();
|
|
2259
|
+
const { width, height } = yield this.browser.getWindowSize();
|
|
2260
|
+
const parser = new DOMParser();
|
|
2261
|
+
const doc = parser.parseFromString(pageSource, "text/xml");
|
|
2262
|
+
const infos = extractTextWithPosition2(doc).filter(
|
|
2263
|
+
(element) => element.rect.height !== height && element.rect.width !== width && element.rect.left !== 0 && element.rect.top !== 0 && element.attributes.visible === "true"
|
|
2264
|
+
);
|
|
2265
|
+
return infos;
|
|
2266
|
+
});
|
|
2267
|
+
}
|
|
2268
|
+
screenshotBase64() {
|
|
2269
|
+
return __async(this, null, function* () {
|
|
2270
|
+
const { width, height } = yield this.browser.getWindowSize();
|
|
2271
|
+
const path3 = getTmpFile2("png");
|
|
2272
|
+
const screenshotBuffer = yield this.browser.saveScreenshot(path3);
|
|
2273
|
+
const resizedScreenshotBuffer = yield resizeImg2(screenshotBuffer, {
|
|
2274
|
+
width,
|
|
2275
|
+
height
|
|
2276
|
+
});
|
|
2277
|
+
fs.writeFileSync(path3, resizedScreenshotBuffer);
|
|
2278
|
+
return base64Encoded2(path3);
|
|
2165
2279
|
});
|
|
2166
|
-
fs.writeFileSync(path3, resizedScreenshotBuffer);
|
|
2167
|
-
return path3;
|
|
2168
2280
|
}
|
|
2169
2281
|
get mouse() {
|
|
2170
2282
|
return {
|
|
@@ -2180,14 +2292,16 @@ var Page2 = class {
|
|
|
2180
2292
|
press: (key) => this.keyboardPress(key)
|
|
2181
2293
|
};
|
|
2182
2294
|
}
|
|
2183
|
-
|
|
2184
|
-
|
|
2185
|
-
|
|
2186
|
-
|
|
2187
|
-
|
|
2188
|
-
|
|
2189
|
-
|
|
2190
|
-
|
|
2295
|
+
clearInput(element) {
|
|
2296
|
+
return __async(this, null, function* () {
|
|
2297
|
+
if (!element) {
|
|
2298
|
+
return;
|
|
2299
|
+
}
|
|
2300
|
+
const ele = yield this.browser.$(element.locator);
|
|
2301
|
+
const blank = " ";
|
|
2302
|
+
yield this.keyboardType(blank);
|
|
2303
|
+
yield ele.clearValue();
|
|
2304
|
+
});
|
|
2191
2305
|
}
|
|
2192
2306
|
url() {
|
|
2193
2307
|
var _a;
|
|
@@ -2203,114 +2317,139 @@ var Page2 = class {
|
|
|
2203
2317
|
return "";
|
|
2204
2318
|
}
|
|
2205
2319
|
// Scroll to top element
|
|
2206
|
-
|
|
2207
|
-
|
|
2208
|
-
|
|
2320
|
+
scrollUntilTop() {
|
|
2321
|
+
return __async(this, null, function* () {
|
|
2322
|
+
const { height } = yield this.browser.getWindowSize();
|
|
2323
|
+
yield this.mouseWheel(0, height, 100);
|
|
2324
|
+
});
|
|
2209
2325
|
}
|
|
2210
2326
|
// Scroll to bottom element
|
|
2211
|
-
|
|
2212
|
-
|
|
2213
|
-
|
|
2327
|
+
scrollUntilBottom() {
|
|
2328
|
+
return __async(this, null, function* () {
|
|
2329
|
+
const { height } = yield this.browser.getWindowSize();
|
|
2330
|
+
yield this.mouseWheel(0, -height, 100);
|
|
2331
|
+
});
|
|
2214
2332
|
}
|
|
2215
2333
|
// Scroll up one screen
|
|
2216
|
-
|
|
2217
|
-
|
|
2218
|
-
|
|
2334
|
+
scrollUpOneScreen() {
|
|
2335
|
+
return __async(this, null, function* () {
|
|
2336
|
+
const { height } = yield this.browser.getWindowSize();
|
|
2337
|
+
yield this.mouseWheel(0, height, 1e3);
|
|
2338
|
+
});
|
|
2219
2339
|
}
|
|
2220
2340
|
// Scroll down one screen
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
|
|
2224
|
-
|
|
2225
|
-
|
|
2226
|
-
const actions = [];
|
|
2227
|
-
for (const char of text) {
|
|
2228
|
-
actions.push({ type: "keyDown", value: char });
|
|
2229
|
-
actions.push({ type: "keyUp", value: char });
|
|
2230
|
-
}
|
|
2231
|
-
if (!actions.length) {
|
|
2232
|
-
return;
|
|
2233
|
-
}
|
|
2234
|
-
await this.browser.performActions([
|
|
2235
|
-
{
|
|
2236
|
-
type: "key",
|
|
2237
|
-
id: "keyboard",
|
|
2238
|
-
actions
|
|
2239
|
-
}
|
|
2240
|
-
]);
|
|
2341
|
+
scrollDownOneScreen() {
|
|
2342
|
+
return __async(this, null, function* () {
|
|
2343
|
+
const { height } = yield this.browser.getWindowSize();
|
|
2344
|
+
yield this.mouseWheel(0, -height, 1e3);
|
|
2345
|
+
});
|
|
2241
2346
|
}
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
|
|
2246
|
-
|
|
2247
|
-
actions:
|
|
2248
|
-
{ type: "keyDown", value: key },
|
|
2249
|
-
{ type: "keyUp", value: key }
|
|
2250
|
-
]
|
|
2347
|
+
keyboardType(text) {
|
|
2348
|
+
return __async(this, null, function* () {
|
|
2349
|
+
const actions = [];
|
|
2350
|
+
for (const char of text) {
|
|
2351
|
+
actions.push({ type: "keyDown", value: char });
|
|
2352
|
+
actions.push({ type: "keyUp", value: char });
|
|
2251
2353
|
}
|
|
2252
|
-
|
|
2253
|
-
|
|
2254
|
-
async mouseClick(x, y, button = "left") {
|
|
2255
|
-
await this.mouseMove(x, y);
|
|
2256
|
-
await this.browser.performActions([
|
|
2257
|
-
{
|
|
2258
|
-
type: "pointer",
|
|
2259
|
-
id: "mouse",
|
|
2260
|
-
parameters: { pointerType: "mouse" },
|
|
2261
|
-
actions: [
|
|
2262
|
-
{ type: "pointerMove", duration: 0, x, y },
|
|
2263
|
-
{ type: "pointerDown", button: buttonToNumber(button) },
|
|
2264
|
-
{ type: "pause", duration: 100 },
|
|
2265
|
-
{ type: "pointerUp", button: buttonToNumber(button) }
|
|
2266
|
-
]
|
|
2354
|
+
if (!actions.length) {
|
|
2355
|
+
return;
|
|
2267
2356
|
}
|
|
2268
|
-
|
|
2357
|
+
yield this.browser.performActions([
|
|
2358
|
+
{
|
|
2359
|
+
type: "key",
|
|
2360
|
+
id: "keyboard",
|
|
2361
|
+
actions
|
|
2362
|
+
}
|
|
2363
|
+
]);
|
|
2364
|
+
});
|
|
2269
2365
|
}
|
|
2270
|
-
|
|
2271
|
-
|
|
2272
|
-
|
|
2273
|
-
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
|
|
2284
|
-
|
|
2285
|
-
|
|
2286
|
-
|
|
2287
|
-
|
|
2288
|
-
|
|
2289
|
-
|
|
2290
|
-
|
|
2291
|
-
|
|
2292
|
-
|
|
2293
|
-
|
|
2294
|
-
|
|
2295
|
-
|
|
2296
|
-
|
|
2297
|
-
|
|
2298
|
-
|
|
2299
|
-
|
|
2300
|
-
|
|
2301
|
-
|
|
2302
|
-
|
|
2303
|
-
|
|
2304
|
-
|
|
2305
|
-
|
|
2306
|
-
|
|
2307
|
-
|
|
2308
|
-
|
|
2309
|
-
}
|
|
2310
|
-
|
|
2311
|
-
|
|
2312
|
-
|
|
2313
|
-
|
|
2366
|
+
keyboardPress(key) {
|
|
2367
|
+
return __async(this, null, function* () {
|
|
2368
|
+
yield this.browser.performActions([
|
|
2369
|
+
{
|
|
2370
|
+
type: "key",
|
|
2371
|
+
id: "keyboard",
|
|
2372
|
+
actions: [
|
|
2373
|
+
{ type: "keyDown", value: key },
|
|
2374
|
+
{ type: "keyUp", value: key }
|
|
2375
|
+
]
|
|
2376
|
+
}
|
|
2377
|
+
]);
|
|
2378
|
+
});
|
|
2379
|
+
}
|
|
2380
|
+
mouseClick(x, y, button = "left") {
|
|
2381
|
+
return __async(this, null, function* () {
|
|
2382
|
+
yield this.mouseMove(x, y);
|
|
2383
|
+
yield this.browser.performActions([
|
|
2384
|
+
{
|
|
2385
|
+
type: "pointer",
|
|
2386
|
+
id: "mouse",
|
|
2387
|
+
parameters: { pointerType: "mouse" },
|
|
2388
|
+
actions: [
|
|
2389
|
+
{ type: "pointerMove", duration: 0, x, y },
|
|
2390
|
+
{ type: "pointerDown", button: buttonToNumber(button) },
|
|
2391
|
+
{ type: "pause", duration: 100 },
|
|
2392
|
+
{ type: "pointerUp", button: buttonToNumber(button) }
|
|
2393
|
+
]
|
|
2394
|
+
}
|
|
2395
|
+
]);
|
|
2396
|
+
});
|
|
2397
|
+
}
|
|
2398
|
+
mouseMove(x, y) {
|
|
2399
|
+
return __async(this, null, function* () {
|
|
2400
|
+
yield this.browser.performActions([
|
|
2401
|
+
{
|
|
2402
|
+
type: "pointer",
|
|
2403
|
+
id: "mouse",
|
|
2404
|
+
parameters: { pointerType: "mouse" },
|
|
2405
|
+
actions: [{ type: "pointerMove", duration: 0, x, y }]
|
|
2406
|
+
}
|
|
2407
|
+
]);
|
|
2408
|
+
});
|
|
2409
|
+
}
|
|
2410
|
+
mouseWheel(deltaX, deltaY, duration = 1e3) {
|
|
2411
|
+
return __async(this, null, function* () {
|
|
2412
|
+
const n = 4;
|
|
2413
|
+
const windowSize = yield this.browser.getWindowSize();
|
|
2414
|
+
const startX = deltaX < 0 ? (n - 1) * (windowSize.width / n) : windowSize.width / n;
|
|
2415
|
+
const startY = deltaY < 0 ? (n - 1) * (windowSize.height / n) : windowSize.height / n;
|
|
2416
|
+
const maxNegativeDeltaX = startX;
|
|
2417
|
+
const maxPositiveDeltaX = (n - 1) * (windowSize.width / n);
|
|
2418
|
+
const maxNegativeDeltaY = startY;
|
|
2419
|
+
const maxPositiveDeltaY = (n - 1) * (windowSize.height / n);
|
|
2420
|
+
deltaX = Math.max(-maxNegativeDeltaX, Math.min(deltaX, maxPositiveDeltaX));
|
|
2421
|
+
deltaY = Math.max(-maxNegativeDeltaY, Math.min(deltaY, maxPositiveDeltaY));
|
|
2422
|
+
yield this.browser.performActions([
|
|
2423
|
+
{
|
|
2424
|
+
type: "pointer",
|
|
2425
|
+
id: "finger1",
|
|
2426
|
+
parameters: { pointerType: "touch" },
|
|
2427
|
+
actions: [
|
|
2428
|
+
{ type: "pointerMove", duration: 0, x: startX, y: startY },
|
|
2429
|
+
{ type: "pointerDown", button: 0 },
|
|
2430
|
+
{ type: "pause", duration },
|
|
2431
|
+
{
|
|
2432
|
+
type: "pointerMove",
|
|
2433
|
+
duration,
|
|
2434
|
+
origin: "pointer",
|
|
2435
|
+
// Use 'pointer' as the starting point
|
|
2436
|
+
x: deltaX,
|
|
2437
|
+
// X offset relative to the starting point
|
|
2438
|
+
y: deltaY
|
|
2439
|
+
// Y offset relative to the starting point
|
|
2440
|
+
},
|
|
2441
|
+
{ type: "pointerUp", button: 0 }
|
|
2442
|
+
]
|
|
2443
|
+
}
|
|
2444
|
+
]);
|
|
2445
|
+
});
|
|
2446
|
+
}
|
|
2447
|
+
};
|
|
2448
|
+
|
|
2449
|
+
// src/playground/agent.ts
|
|
2450
|
+
var StaticPageAgent = class extends PageAgent {
|
|
2451
|
+
constructor(page) {
|
|
2452
|
+
super(page, {});
|
|
2314
2453
|
}
|
|
2315
2454
|
};
|
|
2316
2455
|
|
|
@@ -2322,67 +2461,67 @@ import {
|
|
|
2322
2461
|
resizeImg as resizeImg3,
|
|
2323
2462
|
saveBase64Image
|
|
2324
2463
|
} from "@midscene/shared/img";
|
|
2325
|
-
|
|
2326
|
-
|
|
2327
|
-
|
|
2328
|
-
|
|
2329
|
-
|
|
2330
|
-
|
|
2331
|
-
|
|
2332
|
-
|
|
2333
|
-
|
|
2334
|
-
|
|
2335
|
-
|
|
2336
|
-
|
|
2337
|
-
|
|
2338
|
-
"output_without_text.png"
|
|
2339
|
-
);
|
|
2340
|
-
const resizeOutputImgPath = path2.join(targetDir, "resize-output.png");
|
|
2341
|
-
const snapshotJsonPath = path2.join(targetDir, "element-snapshot.json");
|
|
2342
|
-
const {
|
|
2343
|
-
compositeElementInfoImgBase64,
|
|
2344
|
-
compositeElementInfoImgWithoutTextBase64
|
|
2345
|
-
} = await processImageElementInfo({
|
|
2346
|
-
elementsPositionInfo,
|
|
2347
|
-
elementsPositionInfoWithoutText,
|
|
2348
|
-
inputImgBase64
|
|
2349
|
-
});
|
|
2350
|
-
const resizeImgBase64 = await resizeImg3(inputImgBase64);
|
|
2351
|
-
const existingSnapshot = existsSync2(snapshotJsonPath) ? JSON.parse(readFileSync4(snapshotJsonPath, "utf-8")) : null;
|
|
2352
|
-
if (existingSnapshot && JSON.stringify(existingSnapshot) === JSON.stringify(captureElementSnapshot)) {
|
|
2353
|
-
console.log("skip save snapshot for ", targetDir);
|
|
2354
|
-
return;
|
|
2355
|
-
}
|
|
2356
|
-
if (!(saveImgType == null ? void 0 : saveImgType.disableSnapshot)) {
|
|
2357
|
-
writeFileSyncWithDir(
|
|
2358
|
-
snapshotJsonPath,
|
|
2359
|
-
JSON.stringify(captureElementSnapshot, null, 2)
|
|
2464
|
+
function generateExtractData(page, targetDir, saveImgType) {
|
|
2465
|
+
return __async(this, null, function* () {
|
|
2466
|
+
const inputImgBase64 = yield page.screenshotBase64();
|
|
2467
|
+
const {
|
|
2468
|
+
elementsPositionInfo,
|
|
2469
|
+
captureElementSnapshot,
|
|
2470
|
+
elementsPositionInfoWithoutText
|
|
2471
|
+
} = yield getElementInfos(page);
|
|
2472
|
+
const inputImagePath = path2.join(targetDir, "input.png");
|
|
2473
|
+
const outputImagePath = path2.join(targetDir, "output.png");
|
|
2474
|
+
const outputWithoutTextImgPath = path2.join(
|
|
2475
|
+
targetDir,
|
|
2476
|
+
"output_without_text.png"
|
|
2360
2477
|
);
|
|
2361
|
-
|
|
2362
|
-
|
|
2363
|
-
|
|
2364
|
-
|
|
2365
|
-
|
|
2478
|
+
const resizeOutputImgPath = path2.join(targetDir, "resize-output.png");
|
|
2479
|
+
const snapshotJsonPath = path2.join(targetDir, "element-snapshot.json");
|
|
2480
|
+
const {
|
|
2481
|
+
compositeElementInfoImgBase64,
|
|
2482
|
+
compositeElementInfoImgWithoutTextBase64
|
|
2483
|
+
} = yield processImageElementInfo({
|
|
2484
|
+
elementsPositionInfo,
|
|
2485
|
+
elementsPositionInfoWithoutText,
|
|
2486
|
+
inputImgBase64
|
|
2366
2487
|
});
|
|
2367
|
-
|
|
2368
|
-
|
|
2369
|
-
|
|
2370
|
-
|
|
2371
|
-
|
|
2372
|
-
}
|
|
2373
|
-
|
|
2374
|
-
|
|
2375
|
-
|
|
2376
|
-
|
|
2377
|
-
|
|
2378
|
-
}
|
|
2379
|
-
|
|
2380
|
-
|
|
2381
|
-
|
|
2382
|
-
|
|
2383
|
-
|
|
2384
|
-
}
|
|
2385
|
-
|
|
2488
|
+
const resizeImgBase64 = yield resizeImg3(inputImgBase64);
|
|
2489
|
+
const existingSnapshot = existsSync2(snapshotJsonPath) ? JSON.parse(readFileSync4(snapshotJsonPath, "utf-8")) : null;
|
|
2490
|
+
if (existingSnapshot && JSON.stringify(existingSnapshot) === JSON.stringify(captureElementSnapshot)) {
|
|
2491
|
+
console.log("skip save snapshot for ", targetDir);
|
|
2492
|
+
return;
|
|
2493
|
+
}
|
|
2494
|
+
if (!(saveImgType == null ? void 0 : saveImgType.disableSnapshot)) {
|
|
2495
|
+
writeFileSyncWithDir(
|
|
2496
|
+
snapshotJsonPath,
|
|
2497
|
+
JSON.stringify(captureElementSnapshot, null, 2)
|
|
2498
|
+
);
|
|
2499
|
+
}
|
|
2500
|
+
if (!(saveImgType == null ? void 0 : saveImgType.disableInputImage)) {
|
|
2501
|
+
yield saveBase64Image({
|
|
2502
|
+
base64Data: inputImgBase64,
|
|
2503
|
+
outputPath: inputImagePath
|
|
2504
|
+
});
|
|
2505
|
+
}
|
|
2506
|
+
if (!(saveImgType == null ? void 0 : saveImgType.disableOutputImage)) {
|
|
2507
|
+
yield saveBase64Image({
|
|
2508
|
+
base64Data: compositeElementInfoImgBase64,
|
|
2509
|
+
outputPath: outputImagePath
|
|
2510
|
+
});
|
|
2511
|
+
}
|
|
2512
|
+
if (!(saveImgType == null ? void 0 : saveImgType.disableOutputWithoutTextImg)) {
|
|
2513
|
+
yield saveBase64Image({
|
|
2514
|
+
base64Data: compositeElementInfoImgWithoutTextBase64,
|
|
2515
|
+
outputPath: outputWithoutTextImgPath
|
|
2516
|
+
});
|
|
2517
|
+
}
|
|
2518
|
+
if (!(saveImgType == null ? void 0 : saveImgType.disableResizeOutputImg)) {
|
|
2519
|
+
yield saveBase64Image({
|
|
2520
|
+
base64Data: resizeImgBase64,
|
|
2521
|
+
outputPath: resizeOutputImgPath
|
|
2522
|
+
});
|
|
2523
|
+
}
|
|
2524
|
+
});
|
|
2386
2525
|
}
|
|
2387
2526
|
function ensureDirectoryExistence(filePath) {
|
|
2388
2527
|
const dirname = path2.dirname(filePath);
|
|
@@ -2396,21 +2535,23 @@ function writeFileSyncWithDir(filePath, content, options = {}) {
|
|
|
2396
2535
|
ensureDirectoryExistence(filePath);
|
|
2397
2536
|
writeFileSync2(filePath, content, options);
|
|
2398
2537
|
}
|
|
2399
|
-
|
|
2400
|
-
|
|
2401
|
-
|
|
2402
|
-
|
|
2403
|
-
|
|
2404
|
-
|
|
2538
|
+
function getElementInfos(page) {
|
|
2539
|
+
return __async(this, null, function* () {
|
|
2540
|
+
const captureElementSnapshot = yield page.getElementInfos();
|
|
2541
|
+
const elementsPositionInfoWithoutText = captureElementSnapshot.filter(
|
|
2542
|
+
(elementInfo) => {
|
|
2543
|
+
if (elementInfo.attributes.nodeType === NodeType3.TEXT) {
|
|
2544
|
+
return false;
|
|
2545
|
+
}
|
|
2546
|
+
return true;
|
|
2405
2547
|
}
|
|
2406
|
-
|
|
2407
|
-
|
|
2408
|
-
|
|
2409
|
-
|
|
2410
|
-
|
|
2411
|
-
|
|
2412
|
-
|
|
2413
|
-
};
|
|
2548
|
+
);
|
|
2549
|
+
return {
|
|
2550
|
+
elementsPositionInfo: captureElementSnapshot,
|
|
2551
|
+
captureElementSnapshot,
|
|
2552
|
+
elementsPositionInfoWithoutText
|
|
2553
|
+
};
|
|
2554
|
+
});
|
|
2414
2555
|
}
|
|
2415
2556
|
export {
|
|
2416
2557
|
PageAgent as AppiumAgent,
|
|
@@ -2418,6 +2559,7 @@ export {
|
|
|
2418
2559
|
PageAgent as PlaywrightAgent,
|
|
2419
2560
|
PlaywrightAiFixture,
|
|
2420
2561
|
PuppeteerAgent,
|
|
2562
|
+
StaticPageAgent,
|
|
2421
2563
|
generateExtractData
|
|
2422
2564
|
};
|
|
2423
2565
|
/*! Bundled license information:
|