android-midscene-automation 0.1.6 → 0.1.9
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/README.md +8 -10
- package/package.json +3 -3
- package/server/appium-recorder/appium-runner.ts +362 -26
- package/server/appium-recorder/repository.ts +89 -12
- package/src/appium-recorder/AppiumPage.vue +504 -124
- package/src/appium-recorder/components/ComponentTree.vue +14 -1
- package/src/appium-recorder/components/NodeDetail.vue +24 -118
- package/src/appium-recorder/components/RecordedSteps.vue +898 -36
- package/src/appium-recorder/tree.ts +55 -9
- package/src/appium-recorder/types.ts +33 -1
- package/src/style.css +659 -11
|
@@ -12,6 +12,10 @@ export type AppiumRecordedStepRecord = {
|
|
|
12
12
|
type:
|
|
13
13
|
| 'tap'
|
|
14
14
|
| 'input'
|
|
15
|
+
| 'tapIfExists'
|
|
16
|
+
| 'inputIfExists'
|
|
17
|
+
| 'clearIfExists'
|
|
18
|
+
| 'backIfExists'
|
|
15
19
|
| 'waitFor'
|
|
16
20
|
| 'assertExists'
|
|
17
21
|
| 'key'
|
|
@@ -25,23 +29,72 @@ export type AppiumRecordedStepRecord = {
|
|
|
25
29
|
| 'waitDisappear'
|
|
26
30
|
| 'assertText'
|
|
27
31
|
| 'longPress'
|
|
28
|
-
| 'pinch'
|
|
32
|
+
| 'pinch'
|
|
33
|
+
| 'runScript';
|
|
29
34
|
label: string;
|
|
30
35
|
selector?: {
|
|
31
36
|
strategy: 'accessibilityId' | 'id' | 'androidUiAutomator' | 'xpath' | 'bounds';
|
|
32
37
|
value?: string;
|
|
33
38
|
centerX?: number;
|
|
34
39
|
centerY?: number;
|
|
40
|
+
unique?: boolean;
|
|
41
|
+
matchCount?: number;
|
|
42
|
+
source?: 'node' | 'ancestor' | 'fallback';
|
|
35
43
|
};
|
|
44
|
+
contextSelector?: {
|
|
45
|
+
strategy: 'accessibilityId' | 'id' | 'androidUiAutomator' | 'xpath' | 'bounds';
|
|
46
|
+
value?: string;
|
|
47
|
+
centerX?: number;
|
|
48
|
+
centerY?: number;
|
|
49
|
+
unique?: boolean;
|
|
50
|
+
matchCount?: number;
|
|
51
|
+
source?: 'node' | 'ancestor' | 'fallback';
|
|
52
|
+
};
|
|
53
|
+
selectorChain?: Array<{
|
|
54
|
+
strategy: 'accessibilityId' | 'id' | 'androidUiAutomator' | 'xpath' | 'bounds';
|
|
55
|
+
value?: string;
|
|
56
|
+
centerX?: number;
|
|
57
|
+
centerY?: number;
|
|
58
|
+
unique?: boolean;
|
|
59
|
+
matchCount?: number;
|
|
60
|
+
source?: 'node' | 'ancestor' | 'fallback';
|
|
61
|
+
}>;
|
|
36
62
|
fallback?: {
|
|
37
63
|
strategy: 'accessibilityId' | 'id' | 'androidUiAutomator' | 'xpath' | 'bounds';
|
|
38
64
|
value?: string;
|
|
39
65
|
centerX?: number;
|
|
40
66
|
centerY?: number;
|
|
67
|
+
unique?: boolean;
|
|
68
|
+
matchCount?: number;
|
|
69
|
+
source?: 'node' | 'ancestor' | 'fallback';
|
|
41
70
|
};
|
|
42
71
|
value?: string;
|
|
72
|
+
optional?: boolean;
|
|
43
73
|
keyCode?: number;
|
|
44
74
|
timeoutMs?: number;
|
|
75
|
+
flow?: {
|
|
76
|
+
scope?: 'pre' | 'main' | 'post';
|
|
77
|
+
nodeKind?: 'action' | 'condition' | 'assertion';
|
|
78
|
+
yesTargetId?: string;
|
|
79
|
+
noTargetId?: string;
|
|
80
|
+
parentConditionId?: string;
|
|
81
|
+
parentBranch?: 'yes' | 'no';
|
|
82
|
+
successTargetId?: string;
|
|
83
|
+
failureTargetId?: string;
|
|
84
|
+
collapsed?: boolean;
|
|
85
|
+
};
|
|
86
|
+
pageBefore?: {
|
|
87
|
+
activity?: string;
|
|
88
|
+
packageName?: string;
|
|
89
|
+
treeSignature?: string;
|
|
90
|
+
selectedNodePath?: string;
|
|
91
|
+
};
|
|
92
|
+
pageAfter?: {
|
|
93
|
+
activity?: string;
|
|
94
|
+
packageName?: string;
|
|
95
|
+
treeSignature?: string;
|
|
96
|
+
selectedNodePath?: string;
|
|
97
|
+
};
|
|
45
98
|
swipe?: {
|
|
46
99
|
startX: number;
|
|
47
100
|
startY: number;
|
|
@@ -80,16 +133,14 @@ type AppiumRecordedScriptRow = {
|
|
|
80
133
|
app_package: string;
|
|
81
134
|
app_activity: string | null;
|
|
82
135
|
device_id: string | null;
|
|
83
|
-
|
|
136
|
+
flow_json: string | null;
|
|
84
137
|
created_at: string;
|
|
85
138
|
updated_at: string;
|
|
86
139
|
};
|
|
87
140
|
|
|
88
141
|
let initialized = false;
|
|
89
142
|
|
|
90
|
-
function
|
|
91
|
-
if (initialized) return;
|
|
92
|
-
initialized = true;
|
|
143
|
+
function createAppiumRecordedScriptsTable() {
|
|
93
144
|
runSql(`
|
|
94
145
|
CREATE TABLE IF NOT EXISTS appium_recorded_scripts (
|
|
95
146
|
id TEXT PRIMARY KEY,
|
|
@@ -97,7 +148,7 @@ function initDb() {
|
|
|
97
148
|
app_package TEXT NOT NULL,
|
|
98
149
|
app_activity TEXT,
|
|
99
150
|
device_id TEXT,
|
|
100
|
-
|
|
151
|
+
flow_json TEXT NOT NULL DEFAULT '[]',
|
|
101
152
|
created_at TEXT NOT NULL,
|
|
102
153
|
updated_at TEXT NOT NULL
|
|
103
154
|
);
|
|
@@ -106,6 +157,32 @@ function initDb() {
|
|
|
106
157
|
`);
|
|
107
158
|
}
|
|
108
159
|
|
|
160
|
+
function initDb() {
|
|
161
|
+
if (initialized) return;
|
|
162
|
+
initialized = true;
|
|
163
|
+
createAppiumRecordedScriptsTable();
|
|
164
|
+
const columns = querySql<{ name: string }>('PRAGMA table_info(appium_recorded_scripts);').map((column) => column.name);
|
|
165
|
+
if (columns.includes('steps_json') || !columns.includes('flow_json')) {
|
|
166
|
+
runSql('DROP TABLE appium_recorded_scripts;');
|
|
167
|
+
createAppiumRecordedScriptsTable();
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
function parseStepList(value?: string | null) {
|
|
172
|
+
try {
|
|
173
|
+
const parsed = JSON.parse(value || '[]');
|
|
174
|
+
if (!Array.isArray(parsed)) return [];
|
|
175
|
+
return parsed.filter((step) => (
|
|
176
|
+
step
|
|
177
|
+
&& typeof step === 'object'
|
|
178
|
+
&& typeof step.id === 'string'
|
|
179
|
+
&& typeof step.type === 'string'
|
|
180
|
+
)) as AppiumRecordedStepRecord[];
|
|
181
|
+
} catch {
|
|
182
|
+
return [];
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
109
186
|
function rowToRecord(row: AppiumRecordedScriptRow): AppiumRecordedScriptRecord {
|
|
110
187
|
return {
|
|
111
188
|
id: row.id,
|
|
@@ -113,7 +190,7 @@ function rowToRecord(row: AppiumRecordedScriptRow): AppiumRecordedScriptRecord {
|
|
|
113
190
|
appPackage: row.app_package,
|
|
114
191
|
appActivity: row.app_activity || '',
|
|
115
192
|
deviceId: row.device_id || '',
|
|
116
|
-
steps:
|
|
193
|
+
steps: parseStepList(row.flow_json),
|
|
117
194
|
createdAt: row.created_at,
|
|
118
195
|
updatedAt: row.updated_at,
|
|
119
196
|
};
|
|
@@ -128,7 +205,7 @@ export function listAppiumRecordedScripts() {
|
|
|
128
205
|
app_package,
|
|
129
206
|
app_activity,
|
|
130
207
|
device_id,
|
|
131
|
-
|
|
208
|
+
flow_json,
|
|
132
209
|
created_at,
|
|
133
210
|
updated_at
|
|
134
211
|
FROM appium_recorded_scripts
|
|
@@ -145,7 +222,7 @@ export function getAppiumRecordedScript(id: string) {
|
|
|
145
222
|
app_package,
|
|
146
223
|
app_activity,
|
|
147
224
|
device_id,
|
|
148
|
-
|
|
225
|
+
flow_json,
|
|
149
226
|
created_at,
|
|
150
227
|
updated_at
|
|
151
228
|
FROM appium_recorded_scripts
|
|
@@ -180,7 +257,7 @@ export function saveAppiumRecordedScript(input: {
|
|
|
180
257
|
app_package = ${sqlString(appPackage)},
|
|
181
258
|
app_activity = ${sqlNullableString(input.appActivity || '')},
|
|
182
259
|
device_id = ${sqlNullableString(input.deviceId || '')},
|
|
183
|
-
|
|
260
|
+
flow_json = ${sqlJson(input.steps || [])},
|
|
184
261
|
updated_at = ${sqlString(now)}
|
|
185
262
|
WHERE id = ${sqlString(input.id)};
|
|
186
263
|
`);
|
|
@@ -194,7 +271,7 @@ export function saveAppiumRecordedScript(input: {
|
|
|
194
271
|
app_package,
|
|
195
272
|
app_activity,
|
|
196
273
|
device_id,
|
|
197
|
-
|
|
274
|
+
flow_json,
|
|
198
275
|
created_at,
|
|
199
276
|
updated_at
|
|
200
277
|
)
|
|
@@ -212,7 +289,7 @@ export function saveAppiumRecordedScript(input: {
|
|
|
212
289
|
app_package = excluded.app_package,
|
|
213
290
|
app_activity = excluded.app_activity,
|
|
214
291
|
device_id = excluded.device_id,
|
|
215
|
-
|
|
292
|
+
flow_json = excluded.flow_json,
|
|
216
293
|
updated_at = excluded.updated_at;
|
|
217
294
|
`);
|
|
218
295
|
|