@veolab/discoverylab 1.2.2 → 1.3.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/.mcp.json +2 -2
- package/README.md +182 -0
- package/dist/{chunk-YEZ26ENO.js → chunk-3QRQEDWR.js} +313 -192
- package/dist/{chunk-E3N3P2AG.js → chunk-4L76GPRC.js} +1124 -51
- package/dist/{chunk-I6YD3QFM.js → chunk-FIL7IWEL.js} +5 -3
- package/dist/{chunk-G524UVBK.js → chunk-FNUN7EPB.js} +5 -5
- package/dist/chunk-GAKEFJ5T.js +481 -0
- package/dist/chunk-LB3RNE3O.js +109 -0
- package/dist/chunk-N6JJ2RGV.js +2680 -0
- package/dist/{chunk-7HZEDTS7.js → chunk-VRM42PML.js} +3471 -848
- package/dist/{chunk-TJ3H23LL.js → chunk-VVIOB362.js} +3 -1
- package/dist/{chunk-W3WJGYR6.js → chunk-XFVDP332.js} +8 -2
- package/dist/cli.js +397 -9
- package/dist/{db-ADBEBNH6.js → db-6WLEVKUV.js} +3 -1
- package/dist/esvp-GSISVXLC.js +52 -0
- package/dist/esvp-mobile-GC7MAGMI.js +20 -0
- package/dist/index.d.ts +123 -1
- package/dist/index.html +11689 -8690
- package/dist/index.js +67 -12
- package/dist/{ocr-UTWC7537.js → ocr-QDYNCSPE.js} +1 -1
- package/dist/{playwright-R7Y5HREH.js → playwright-VZ7PXDC5.js} +2 -2
- package/dist/runtime/esvp-host-runtime/darwin-arm64/esvp-host-runtime +0 -0
- package/dist/runtime/esvp-host-runtime/manifest.json +10 -0
- package/dist/server-FO3UVUZU.js +22 -0
- package/dist/{setup-B5YPNUE4.js → setup-2SQC5UHJ.js} +2 -2
- package/dist/{tools-YS4QHOTQ.js → tools-OCRMOQ4U.js} +61 -6
- package/package.json +36 -5
- package/dist/chunk-22OCFYHG.js +0 -6283
- package/dist/chunk-24VARQVO.js +0 -7818
- package/dist/chunk-2OGZX6C4.js +0 -588
- package/dist/chunk-2WCNIFRO.js +0 -6191
- package/dist/chunk-43U6UYV7.js +0 -590
- package/dist/chunk-4H2E3K2G.js +0 -7638
- package/dist/chunk-4KLG6DDE.js +0 -334
- package/dist/chunk-4MS6YW2B.js +0 -6490
- package/dist/chunk-4NNTRJOI.js +0 -7791
- package/dist/chunk-5F76VWME.js +0 -6397
- package/dist/chunk-5NEFN42O.js +0 -7791
- package/dist/chunk-63MEQ6UH.js +0 -7673
- package/dist/chunk-6H3NXFX3.js +0 -6861
- package/dist/chunk-7DOG2W4O.js +0 -6428
- package/dist/chunk-7IDQLLBW.js +0 -311
- package/dist/chunk-7NP64TGJ.js +0 -6822
- package/dist/chunk-AATLY4KT.js +0 -6505
- package/dist/chunk-C7QUR7XX.js +0 -6397
- package/dist/chunk-CGKCE6MC.js +0 -6279
- package/dist/chunk-D25V6IWE.js +0 -6487
- package/dist/chunk-EQOZSXAT.js +0 -6822
- package/dist/chunk-FPHD7HSQ.js +0 -6812
- package/dist/chunk-GGJJUCFK.js +0 -7160
- package/dist/chunk-GLHOY3NN.js +0 -7805
- package/dist/chunk-GML5MKQA.js +0 -6398
- package/dist/chunk-GOL6FUJL.js +0 -6045
- package/dist/chunk-GSWHWEYC.js +0 -1346
- package/dist/chunk-HDKEQOF5.js +0 -7788
- package/dist/chunk-HZGSWVVS.js +0 -7111
- package/dist/chunk-IGZ5TICZ.js +0 -334
- package/dist/chunk-IRKQG33A.js +0 -7054
- package/dist/chunk-JFTBF4JR.js +0 -6040
- package/dist/chunk-JVLVBPUJ.js +0 -6180
- package/dist/chunk-JY3KC67R.js +0 -6504
- package/dist/chunk-KUFBCBNJ.js +0 -6815
- package/dist/chunk-KV7KDJ43.js +0 -7639
- package/dist/chunk-L4SA5F5W.js +0 -6397
- package/dist/chunk-L5IJZV5F.js +0 -6822
- package/dist/chunk-MFFPQLU4.js +0 -7102
- package/dist/chunk-MJS2YKNR.js +0 -6397
- package/dist/chunk-MN6LCZHZ.js +0 -1320
- package/dist/chunk-NBAUZ7X2.js +0 -1336
- package/dist/chunk-NDBW6ELQ.js +0 -7638
- package/dist/chunk-O2HBSDI2.js +0 -6175
- package/dist/chunk-OFFIUYMG.js +0 -6341
- package/dist/chunk-OVCQGF2J.js +0 -1321
- package/dist/chunk-P4S7ZY6G.js +0 -7638
- package/dist/chunk-PBHUHSC3.js +0 -6002
- package/dist/chunk-PC4LR4ZI.js +0 -6359
- package/dist/chunk-PMTGGZ7R.js +0 -6397
- package/dist/chunk-PTXSB3UV.js +0 -497
- package/dist/chunk-PYUCY3U6.js +0 -1340
- package/dist/chunk-QJXXHOV7.js +0 -205
- package/dist/chunk-RDZDSOAL.js +0 -7750
- package/dist/chunk-RLW2OI2L.js +0 -6383
- package/dist/chunk-RUGHHO4K.js +0 -6395
- package/dist/chunk-SIOQVM2E.js +0 -6819
- package/dist/chunk-SR67SRIT.js +0 -1336
- package/dist/chunk-SSRXIO2V.js +0 -6822
- package/dist/chunk-SWSEKFON.js +0 -6487
- package/dist/chunk-TAODYZ52.js +0 -1393
- package/dist/chunk-TBG76CYG.js +0 -6395
- package/dist/chunk-V3CBINLD.js +0 -6812
- package/dist/chunk-VPYSLEGM.js +0 -6710
- package/dist/chunk-VY3BLXBW.js +0 -329
- package/dist/chunk-WTFOGVJQ.js +0 -6365
- package/dist/chunk-X64SFUT5.js +0 -6099
- package/dist/chunk-XIBF5LBD.js +0 -6395
- package/dist/chunk-XUKWS2CE.js +0 -7805
- package/dist/chunk-XZZKFF5V.js +0 -7787
- package/dist/chunk-Y5VDMSYC.js +0 -6701
- package/dist/chunk-YUBL36H4.js +0 -6605
- package/dist/chunk-YWVXFVSW.js +0 -6456
- package/dist/chunk-ZJFWMSZF.js +0 -7883
- package/dist/chunk-ZXZACOLD.js +0 -6822
- package/dist/db-IWIL65EX.js +0 -33
- package/dist/gridCompositor-ENKLFPWR.js +0 -409
- package/dist/playwright-A3OGSDRG.js +0 -38
- package/dist/playwright-ATDC4NYW.js +0 -38
- package/dist/playwright-E6EUFIJG.js +0 -38
- package/dist/server-2DXLKLFM.js +0 -13
- package/dist/server-2ICEWJVK.js +0 -13
- package/dist/server-2MQV3FNY.js +0 -13
- package/dist/server-2NGD7GE3.js +0 -13
- package/dist/server-2VKO76UK.js +0 -14
- package/dist/server-3BK2VFU7.js +0 -13
- package/dist/server-3FBHBA7L.js +0 -15
- package/dist/server-4LDOB3NX.js +0 -13
- package/dist/server-4YI44KDR.js +0 -13
- package/dist/server-64XMXA5P.js +0 -13
- package/dist/server-6IPHVUYT.js +0 -14
- package/dist/server-73ORHMJN.js +0 -13
- package/dist/server-73P7M3QB.js +0 -14
- package/dist/server-BPVRW5LJ.js +0 -14
- package/dist/server-BW4RKZIX.js +0 -13
- package/dist/server-CFS5SM5K.js +0 -13
- package/dist/server-DX7VYHHM.js +0 -13
- package/dist/server-F3YPX6ET.js +0 -13
- package/dist/server-FUXTR33I.js +0 -13
- package/dist/server-G2SY3DOS.js +0 -13
- package/dist/server-G32U7VOQ.js +0 -13
- package/dist/server-HKRIY7FP.js +0 -14
- package/dist/server-HON66OES.js +0 -15
- package/dist/server-IOOZK4NP.js +0 -14
- package/dist/server-IZEO7OJJ.js +0 -14
- package/dist/server-J52LMTBT.js +0 -13
- package/dist/server-JG7UKFGK.js +0 -14
- package/dist/server-JSCHEBOD.js +0 -13
- package/dist/server-K6KC4ZOM.js +0 -13
- package/dist/server-KJVRGWFE.js +0 -13
- package/dist/server-LCPB2L4U.js +0 -13
- package/dist/server-M7LDYKAJ.js +0 -13
- package/dist/server-MKVK6ZQQ.js +0 -13
- package/dist/server-MU52LCXT.js +0 -13
- package/dist/server-NM5CKDUU.js +0 -13
- package/dist/server-NPZN3FWO.js +0 -14
- package/dist/server-O5FIAHSY.js +0 -14
- package/dist/server-OESJUEYC.js +0 -13
- package/dist/server-ONSKQO4W.js +0 -13
- package/dist/server-P27BZXBL.js +0 -14
- package/dist/server-Q4FBWQUA.js +0 -13
- package/dist/server-RNQ7VUAL.js +0 -13
- package/dist/server-S6B5WUBT.js +0 -14
- package/dist/server-SRYNSGSP.js +0 -14
- package/dist/server-SUN3W2YK.js +0 -13
- package/dist/server-UA62LHZB.js +0 -13
- package/dist/server-UJB44EW5.js +0 -13
- package/dist/server-X3TLP6DX.js +0 -14
- package/dist/server-YT2UGEZK.js +0 -13
- package/dist/server-ZBPQ33V6.js +0 -14
- package/dist/setup-27CQAX6K.js +0 -17
- package/dist/setup-AQX4JQVR.js +0 -17
- package/dist/setup-EQTU7FI6.js +0 -17
- package/dist/tools-2KPB37GK.js +0 -178
- package/dist/tools-3H6IOWXV.js +0 -178
- package/dist/tools-3KYHPDCJ.js +0 -178
- package/dist/tools-75BAPCUM.js +0 -177
- package/dist/tools-BUVCUCRL.js +0 -178
- package/dist/tools-HDNODRS6.js +0 -178
- package/dist/tools-HP5MNY3D.js +0 -177
- package/dist/tools-L6PKKQPY.js +0 -179
- package/dist/tools-N5N2IO7V.js +0 -178
- package/dist/tools-NFJEZ2FF.js +0 -177
- package/dist/tools-OPULIER6.js +0 -178
- package/dist/tools-TLCKABUW.js +0 -178
|
@@ -1,12 +1,83 @@
|
|
|
1
1
|
import {
|
|
2
|
-
PROJECTS_DIR
|
|
3
|
-
|
|
2
|
+
PROJECTS_DIR,
|
|
3
|
+
TEMPLATES_DIR
|
|
4
|
+
} from "./chunk-VVIOB362.js";
|
|
5
|
+
|
|
6
|
+
// src/core/templates/loader.ts
|
|
7
|
+
import { existsSync, readFileSync } from "fs";
|
|
8
|
+
import { join, dirname } from "path";
|
|
9
|
+
import { fileURLToPath } from "url";
|
|
10
|
+
var MANIFEST_FILE = "manifest.json";
|
|
11
|
+
var BUNDLE_DIR = "bundle";
|
|
12
|
+
var __dirname = dirname(fileURLToPath(import.meta.url));
|
|
13
|
+
var BUNDLED_TEMPLATES_DIR = join(__dirname, "..", "..", "templates");
|
|
14
|
+
var cachedManifest = null;
|
|
15
|
+
var cachedTemplatesDir = null;
|
|
16
|
+
var cachedAt = 0;
|
|
17
|
+
var CACHE_TTL = 3e4;
|
|
18
|
+
function resolveTemplatesDir() {
|
|
19
|
+
const localManifest = join(TEMPLATES_DIR, MANIFEST_FILE);
|
|
20
|
+
const localBundle = join(TEMPLATES_DIR, BUNDLE_DIR);
|
|
21
|
+
if (existsSync(localManifest) && existsSync(localBundle)) {
|
|
22
|
+
return TEMPLATES_DIR;
|
|
23
|
+
}
|
|
24
|
+
const bundledManifest = join(BUNDLED_TEMPLATES_DIR, MANIFEST_FILE);
|
|
25
|
+
const bundledBundle = join(BUNDLED_TEMPLATES_DIR, BUNDLE_DIR);
|
|
26
|
+
if (existsSync(bundledManifest) && existsSync(bundledBundle)) {
|
|
27
|
+
return BUNDLED_TEMPLATES_DIR;
|
|
28
|
+
}
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
function loadManifest() {
|
|
32
|
+
const now = Date.now();
|
|
33
|
+
if (cachedManifest && now - cachedAt < CACHE_TTL) {
|
|
34
|
+
return cachedManifest;
|
|
35
|
+
}
|
|
36
|
+
const dir = resolveTemplatesDir();
|
|
37
|
+
if (!dir) {
|
|
38
|
+
cachedManifest = null;
|
|
39
|
+
cachedTemplatesDir = null;
|
|
40
|
+
cachedAt = now;
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
try {
|
|
44
|
+
const raw = readFileSync(join(dir, MANIFEST_FILE), "utf-8");
|
|
45
|
+
const manifest = JSON.parse(raw);
|
|
46
|
+
cachedManifest = manifest;
|
|
47
|
+
cachedTemplatesDir = dir;
|
|
48
|
+
cachedAt = now;
|
|
49
|
+
return manifest;
|
|
50
|
+
} catch {
|
|
51
|
+
cachedManifest = null;
|
|
52
|
+
cachedTemplatesDir = null;
|
|
53
|
+
cachedAt = now;
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
function isTemplatesInstalled() {
|
|
58
|
+
return loadManifest() !== null;
|
|
59
|
+
}
|
|
60
|
+
function getAvailableTemplates() {
|
|
61
|
+
const manifest = loadManifest();
|
|
62
|
+
return manifest?.templates ?? [];
|
|
63
|
+
}
|
|
64
|
+
function getTemplate(id) {
|
|
65
|
+
const templates = getAvailableTemplates();
|
|
66
|
+
return templates.find((t) => t.id === id) ?? null;
|
|
67
|
+
}
|
|
68
|
+
function getBundlePath() {
|
|
69
|
+
loadManifest();
|
|
70
|
+
if (!cachedTemplatesDir) return null;
|
|
71
|
+
const bundlePath = join(cachedTemplatesDir, BUNDLE_DIR);
|
|
72
|
+
if (!existsSync(bundlePath)) return null;
|
|
73
|
+
return bundlePath;
|
|
74
|
+
}
|
|
4
75
|
|
|
5
76
|
// src/core/android/adb.ts
|
|
6
77
|
import { execSync } from "child_process";
|
|
7
|
-
import { existsSync } from "fs";
|
|
78
|
+
import { existsSync as existsSync2 } from "fs";
|
|
8
79
|
import { homedir } from "os";
|
|
9
|
-
import { join } from "path";
|
|
80
|
+
import { join as join2 } from "path";
|
|
10
81
|
var ADB_COMMAND_CACHE_SUCCESS_TTL_MS = 5 * 60 * 1e3;
|
|
11
82
|
var ADB_COMMAND_CACHE_FAILURE_TTL_MS = 5 * 1e3;
|
|
12
83
|
var adbCommandCache = null;
|
|
@@ -25,19 +96,19 @@ function findAndroidSdkPath() {
|
|
|
25
96
|
process.env.ANDROID_SDK
|
|
26
97
|
].filter((value) => typeof value === "string" && value.trim().length > 0);
|
|
27
98
|
for (const envPath of envPaths) {
|
|
28
|
-
if (
|
|
99
|
+
if (existsSync2(join2(envPath, "platform-tools", "adb"))) {
|
|
29
100
|
return envPath;
|
|
30
101
|
}
|
|
31
102
|
}
|
|
32
103
|
const home = homedir();
|
|
33
104
|
const commonPaths = [
|
|
34
|
-
|
|
35
|
-
|
|
105
|
+
join2(home, "Library", "Android", "sdk"),
|
|
106
|
+
join2(home, "Android", "Sdk"),
|
|
36
107
|
"/opt/android-sdk",
|
|
37
108
|
"/usr/local/android-sdk"
|
|
38
109
|
];
|
|
39
110
|
for (const sdkPath of commonPaths) {
|
|
40
|
-
if (
|
|
111
|
+
if (existsSync2(join2(sdkPath, "platform-tools", "adb"))) {
|
|
41
112
|
return sdkPath;
|
|
42
113
|
}
|
|
43
114
|
}
|
|
@@ -46,14 +117,14 @@ function findAndroidSdkPath() {
|
|
|
46
117
|
function getAdbPath() {
|
|
47
118
|
const sdkPath = findAndroidSdkPath();
|
|
48
119
|
if (!sdkPath) return null;
|
|
49
|
-
const adbPath =
|
|
50
|
-
return
|
|
120
|
+
const adbPath = join2(sdkPath, "platform-tools", "adb");
|
|
121
|
+
return existsSync2(adbPath) ? adbPath : null;
|
|
51
122
|
}
|
|
52
123
|
function getEmulatorPath() {
|
|
53
124
|
const sdkPath = findAndroidSdkPath();
|
|
54
125
|
if (!sdkPath) return null;
|
|
55
|
-
const emulatorPath =
|
|
56
|
-
return
|
|
126
|
+
const emulatorPath = join2(sdkPath, "emulator", "emulator");
|
|
127
|
+
return existsSync2(emulatorPath) ? emulatorPath : null;
|
|
57
128
|
}
|
|
58
129
|
function getAdbCommand(options) {
|
|
59
130
|
const forceRefresh = options?.forceRefresh === true;
|
|
@@ -74,7 +145,7 @@ function getAdbCommand(options) {
|
|
|
74
145
|
try {
|
|
75
146
|
if (candidate === "adb") {
|
|
76
147
|
execSync("which adb", { stdio: "pipe", timeout: 2e3 });
|
|
77
|
-
} else if (!
|
|
148
|
+
} else if (!existsSync2(candidate)) {
|
|
78
149
|
continue;
|
|
79
150
|
}
|
|
80
151
|
adbCommandCache = { value: candidate, checkedAt: Date.now() };
|
|
@@ -662,6 +733,212 @@ function createNavigationTestFlow(appId, tabs) {
|
|
|
662
733
|
steps
|
|
663
734
|
};
|
|
664
735
|
}
|
|
736
|
+
function parseMaestroActionsFromYaml(yamlContent) {
|
|
737
|
+
const actions = [];
|
|
738
|
+
const lines = yamlContent.split("\n");
|
|
739
|
+
let currentAction = null;
|
|
740
|
+
let actionIndex = 0;
|
|
741
|
+
const nextId = () => `action_${String(++actionIndex).padStart(3, "0")}`;
|
|
742
|
+
const stripInlineComment = (value) => {
|
|
743
|
+
let inSingle = false;
|
|
744
|
+
let inDouble = false;
|
|
745
|
+
let escaped = false;
|
|
746
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
747
|
+
const char = value[index];
|
|
748
|
+
if (escaped) {
|
|
749
|
+
escaped = false;
|
|
750
|
+
continue;
|
|
751
|
+
}
|
|
752
|
+
if (char === "\\") {
|
|
753
|
+
escaped = true;
|
|
754
|
+
continue;
|
|
755
|
+
}
|
|
756
|
+
if (char === '"' && !inSingle) {
|
|
757
|
+
inDouble = !inDouble;
|
|
758
|
+
continue;
|
|
759
|
+
}
|
|
760
|
+
if (char === "'" && !inDouble) {
|
|
761
|
+
inSingle = !inSingle;
|
|
762
|
+
continue;
|
|
763
|
+
}
|
|
764
|
+
if (char === "#" && !inSingle && !inDouble) {
|
|
765
|
+
return value.slice(0, index).trimEnd();
|
|
766
|
+
}
|
|
767
|
+
}
|
|
768
|
+
return value;
|
|
769
|
+
};
|
|
770
|
+
const cleanValue = (value) => stripInlineComment(value).trim().replace(/^["']|["']$/g, "");
|
|
771
|
+
const pushAction = (action) => {
|
|
772
|
+
actions.push({
|
|
773
|
+
id: action.id || nextId(),
|
|
774
|
+
type: action.type || "tap",
|
|
775
|
+
timestamp: action.timestamp || Date.now(),
|
|
776
|
+
description: action.description || action.type || "Action",
|
|
777
|
+
x: action.x,
|
|
778
|
+
y: action.y,
|
|
779
|
+
endX: action.endX,
|
|
780
|
+
endY: action.endY,
|
|
781
|
+
text: action.text,
|
|
782
|
+
direction: action.direction,
|
|
783
|
+
seconds: action.seconds,
|
|
784
|
+
appId: action.appId,
|
|
785
|
+
duration: action.duration,
|
|
786
|
+
screenshotPath: action.screenshotPath
|
|
787
|
+
});
|
|
788
|
+
};
|
|
789
|
+
for (const line of lines) {
|
|
790
|
+
const trimmed = line.trim();
|
|
791
|
+
if (trimmed.startsWith("#") || trimmed === "" || trimmed === "---") {
|
|
792
|
+
continue;
|
|
793
|
+
}
|
|
794
|
+
if (trimmed.startsWith("appId:")) {
|
|
795
|
+
continue;
|
|
796
|
+
}
|
|
797
|
+
if (trimmed.startsWith("- tapOn:")) {
|
|
798
|
+
const inlineValue = cleanValue(trimmed.replace("- tapOn:", ""));
|
|
799
|
+
if (inlineValue) {
|
|
800
|
+
pushAction({
|
|
801
|
+
type: "tap",
|
|
802
|
+
description: `Tap on "${inlineValue}"`,
|
|
803
|
+
text: inlineValue
|
|
804
|
+
});
|
|
805
|
+
continue;
|
|
806
|
+
}
|
|
807
|
+
currentAction = { type: "tap", description: "Tap" };
|
|
808
|
+
} else if (trimmed.startsWith("- tap:")) {
|
|
809
|
+
currentAction = { type: "tap", description: "Tap" };
|
|
810
|
+
} else if (trimmed.startsWith("- swipe:")) {
|
|
811
|
+
currentAction = { type: "swipe", description: "Swipe" };
|
|
812
|
+
} else if (trimmed.startsWith("- scroll:")) {
|
|
813
|
+
currentAction = { type: "scroll", description: "Scroll" };
|
|
814
|
+
} else if (trimmed.startsWith("- scrollUntilVisible:")) {
|
|
815
|
+
currentAction = { type: "scroll", description: "Scroll" };
|
|
816
|
+
} else if (trimmed.startsWith("- inputText:")) {
|
|
817
|
+
const text = cleanValue(trimmed.replace("- inputText:", ""));
|
|
818
|
+
pushAction({
|
|
819
|
+
type: "input",
|
|
820
|
+
description: `Input: ${text.slice(0, 20)}${text.length > 20 ? "..." : ""}`,
|
|
821
|
+
text
|
|
822
|
+
});
|
|
823
|
+
continue;
|
|
824
|
+
} else if (trimmed.startsWith("- launchApp:")) {
|
|
825
|
+
const appId = cleanValue(trimmed.replace("- launchApp:", ""));
|
|
826
|
+
pushAction({
|
|
827
|
+
type: "launch",
|
|
828
|
+
description: `Launch: ${appId}`,
|
|
829
|
+
text: appId,
|
|
830
|
+
appId
|
|
831
|
+
});
|
|
832
|
+
continue;
|
|
833
|
+
} else if (trimmed === "- launchApp") {
|
|
834
|
+
pushAction({
|
|
835
|
+
type: "launch",
|
|
836
|
+
description: "Launch app"
|
|
837
|
+
});
|
|
838
|
+
continue;
|
|
839
|
+
} else if (trimmed.startsWith("- assertVisible:")) {
|
|
840
|
+
const inlineValue = cleanValue(trimmed.replace("- assertVisible:", ""));
|
|
841
|
+
if (inlineValue) {
|
|
842
|
+
pushAction({
|
|
843
|
+
type: "assert",
|
|
844
|
+
description: `Assert visible "${inlineValue}"`,
|
|
845
|
+
text: inlineValue
|
|
846
|
+
});
|
|
847
|
+
continue;
|
|
848
|
+
}
|
|
849
|
+
currentAction = { type: "assert", description: "Assert visible" };
|
|
850
|
+
} else if (trimmed.startsWith("- extendedWaitUntil:")) {
|
|
851
|
+
currentAction = { type: "wait", description: "Wait" };
|
|
852
|
+
} else if (trimmed.startsWith("- pressKey:")) {
|
|
853
|
+
const key = cleanValue(trimmed.replace("- pressKey:", "")).toLowerCase();
|
|
854
|
+
if (key === "back") {
|
|
855
|
+
pushAction({ type: "back", description: "Back button" });
|
|
856
|
+
} else if (key === "home") {
|
|
857
|
+
pushAction({ type: "home", description: "Home button" });
|
|
858
|
+
} else {
|
|
859
|
+
pushAction({
|
|
860
|
+
type: "pressKey",
|
|
861
|
+
description: `Press: ${key}`,
|
|
862
|
+
text: key
|
|
863
|
+
});
|
|
864
|
+
}
|
|
865
|
+
continue;
|
|
866
|
+
} else if (trimmed.startsWith("- back")) {
|
|
867
|
+
pushAction({ type: "back", description: "Back button" });
|
|
868
|
+
continue;
|
|
869
|
+
}
|
|
870
|
+
if (currentAction && trimmed.startsWith("point:")) {
|
|
871
|
+
const point = cleanValue(trimmed.replace("point:", ""));
|
|
872
|
+
const [x, y] = point.split(",").map((n) => parseInt(n.trim()));
|
|
873
|
+
if (!isNaN(x) && !isNaN(y)) {
|
|
874
|
+
currentAction.x = x;
|
|
875
|
+
currentAction.y = y;
|
|
876
|
+
currentAction.description = `Tap at (${x}, ${y})`;
|
|
877
|
+
pushAction(currentAction);
|
|
878
|
+
currentAction = null;
|
|
879
|
+
continue;
|
|
880
|
+
}
|
|
881
|
+
}
|
|
882
|
+
if (currentAction && trimmed.startsWith("text:")) {
|
|
883
|
+
const text = cleanValue(trimmed.replace("text:", ""));
|
|
884
|
+
currentAction.text = text;
|
|
885
|
+
if (currentAction.type === "assert") {
|
|
886
|
+
currentAction.description = `Assert visible "${text}"`;
|
|
887
|
+
} else {
|
|
888
|
+
currentAction.description = `Tap on "${text}"`;
|
|
889
|
+
}
|
|
890
|
+
pushAction(currentAction);
|
|
891
|
+
currentAction = null;
|
|
892
|
+
continue;
|
|
893
|
+
}
|
|
894
|
+
if (currentAction && trimmed.startsWith("id:")) {
|
|
895
|
+
const id = cleanValue(trimmed.replace("id:", ""));
|
|
896
|
+
currentAction.text = id;
|
|
897
|
+
currentAction.description = `Tap on id: ${id}`;
|
|
898
|
+
pushAction(currentAction);
|
|
899
|
+
currentAction = null;
|
|
900
|
+
continue;
|
|
901
|
+
}
|
|
902
|
+
if (currentAction && (currentAction.type === "swipe" || currentAction.type === "scroll") && trimmed.startsWith("direction:")) {
|
|
903
|
+
const direction = cleanValue(trimmed.replace("direction:", "")).toLowerCase();
|
|
904
|
+
currentAction.direction = direction;
|
|
905
|
+
currentAction.description = currentAction.type === "swipe" ? `Swipe ${direction}` : `Scroll ${direction}`;
|
|
906
|
+
pushAction(currentAction);
|
|
907
|
+
currentAction = null;
|
|
908
|
+
continue;
|
|
909
|
+
}
|
|
910
|
+
if (currentAction && currentAction.type === "wait" && trimmed.startsWith("timeout:")) {
|
|
911
|
+
const timeoutMs = parseInt(cleanValue(trimmed.replace("timeout:", "")), 10);
|
|
912
|
+
const seconds = Number.isFinite(timeoutMs) && timeoutMs > 0 ? Math.max(1, Math.round(timeoutMs / 1e3)) : 3;
|
|
913
|
+
currentAction.seconds = seconds;
|
|
914
|
+
currentAction.description = `Wait ${seconds}s`;
|
|
915
|
+
pushAction(currentAction);
|
|
916
|
+
currentAction = null;
|
|
917
|
+
continue;
|
|
918
|
+
}
|
|
919
|
+
if (currentAction && currentAction.type === "swipe") {
|
|
920
|
+
if (trimmed.startsWith("start:")) {
|
|
921
|
+
const point = cleanValue(trimmed.replace("start:", ""));
|
|
922
|
+
const [x, y] = point.split(",").map((n) => parseInt(n.trim()));
|
|
923
|
+
if (!isNaN(x) && !isNaN(y)) {
|
|
924
|
+
currentAction.x = x;
|
|
925
|
+
currentAction.y = y;
|
|
926
|
+
}
|
|
927
|
+
} else if (trimmed.startsWith("end:")) {
|
|
928
|
+
const point = cleanValue(trimmed.replace("end:", ""));
|
|
929
|
+
const [x, y] = point.split(",").map((n) => parseInt(n.trim()));
|
|
930
|
+
if (!isNaN(x) && !isNaN(y)) {
|
|
931
|
+
currentAction.endX = x;
|
|
932
|
+
currentAction.endY = y;
|
|
933
|
+
currentAction.description = `Swipe from (${currentAction.x}, ${currentAction.y}) to (${x}, ${y})`;
|
|
934
|
+
pushAction(currentAction);
|
|
935
|
+
currentAction = null;
|
|
936
|
+
}
|
|
937
|
+
}
|
|
938
|
+
}
|
|
939
|
+
}
|
|
940
|
+
return actions;
|
|
941
|
+
}
|
|
665
942
|
var MaestroRecorder = class extends EventEmitter {
|
|
666
943
|
session = null;
|
|
667
944
|
adbProcess = null;
|
|
@@ -1198,6 +1475,18 @@ var MaestroRecorder = class extends EventEmitter {
|
|
|
1198
1475
|
console.log("[MaestroRecorder] Generated flow YAML from recorded actions");
|
|
1199
1476
|
}
|
|
1200
1477
|
}
|
|
1478
|
+
if (this.session.actions.length === 0 && this.session.flowPath && fs.existsSync(this.session.flowPath)) {
|
|
1479
|
+
try {
|
|
1480
|
+
const fallbackYaml = await fs.promises.readFile(this.session.flowPath, "utf-8");
|
|
1481
|
+
const parsedActions = parseMaestroActionsFromYaml(fallbackYaml);
|
|
1482
|
+
if (parsedActions.length > 0) {
|
|
1483
|
+
this.session.actions = parsedActions;
|
|
1484
|
+
console.log(`[MaestroRecorder] Rehydrated ${parsedActions.length} actions from YAML for manual capture`);
|
|
1485
|
+
}
|
|
1486
|
+
} catch (error) {
|
|
1487
|
+
console.error("[MaestroRecorder] Failed to rehydrate actions from YAML:", error);
|
|
1488
|
+
}
|
|
1489
|
+
}
|
|
1201
1490
|
const metadataPath = path.join(this.session.screenshotsDir, "..", "session.json");
|
|
1202
1491
|
await fs.promises.writeFile(metadataPath, JSON.stringify(this.session, null, 2));
|
|
1203
1492
|
this.clearPendingSession();
|
|
@@ -1211,186 +1500,13 @@ var MaestroRecorder = class extends EventEmitter {
|
|
|
1211
1500
|
* Parse actions from Maestro YAML content
|
|
1212
1501
|
*/
|
|
1213
1502
|
parseActionsFromYaml(yamlContent) {
|
|
1214
|
-
const
|
|
1215
|
-
const
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
const cleanValue = (value) => value.trim().replace(/^["']|["']$/g, "");
|
|
1220
|
-
const pushAction = (action) => {
|
|
1221
|
-
actions.push({
|
|
1222
|
-
id: action.id || nextId(),
|
|
1223
|
-
type: action.type || "tap",
|
|
1224
|
-
timestamp: action.timestamp || Date.now(),
|
|
1225
|
-
description: action.description || action.type || "Action",
|
|
1226
|
-
x: action.x,
|
|
1227
|
-
y: action.y,
|
|
1228
|
-
endX: action.endX,
|
|
1229
|
-
endY: action.endY,
|
|
1230
|
-
text: action.text,
|
|
1231
|
-
direction: action.direction,
|
|
1232
|
-
seconds: action.seconds,
|
|
1233
|
-
appId: action.appId,
|
|
1234
|
-
duration: action.duration,
|
|
1235
|
-
screenshotPath: action.screenshotPath
|
|
1236
|
-
});
|
|
1237
|
-
};
|
|
1238
|
-
for (const line of lines) {
|
|
1239
|
-
const trimmed = line.trim();
|
|
1240
|
-
if (trimmed.startsWith("#") || trimmed === "" || trimmed === "---") {
|
|
1241
|
-
continue;
|
|
1242
|
-
}
|
|
1243
|
-
if (trimmed.startsWith("appId:")) {
|
|
1244
|
-
const parsedAppId = cleanValue(trimmed.replace("appId:", ""));
|
|
1245
|
-
if (this.session && parsedAppId && !this.session.appId) {
|
|
1246
|
-
this.session.appId = parsedAppId;
|
|
1247
|
-
}
|
|
1248
|
-
continue;
|
|
1249
|
-
}
|
|
1250
|
-
if (trimmed.startsWith("- tapOn:")) {
|
|
1251
|
-
const inlineValue = cleanValue(trimmed.replace("- tapOn:", ""));
|
|
1252
|
-
if (inlineValue) {
|
|
1253
|
-
pushAction({
|
|
1254
|
-
type: "tap",
|
|
1255
|
-
description: `Tap on "${inlineValue}"`,
|
|
1256
|
-
text: inlineValue
|
|
1257
|
-
});
|
|
1258
|
-
continue;
|
|
1259
|
-
}
|
|
1260
|
-
currentAction = { type: "tap", description: "Tap" };
|
|
1261
|
-
} else if (trimmed.startsWith("- tap:")) {
|
|
1262
|
-
currentAction = { type: "tap", description: "Tap" };
|
|
1263
|
-
} else if (trimmed.startsWith("- swipe:")) {
|
|
1264
|
-
currentAction = { type: "swipe", description: "Swipe" };
|
|
1265
|
-
} else if (trimmed.startsWith("- scroll:")) {
|
|
1266
|
-
currentAction = { type: "scroll", description: "Scroll" };
|
|
1267
|
-
} else if (trimmed.startsWith("- scrollUntilVisible:")) {
|
|
1268
|
-
currentAction = { type: "scroll", description: "Scroll" };
|
|
1269
|
-
} else if (trimmed.startsWith("- inputText:")) {
|
|
1270
|
-
const text = cleanValue(trimmed.replace("- inputText:", ""));
|
|
1271
|
-
pushAction({
|
|
1272
|
-
type: "input",
|
|
1273
|
-
description: `Input: ${text.slice(0, 20)}${text.length > 20 ? "..." : ""}`,
|
|
1274
|
-
text
|
|
1275
|
-
});
|
|
1276
|
-
continue;
|
|
1277
|
-
} else if (trimmed.startsWith("- launchApp:")) {
|
|
1278
|
-
const appId = cleanValue(trimmed.replace("- launchApp:", ""));
|
|
1279
|
-
pushAction({
|
|
1280
|
-
type: "launch",
|
|
1281
|
-
description: `Launch: ${appId}`,
|
|
1282
|
-
text: appId,
|
|
1283
|
-
appId
|
|
1284
|
-
});
|
|
1285
|
-
continue;
|
|
1286
|
-
} else if (trimmed === "- launchApp") {
|
|
1287
|
-
pushAction({
|
|
1288
|
-
type: "launch",
|
|
1289
|
-
description: "Launch app"
|
|
1290
|
-
});
|
|
1291
|
-
continue;
|
|
1292
|
-
} else if (trimmed.startsWith("- assertVisible:")) {
|
|
1293
|
-
const inlineValue = cleanValue(trimmed.replace("- assertVisible:", ""));
|
|
1294
|
-
if (inlineValue) {
|
|
1295
|
-
pushAction({
|
|
1296
|
-
type: "assert",
|
|
1297
|
-
description: `Assert visible "${inlineValue}"`,
|
|
1298
|
-
text: inlineValue
|
|
1299
|
-
});
|
|
1300
|
-
continue;
|
|
1301
|
-
}
|
|
1302
|
-
currentAction = { type: "assert", description: "Assert visible" };
|
|
1303
|
-
} else if (trimmed.startsWith("- extendedWaitUntil:")) {
|
|
1304
|
-
currentAction = { type: "wait", description: "Wait" };
|
|
1305
|
-
} else if (trimmed.startsWith("- pressKey:")) {
|
|
1306
|
-
const key = cleanValue(trimmed.replace("- pressKey:", "")).toLowerCase();
|
|
1307
|
-
if (key === "back") {
|
|
1308
|
-
pushAction({ type: "back", description: "Back button" });
|
|
1309
|
-
} else if (key === "home") {
|
|
1310
|
-
pushAction({ type: "home", description: "Home button" });
|
|
1311
|
-
} else {
|
|
1312
|
-
pushAction({
|
|
1313
|
-
type: "pressKey",
|
|
1314
|
-
description: `Press: ${key}`,
|
|
1315
|
-
text: key
|
|
1316
|
-
});
|
|
1317
|
-
}
|
|
1318
|
-
continue;
|
|
1319
|
-
} else if (trimmed.startsWith("- back")) {
|
|
1320
|
-
pushAction({ type: "back", description: "Back button" });
|
|
1321
|
-
continue;
|
|
1322
|
-
}
|
|
1323
|
-
if (currentAction && trimmed.startsWith("point:")) {
|
|
1324
|
-
const point = cleanValue(trimmed.replace("point:", ""));
|
|
1325
|
-
const [x, y] = point.split(",").map((n) => parseInt(n.trim()));
|
|
1326
|
-
if (!isNaN(x) && !isNaN(y)) {
|
|
1327
|
-
currentAction.x = x;
|
|
1328
|
-
currentAction.y = y;
|
|
1329
|
-
currentAction.description = `Tap at (${x}, ${y})`;
|
|
1330
|
-
pushAction(currentAction);
|
|
1331
|
-
currentAction = null;
|
|
1332
|
-
continue;
|
|
1333
|
-
}
|
|
1334
|
-
}
|
|
1335
|
-
if (currentAction && trimmed.startsWith("text:")) {
|
|
1336
|
-
const text = cleanValue(trimmed.replace("text:", ""));
|
|
1337
|
-
currentAction.text = text;
|
|
1338
|
-
if (currentAction.type === "assert") {
|
|
1339
|
-
currentAction.description = `Assert visible "${text}"`;
|
|
1340
|
-
} else {
|
|
1341
|
-
currentAction.description = `Tap on "${text}"`;
|
|
1342
|
-
}
|
|
1343
|
-
pushAction(currentAction);
|
|
1344
|
-
currentAction = null;
|
|
1345
|
-
continue;
|
|
1346
|
-
}
|
|
1347
|
-
if (currentAction && trimmed.startsWith("id:")) {
|
|
1348
|
-
const id = cleanValue(trimmed.replace("id:", ""));
|
|
1349
|
-
currentAction.text = id;
|
|
1350
|
-
currentAction.description = `Tap on id: ${id}`;
|
|
1351
|
-
pushAction(currentAction);
|
|
1352
|
-
currentAction = null;
|
|
1353
|
-
continue;
|
|
1354
|
-
}
|
|
1355
|
-
if (currentAction && (currentAction.type === "swipe" || currentAction.type === "scroll") && trimmed.startsWith("direction:")) {
|
|
1356
|
-
const direction = cleanValue(trimmed.replace("direction:", "")).toLowerCase();
|
|
1357
|
-
currentAction.direction = direction;
|
|
1358
|
-
currentAction.description = currentAction.type === "swipe" ? `Swipe ${direction}` : `Scroll ${direction}`;
|
|
1359
|
-
pushAction(currentAction);
|
|
1360
|
-
currentAction = null;
|
|
1361
|
-
continue;
|
|
1362
|
-
}
|
|
1363
|
-
if (currentAction && currentAction.type === "wait" && trimmed.startsWith("timeout:")) {
|
|
1364
|
-
const timeoutMs = parseInt(cleanValue(trimmed.replace("timeout:", "")), 10);
|
|
1365
|
-
const seconds = Number.isFinite(timeoutMs) && timeoutMs > 0 ? Math.max(1, Math.round(timeoutMs / 1e3)) : 3;
|
|
1366
|
-
currentAction.seconds = seconds;
|
|
1367
|
-
currentAction.description = `Wait ${seconds}s`;
|
|
1368
|
-
pushAction(currentAction);
|
|
1369
|
-
currentAction = null;
|
|
1370
|
-
continue;
|
|
1371
|
-
}
|
|
1372
|
-
if (currentAction && currentAction.type === "swipe") {
|
|
1373
|
-
if (trimmed.startsWith("start:")) {
|
|
1374
|
-
const point = cleanValue(trimmed.replace("start:", ""));
|
|
1375
|
-
const [x, y] = point.split(",").map((n) => parseInt(n.trim()));
|
|
1376
|
-
if (!isNaN(x) && !isNaN(y)) {
|
|
1377
|
-
currentAction.x = x;
|
|
1378
|
-
currentAction.y = y;
|
|
1379
|
-
}
|
|
1380
|
-
} else if (trimmed.startsWith("end:")) {
|
|
1381
|
-
const point = cleanValue(trimmed.replace("end:", ""));
|
|
1382
|
-
const [x, y] = point.split(",").map((n) => parseInt(n.trim()));
|
|
1383
|
-
if (!isNaN(x) && !isNaN(y)) {
|
|
1384
|
-
currentAction.endX = x;
|
|
1385
|
-
currentAction.endY = y;
|
|
1386
|
-
currentAction.description = `Swipe from (${currentAction.x}, ${currentAction.y}) to (${x}, ${y})`;
|
|
1387
|
-
pushAction(currentAction);
|
|
1388
|
-
currentAction = null;
|
|
1389
|
-
}
|
|
1390
|
-
}
|
|
1391
|
-
}
|
|
1503
|
+
const parsedActions = parseMaestroActionsFromYaml(yamlContent);
|
|
1504
|
+
const appIdMatch = yamlContent.match(/^\s*appId:\s*([^\n#]+)/m);
|
|
1505
|
+
const parsedAppId = appIdMatch?.[1]?.trim().replace(/^["']|["']$/g, "") || "";
|
|
1506
|
+
if (this.session && parsedAppId && !this.session.appId) {
|
|
1507
|
+
this.session.appId = parsedAppId;
|
|
1392
1508
|
}
|
|
1393
|
-
return
|
|
1509
|
+
return parsedActions;
|
|
1394
1510
|
}
|
|
1395
1511
|
mergeParsedActionsWithRecorded(parsedActions, recordedActions) {
|
|
1396
1512
|
if (parsedActions.length === 0) return recordedActions;
|
|
@@ -1547,6 +1663,10 @@ function getMaestroRecorder() {
|
|
|
1547
1663
|
}
|
|
1548
1664
|
|
|
1549
1665
|
export {
|
|
1666
|
+
isTemplatesInstalled,
|
|
1667
|
+
getAvailableTemplates,
|
|
1668
|
+
getTemplate,
|
|
1669
|
+
getBundlePath,
|
|
1550
1670
|
findAndroidSdkPath,
|
|
1551
1671
|
getEmulatorPath,
|
|
1552
1672
|
getAdbCommand,
|
|
@@ -1565,5 +1685,6 @@ export {
|
|
|
1565
1685
|
createLoginFlow,
|
|
1566
1686
|
createOnboardingFlow,
|
|
1567
1687
|
createNavigationTestFlow,
|
|
1688
|
+
parseMaestroActionsFromYaml,
|
|
1568
1689
|
getMaestroRecorder
|
|
1569
1690
|
};
|