ms-vite-plugin 1.4.41 → 1.4.45
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/docs/api/global.md +70 -15
- package/docs/api/node.md +239 -348
- package/docs/apicn/global.md +65 -10
- package/docs/apicn/node.md +242 -365
- package/docs/apipython/g.md +74 -23
- package/docs/apipython/node.md +186 -287
- package/docs/httpapi/api.md +190 -189
- package/package.json +1 -1
package/docs/api/node.md
CHANGED
|
@@ -1,439 +1,330 @@
|
|
|
1
1
|
# 节点模块 (Node)
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
用于查找和操作当前界面上的控件节点。
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
**注意**
|
|
6
6
|
|
|
7
|
-
-
|
|
8
|
-
-
|
|
7
|
+
- 仅支持快点 Agent 模式
|
|
8
|
+
- 用完调用 `releaseNode()` 释放;找完节点即可释放,不必等点击结束
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
**基本流程**
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
12
|
+
```text
|
|
13
|
+
createNodeSelector → 设置筛选条件 → getOneNodeInfo / getNodeInfo
|
|
14
|
+
→ 点击 / 输入 / 设值等 → releaseNode
|
|
15
|
+
```
|
|
15
16
|
|
|
16
|
-
|
|
17
|
+
---
|
|
17
18
|
|
|
18
|
-
|
|
19
|
+
## 创建选择器
|
|
19
20
|
|
|
20
21
|
```typescript
|
|
21
22
|
function createNodeSelector(params?: {
|
|
22
|
-
maxDepth?: number; //
|
|
23
|
-
mode?: number; //
|
|
23
|
+
maxDepth?: number; // 最大层级,默认 50;越小通常越快
|
|
24
|
+
mode?: number; // 抓取模式 1 | 2 | 3,默认 1
|
|
24
25
|
}): NodeSelector;
|
|
25
26
|
```
|
|
26
27
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
|
30
|
-
|
|
|
31
|
-
| `maxDepth` | number | 否 | 50 | 遍历的最大层级深度,默认 50 |
|
|
32
|
-
| `mode` | number | 否 | 1 | 抓取模式。支持模式 1、模式 2、模式 3,默认模式 1 |
|
|
33
|
-
|
|
34
|
-
**返回值:**
|
|
35
|
-
|
|
36
|
-
| 类型 | 描述 |
|
|
37
|
-
| -------------- | ---------------------- |
|
|
38
|
-
| `NodeSelector` | 新创建的节点选择器实例 |
|
|
39
|
-
|
|
40
|
-
**示例:**
|
|
41
|
-
|
|
42
|
-
```typescript
|
|
43
|
-
// 创建选择器
|
|
44
|
-
const selector = createNodeSelector({
|
|
45
|
-
maxDepth: 20, // 最大20层深度
|
|
46
|
-
mode: 1, // 默认模式 1
|
|
47
|
-
});
|
|
48
|
-
```
|
|
49
|
-
|
|
50
|
-
### 节点信息接口
|
|
51
|
-
|
|
52
|
-
#### NodeBoundsInfo - 节点边界信息。
|
|
53
|
-
|
|
54
|
-
```typescript
|
|
55
|
-
interface NodeBoundsInfo {
|
|
56
|
-
x: number; // x坐标
|
|
57
|
-
y: number; // y坐标
|
|
58
|
-
width: number; // 宽度
|
|
59
|
-
height: number; // 高度
|
|
60
|
-
centerX: number; // 中心点x坐标
|
|
61
|
-
centerY: number; // 中心点y坐标
|
|
62
|
-
}
|
|
63
|
-
```
|
|
64
|
-
|
|
65
|
-
#### NodeInfo - 节点详细信息。
|
|
66
|
-
|
|
67
|
-
```typescript
|
|
68
|
-
interface NodeInfo {
|
|
69
|
-
id: string; // 节点ID
|
|
70
|
-
identifier: string; // 节点标识符
|
|
71
|
-
label: string; // 节点标签
|
|
72
|
-
type: string; // 节点类型
|
|
73
|
-
value: string; // 节点值
|
|
74
|
-
placeholderValue: string; // 节点占位符值
|
|
75
|
-
title: string; // 节点名称
|
|
76
|
-
enabled: boolean; // 是否启用
|
|
77
|
-
bounds: NodeBoundsInfo; // 边界信息
|
|
78
|
-
depth: number; // 层级深度
|
|
79
|
-
index: number; // 索引
|
|
80
|
-
parentId: string; // 父节点ID
|
|
81
|
-
childCount: number; // 子节点数量
|
|
82
|
-
|
|
83
|
-
// 操作方法
|
|
84
|
-
clickCenter(): boolean; // 点击中心
|
|
85
|
-
clickRandom(): boolean; // 随机点击
|
|
86
|
-
hittable(): boolean; // 节点是否可接收事件,用于判断节点是否在屏幕可见
|
|
87
|
-
tap(): boolean; // 无障碍点击
|
|
88
|
-
doubleTap(): boolean; // 无障碍双击
|
|
89
|
-
press(duration: number): boolean; // 无障碍长按,duration 单位毫秒
|
|
90
|
-
scrollToVisible(): boolean; // 将节点滚动到可见区域
|
|
91
|
-
clearInput(): boolean; // 清空输入
|
|
92
|
-
input(text: string): boolean; // 追加输入(聚焦后输入)
|
|
93
|
-
setValue(text: string): boolean; // 设置节点的值(见下方支持控件)
|
|
94
|
-
parent(): NodeInfo | null; // 获取父节点
|
|
95
|
-
child(index: number): NodeInfo | null; // 获取子节点
|
|
96
|
-
allChildren(): NodeInfo[]; // 获取所有子节点
|
|
97
|
-
siblings(): NodeInfo[]; // 获取兄弟节点
|
|
98
|
-
previousSiblings(): NodeInfo[]; // 获取前面的兄弟节点
|
|
99
|
-
nextSiblings(): NodeInfo[]; // 获取后面的兄弟节点
|
|
100
|
-
}
|
|
101
|
-
```
|
|
102
|
-
|
|
103
|
-
### 选择器方法
|
|
104
|
-
|
|
105
|
-
#### 获取节点
|
|
28
|
+
| 参数 | 默认 | 说明 |
|
|
29
|
+
| --- | --- | --- |
|
|
30
|
+
| `maxDepth` | `50` | 抓取深度,深层控件抓不到时可调大 |
|
|
31
|
+
| `mode` | `1` | 抓取模式,可选 `1` / `2` / `3`,默认 `1` |
|
|
106
32
|
|
|
107
33
|
```typescript
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
// xml - 获取当前页面的 XML 字符串
|
|
112
|
-
xml(timeout?: number): string | null;
|
|
113
|
-
|
|
114
|
-
// 获取多个节点
|
|
115
|
-
getNodeInfo(timeout?: number): NodeInfo[]
|
|
116
|
-
|
|
117
|
-
// 获取单个节点
|
|
118
|
-
getOneNodeInfo(timeout?: number): NodeInfo | null
|
|
119
|
-
|
|
120
|
-
// 加载节点数据
|
|
121
|
-
loadNode(timeout?: number): NodeSelector;
|
|
122
|
-
|
|
123
|
-
// 清除所有选择条件
|
|
124
|
-
clearSelector(): NodeSelector;
|
|
34
|
+
const selector = createNodeSelector();
|
|
35
|
+
const shallow = createNodeSelector({ maxDepth: 15 });
|
|
36
|
+
const listPage = createNodeSelector({ mode: 2 });
|
|
125
37
|
```
|
|
126
38
|
|
|
127
|
-
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
## 节点信息
|
|
42
|
+
|
|
43
|
+
### 位置 `NodeBoundsInfo`
|
|
44
|
+
|
|
45
|
+
| 字段 | 说明 |
|
|
46
|
+
| --- | --- |
|
|
47
|
+
| `x` / `y` | 左上角 |
|
|
48
|
+
| `width` / `height` | 宽高 |
|
|
49
|
+
| `centerX` / `centerY` | 中心点 |
|
|
50
|
+
|
|
51
|
+
### 节点 `NodeInfo`
|
|
52
|
+
|
|
53
|
+
**属性**
|
|
54
|
+
|
|
55
|
+
| 字段 | 说明 |
|
|
56
|
+
| --- | --- |
|
|
57
|
+
| `id` | 节点 ID(内部定位用,不要改) |
|
|
58
|
+
| `identifier` | 控件标识符 |
|
|
59
|
+
| `label` | 标签文案 |
|
|
60
|
+
| `type` | 类型,如 `XCUIElementTypeButton` |
|
|
61
|
+
| `value` | 当前值 |
|
|
62
|
+
| `placeholderValue` | 输入框占位文字 |
|
|
63
|
+
| `title` | 标题 |
|
|
64
|
+
| `enabled` | 是否启用 |
|
|
65
|
+
| `bounds` | 位置大小 |
|
|
66
|
+
| `depth` | 层级 |
|
|
67
|
+
| `index` | 在父节点下的序号 |
|
|
68
|
+
| `parentId` | 父节点 ID |
|
|
69
|
+
| `childCount` | 子节点数量 |
|
|
70
|
+
|
|
71
|
+
**方法**
|
|
72
|
+
|
|
73
|
+
| 方法 | 说明 |
|
|
74
|
+
| --- | --- |
|
|
75
|
+
| `hittable()` | 当前是否可点击(可见且未被挡住) |
|
|
76
|
+
| `scrollToVisible()` | 仅滚动到可见,不点击;一般不必手动调用 |
|
|
77
|
+
| `tap()` | 单击;不可见时会先自动滚到可见 |
|
|
78
|
+
| `doubleTap()` | 双击;不可见时会先自动滚到可见 |
|
|
79
|
+
| `press(duration?)` | 长按,`duration` 单位毫秒,默认 500;不可见时会先自动滚到可见 |
|
|
80
|
+
| `clickCenter()` | 点击中心坐标(坐标点击,不会自动滚动) |
|
|
81
|
+
| `clickRandom()` | 在节点范围内随机点击(坐标点击,不会自动滚动) |
|
|
82
|
+
| `clearInput()` | 清空输入内容(内部会先 `tap` 聚焦,因而也会自动滚到可见) |
|
|
83
|
+
| `input(text)` | 追加输入(不先清空;内部会先 `tap` 聚焦) |
|
|
84
|
+
| `setValue(text)` | 设置值(见下方;不可见时会先自动滚到可见) |
|
|
85
|
+
| `parent()` | 父节点 |
|
|
86
|
+
| `child(index)` | 第 `index` 个子节点 |
|
|
87
|
+
| `allChildren()` | 全部子节点 |
|
|
88
|
+
| `siblings()` | 全部兄弟节点 |
|
|
89
|
+
| `previousSiblings()` | 前面的兄弟 |
|
|
90
|
+
| `nextSiblings()` | 后面的兄弟 |
|
|
91
|
+
|
|
92
|
+
---
|
|
93
|
+
|
|
94
|
+
## 选择器方法
|
|
95
|
+
|
|
96
|
+
### 加载与结果
|
|
97
|
+
|
|
98
|
+
| 方法 | 说明 |
|
|
99
|
+
| --- | --- |
|
|
100
|
+
| `releaseNode()` | 释放内存 |
|
|
101
|
+
| `loadNode(timeout?)` | 重新加载界面节点,默认超时 5000ms |
|
|
102
|
+
| `clearSelector()` | 清除已设置的筛选条件 |
|
|
103
|
+
| `xml(timeout?)` | 获取界面 XML,失败返回 `null` |
|
|
104
|
+
| `getNodeInfo(timeout?)` | 获取所有匹配节点 |
|
|
105
|
+
| `getOneNodeInfo(timeout?)` | 获取第一个匹配节点,没有则 `null` |
|
|
106
|
+
|
|
107
|
+
`timeout` 单位均为毫秒,默认 `5000`。
|
|
128
108
|
|
|
129
109
|
```typescript
|
|
130
110
|
const selector = createNodeSelector();
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
const
|
|
134
|
-
logi(`找到 ${allNodes.length} 个节点`);
|
|
135
|
-
|
|
136
|
-
// 获取第一个匹配的节点
|
|
137
|
-
const firstNode = selector.getOneNodeInfo(3000);
|
|
138
|
-
if (firstNode) {
|
|
139
|
-
logi(`节点标签: ${firstNode.label}`);
|
|
140
|
-
}
|
|
141
|
-
|
|
111
|
+
const nodes = selector.label("确定").getNodeInfo(5000);
|
|
112
|
+
const one = selector.getOneNodeInfo(3000);
|
|
113
|
+
const xml = selector.xml();
|
|
142
114
|
selector.releaseNode();
|
|
143
115
|
```
|
|
144
116
|
|
|
145
|
-
|
|
117
|
+
### 筛选条件
|
|
146
118
|
|
|
147
|
-
|
|
119
|
+
可链式调用,多个条件同时满足(并且关系)。
|
|
120
|
+
带 `Match` 的为模糊/正则匹配。
|
|
148
121
|
|
|
149
|
-
|
|
150
|
-
// 精确匹配标签
|
|
151
|
-
label(label: string): NodeSelector
|
|
122
|
+
**文本**
|
|
152
123
|
|
|
153
|
-
|
|
154
|
-
|
|
124
|
+
| 方法 | 说明 |
|
|
125
|
+
| --- | --- |
|
|
126
|
+
| `label` / `labelMatch` | 标签 |
|
|
127
|
+
| `title` / `titleMatch` | 标题 |
|
|
128
|
+
| `identifier` / `identifierMatch` | 标识符 |
|
|
129
|
+
| `value` / `valueMatch` | 值 |
|
|
130
|
+
| `placeholderValue` / `placeholderValueMatch` | 占位文字 |
|
|
155
131
|
|
|
156
|
-
|
|
157
|
-
title(title: string): NodeSelector
|
|
132
|
+
**类型与状态**
|
|
158
133
|
|
|
159
|
-
|
|
160
|
-
|
|
134
|
+
| 方法 | 说明 |
|
|
135
|
+
| --- | --- |
|
|
136
|
+
| `type` / `typeMatch` | 类型,如 `XCUIElementTypeButton` |
|
|
137
|
+
| `enabled` | 是否启用 |
|
|
161
138
|
|
|
162
|
-
|
|
163
|
-
identifier(identifier: string): NodeSelector
|
|
139
|
+
**位置与结构**
|
|
164
140
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
```
|
|
174
|
-
|
|
175
|
-
**示例:**
|
|
141
|
+
| 方法 | 说明 |
|
|
142
|
+
| --- | --- |
|
|
143
|
+
| `index` | 序号 |
|
|
144
|
+
| `depth` | 层级 |
|
|
145
|
+
| `childCount` | 子节点数,支持数字或如 `">0"` |
|
|
146
|
+
| `bounds(x, y, width, height)` | 区域 |
|
|
147
|
+
| `xpath` | XPath |
|
|
148
|
+
| `id` | 按节点 ID |
|
|
176
149
|
|
|
177
150
|
```typescript
|
|
178
|
-
//
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
// 查找包含"登录"的元素
|
|
182
|
-
const loginElements = selector.labelMatch(".*登录.*").getNodeInfo(3000);
|
|
151
|
+
// 精确
|
|
152
|
+
selector.label("确定").type("XCUIElementTypeButton").getOneNodeInfo(3000);
|
|
183
153
|
|
|
184
|
-
//
|
|
185
|
-
|
|
154
|
+
// 模糊
|
|
155
|
+
selector.labelMatch(".*登录.*").getNodeInfo(3000);
|
|
156
|
+
selector.identifierMatch("^login_").getNodeInfo(3000);
|
|
186
157
|
|
|
187
|
-
//
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
const fuzzy = selector.identifierMatch("^login_.*").getNodeInfo(3000);
|
|
158
|
+
// 组合
|
|
159
|
+
selector
|
|
160
|
+
.type("XCUIElementTypeTextField")
|
|
161
|
+
.enabled(true)
|
|
162
|
+
.getOneNodeInfo(3000);
|
|
193
163
|
```
|
|
194
164
|
|
|
195
|
-
|
|
165
|
+
常见类型:`XCUIElementTypeButton`、`StaticText`、`TextField`、`SecureTextField`、`TextView`、`SearchField`、`Cell`、`Table`、`CollectionView`、`PickerWheel`、`Switch`、`Slider` 等。
|
|
196
166
|
|
|
197
|
-
|
|
198
|
-
// 精确匹配类型
|
|
199
|
-
type(type: string): NodeSelector
|
|
167
|
+
---
|
|
200
168
|
|
|
201
|
-
|
|
202
|
-
typeMatch(match: string): NodeSelector
|
|
169
|
+
## 节点操作说明
|
|
203
170
|
|
|
204
|
-
|
|
205
|
-
enabled(enabled: boolean): NodeSelector
|
|
206
|
-
```
|
|
171
|
+
### 是否可点 / 滚到可见
|
|
207
172
|
|
|
208
|
-
|
|
173
|
+
`tap` / `doubleTap` / `press` / `setValue`(以及内部会先 `tap` 的 `clearInput` / `input`)在元素当前不可见时,会**自动**先滚到可见再操作,一般不用手写滚动:
|
|
209
174
|
|
|
210
175
|
```typescript
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
const textFields = selector
|
|
217
|
-
.typeMatch("XCUIElementTypeTextField")
|
|
218
|
-
.getNodeInfo(3000);
|
|
219
|
-
|
|
220
|
-
// 查找启用的元素
|
|
221
|
-
const enabledElements = selector.enabled(true).getNodeInfo(3000);
|
|
222
|
-
|
|
223
|
-
selector.releaseNode();
|
|
176
|
+
// 推荐:直接操作,不可见时会自动滚到可见
|
|
177
|
+
node.tap();
|
|
178
|
+
node.doubleTap();
|
|
179
|
+
node.press(800);
|
|
180
|
+
node.setValue("hello");
|
|
224
181
|
```
|
|
225
182
|
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
// 索引位置
|
|
230
|
-
index(index: number): NodeSelector
|
|
183
|
+
- `hittable()`:当前能否点到(可见且未被挡住),用于排查“点了没反应”
|
|
184
|
+
- `scrollToVisible()`:只滚到可见、不点击;仅在你想先露出元素、稍后再操作时使用
|
|
185
|
+
- `clickCenter()` / `clickRandom()`:按坐标点击,**不会**自动滚动
|
|
231
186
|
|
|
232
|
-
|
|
233
|
-
depth(depth: number): NodeSelector
|
|
187
|
+
### 点击
|
|
234
188
|
|
|
235
|
-
|
|
236
|
-
|
|
189
|
+
| 方法 | 说明 |
|
|
190
|
+
| --- | --- |
|
|
191
|
+
| `tap()` | 推荐,单击;不可见时自动滚到可见 |
|
|
192
|
+
| `doubleTap()` | 双击;不可见时自动滚到可见 |
|
|
193
|
+
| `press(ms?)` | 长按,默认 500ms;不可见时自动滚到可见 |
|
|
194
|
+
| `clickCenter()` | 点中心坐标,不自动滚动 |
|
|
195
|
+
| `clickRandom()` | 范围内随机点,不自动滚动 |
|
|
237
196
|
|
|
238
|
-
|
|
239
|
-
|
|
197
|
+
```typescript
|
|
198
|
+
const btn = selector
|
|
199
|
+
.label("确定")
|
|
200
|
+
.type("XCUIElementTypeButton")
|
|
201
|
+
.getOneNodeInfo(3000);
|
|
240
202
|
|
|
241
|
-
|
|
242
|
-
|
|
203
|
+
if (btn) {
|
|
204
|
+
btn.tap();
|
|
205
|
+
btn.doubleTap();
|
|
206
|
+
btn.press(800);
|
|
207
|
+
}
|
|
243
208
|
```
|
|
244
209
|
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
```typescript
|
|
248
|
-
const selector = createNodeSelector();
|
|
249
|
-
|
|
250
|
-
// 查找第一个元素
|
|
251
|
-
const firstElement = selector.index(0).getOneNodeInfo(2000);
|
|
210
|
+
### 输入与设值
|
|
252
211
|
|
|
253
|
-
|
|
254
|
-
|
|
212
|
+
| 方法 | 说明 |
|
|
213
|
+
| --- | --- |
|
|
214
|
+
| `clearInput()` | 清空(内部先 `tap` 聚焦,不可见时也会自动滚到可见) |
|
|
215
|
+
| `input(text)` | 追加输入,不清空原内容(内部先 `tap` 聚焦) |
|
|
216
|
+
| `setValue(text)` | 按控件类型设置;不可见时会先自动滚到可见 |
|
|
255
217
|
|
|
256
|
-
|
|
257
|
-
const parentElements = selector.childCount(">0").getNodeInfo(3000);
|
|
218
|
+
**`setValue` 行为**
|
|
258
219
|
|
|
259
|
-
|
|
260
|
-
|
|
220
|
+
| 控件类型 | 传参 | 效果 |
|
|
221
|
+
| --- | --- | --- |
|
|
222
|
+
| `PickerWheel` | 选项文案,如 `"1小时"` | 滚到该选项(控件本身不可见时也会先自动滚到可见) |
|
|
223
|
+
| 文本框类(TextField 等) | 字符串 | 聚焦后追加输入,不先清空 |
|
|
261
224
|
|
|
262
|
-
|
|
263
|
-
const xpathElements = selector.xpath("//*[@title='快点JS']").getNodeInfo(3000);
|
|
225
|
+
覆盖原内容请先清空:
|
|
264
226
|
|
|
265
|
-
|
|
227
|
+
```typescript
|
|
228
|
+
field.clearInput();
|
|
229
|
+
field.setValue("hello");
|
|
230
|
+
// 或
|
|
231
|
+
field.clearInput();
|
|
232
|
+
field.input("hello");
|
|
266
233
|
```
|
|
267
234
|
|
|
268
|
-
### 节点操作
|
|
269
|
-
|
|
270
|
-
#### 点击操作
|
|
271
|
-
|
|
272
235
|
```typescript
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
// 节点是否可接收事件(用于判断是否显示在屏幕上)
|
|
280
|
-
node.hittable(): boolean;
|
|
281
|
-
|
|
282
|
-
// 无障碍点击
|
|
283
|
-
node.tap(): boolean;
|
|
284
|
-
|
|
285
|
-
// 无障碍双击
|
|
286
|
-
node.doubleTap(): boolean;
|
|
287
|
-
|
|
288
|
-
// 无障碍长按,duration 单位毫秒
|
|
289
|
-
node.press(duration: number): boolean;
|
|
290
|
-
|
|
291
|
-
// 将节点滚动到可见区域
|
|
292
|
-
node.scrollToVisible(): boolean;
|
|
293
|
-
|
|
294
|
-
// 清空输入
|
|
295
|
-
node.clearInput(): boolean;
|
|
296
|
-
|
|
297
|
-
// 追加输入
|
|
298
|
-
node.input(text: string): boolean;
|
|
299
|
-
|
|
300
|
-
// 设置节点的值
|
|
301
|
-
node.setValue(text: string): boolean;
|
|
236
|
+
const wheel = selector.type("XCUIElementTypePickerWheel").getOneNodeInfo(3000);
|
|
237
|
+
if (wheel) {
|
|
238
|
+
// 不可见时会自动滚到可见,再调到目标选项
|
|
239
|
+
wheel.setValue("1小时");
|
|
240
|
+
}
|
|
302
241
|
```
|
|
303
242
|
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
通过无障碍 API 直接操作节点,不依赖坐标点击。
|
|
307
|
-
|
|
308
|
-
| 方法 | 参数 | 说明 |
|
|
309
|
-
| --- | --- | --- |
|
|
310
|
-
| `tap()` | 无 | 无障碍单击 |
|
|
311
|
-
| `doubleTap()` | 无 | 无障碍双击 |
|
|
312
|
-
| `press(duration)` | `duration`:长按持续时间,单位毫秒 | 无障碍长按 |
|
|
313
|
-
|
|
314
|
-
**示例:**
|
|
243
|
+
### 树导航
|
|
315
244
|
|
|
316
245
|
```typescript
|
|
317
|
-
const
|
|
318
|
-
|
|
319
|
-
const
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
// 无障碍双击
|
|
325
|
-
btn.doubleTap();
|
|
326
|
-
|
|
327
|
-
// 无障碍长按 800ms
|
|
328
|
-
btn.press(800);
|
|
246
|
+
const field = selector.type("XCUIElementTypeTextField").getOneNodeInfo(3000);
|
|
247
|
+
if (field) {
|
|
248
|
+
const parent = field.parent();
|
|
249
|
+
const children = parent?.allChildren() ?? [];
|
|
250
|
+
const prev = field.previousSiblings();
|
|
251
|
+
const next = field.nextSiblings();
|
|
252
|
+
const second = parent?.child(1);
|
|
329
253
|
}
|
|
330
|
-
|
|
331
|
-
selector.releaseNode();
|
|
332
254
|
```
|
|
333
255
|
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
按控件类型自动处理,常用支持如下:
|
|
337
|
-
|
|
338
|
-
| 控件类型 | 传参示例 | 说明 |
|
|
339
|
-
| --- | --- | --- |
|
|
340
|
-
| `XCUIElementTypePickerWheel` | `"1小时"` | 滚轮选到对应选项文案 |
|
|
341
|
-
| `XCUIElementTypeTextField` | `"hello"` | 先聚焦再输入文本 |
|
|
342
|
-
| `XCUIElementTypeSecureTextField` | `"123456"` | 同上 |
|
|
343
|
-
| `XCUIElementTypeTextView` | `"多行内容"` | 同上 |
|
|
344
|
-
| `XCUIElementTypeSearchField` | `"关键词"` | 同上 |
|
|
256
|
+
---
|
|
345
257
|
|
|
346
|
-
|
|
258
|
+
## 示例
|
|
347
259
|
|
|
348
|
-
|
|
260
|
+
### 登录
|
|
349
261
|
|
|
350
262
|
```typescript
|
|
351
263
|
const selector = createNodeSelector();
|
|
352
264
|
|
|
353
|
-
|
|
354
|
-
const
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
265
|
+
try {
|
|
266
|
+
const user = selector.type("XCUIElementTypeTextField").getOneNodeInfo(5000);
|
|
267
|
+
if (user) {
|
|
268
|
+
user.clearInput();
|
|
269
|
+
user.setValue("demo_user");
|
|
270
|
+
}
|
|
358
271
|
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
272
|
+
const pass = selector
|
|
273
|
+
.clearSelector()
|
|
274
|
+
.type("XCUIElementTypeSecureTextField")
|
|
275
|
+
.getOneNodeInfo(3000);
|
|
276
|
+
if (pass) {
|
|
277
|
+
pass.clearInput();
|
|
278
|
+
pass.setValue("password");
|
|
279
|
+
}
|
|
362
280
|
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
281
|
+
const login = selector
|
|
282
|
+
.clearSelector()
|
|
283
|
+
.labelMatch(".*登录.*")
|
|
284
|
+
.type("XCUIElementTypeButton")
|
|
285
|
+
.getOneNodeInfo(3000);
|
|
286
|
+
if (login) {
|
|
287
|
+
login.tap();
|
|
368
288
|
}
|
|
289
|
+
} finally {
|
|
290
|
+
selector.releaseNode();
|
|
369
291
|
}
|
|
292
|
+
```
|
|
370
293
|
|
|
371
|
-
|
|
372
|
-
const field = selector.type("XCUIElementTypeTextField").getOneNodeInfo(3000);
|
|
373
|
-
if (field) {
|
|
374
|
-
field.clearInput();
|
|
375
|
-
field.setValue("hello");
|
|
376
|
-
}
|
|
294
|
+
### 列表里靠后的项
|
|
377
295
|
|
|
378
|
-
|
|
379
|
-
const
|
|
380
|
-
|
|
381
|
-
|
|
296
|
+
```typescript
|
|
297
|
+
const selector = createNodeSelector({ mode: 2 });
|
|
298
|
+
const item = selector.label("第 30 项").getOneNodeInfo(5000);
|
|
299
|
+
if (item) {
|
|
300
|
+
item.tap(); // 不在可见区域时会自动滚动
|
|
382
301
|
}
|
|
383
|
-
|
|
384
|
-
// 随机点击避免检测
|
|
385
|
-
const randomClick = confirmBtn?.clickRandom();
|
|
386
|
-
|
|
387
302
|
selector.releaseNode();
|
|
388
303
|
```
|
|
389
304
|
|
|
390
|
-
|
|
305
|
+
### 查看界面结构
|
|
391
306
|
|
|
392
307
|
```typescript
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
// 获取所有子节点
|
|
400
|
-
node.allChildren(): NodeInfo[]
|
|
401
|
-
|
|
402
|
-
// 获取兄弟节点
|
|
403
|
-
node.siblings(): NodeInfo[]
|
|
404
|
-
|
|
405
|
-
// 获取前面的兄弟节点
|
|
406
|
-
node.previousSiblings(): NodeInfo[]
|
|
407
|
-
|
|
408
|
-
// 获取后面的兄弟节点
|
|
409
|
-
node.nextSiblings(): NodeInfo[]
|
|
308
|
+
const selector = createNodeSelector();
|
|
309
|
+
const xml = selector.xml(8000);
|
|
310
|
+
if (xml) {
|
|
311
|
+
logi(xml);
|
|
312
|
+
}
|
|
313
|
+
selector.releaseNode();
|
|
410
314
|
```
|
|
411
315
|
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
```typescript
|
|
415
|
-
const selector = createNodeSelector();
|
|
316
|
+
---
|
|
416
317
|
|
|
417
|
-
|
|
418
|
-
const textField = selector
|
|
419
|
-
.type("XCUIElementTypeTextField")
|
|
420
|
-
.getOneNodeInfo(3000);
|
|
318
|
+
## 常见问题
|
|
421
319
|
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
if (container) {
|
|
425
|
-
logi(`容器类型: ${container.type}`);
|
|
320
|
+
**找不到节点**
|
|
321
|
+
加大超时;减少条件或改用 `*Match`;换 `mode`;用 `xml()` 看实际文案和类型。
|
|
426
322
|
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
logi(`容器包含 ${siblings.length} 个子元素`);
|
|
323
|
+
**点了没反应**
|
|
324
|
+
先看 `hittable()`;确认点到的是目标控件;可对比 `clickCenter()`。
|
|
430
325
|
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
(node) => node.type === "XCUIElementTypeButton",
|
|
434
|
-
);
|
|
435
|
-
}
|
|
436
|
-
}
|
|
326
|
+
**输入叠在旧文字后面**
|
|
327
|
+
`input` / 文本类 `setValue` 是追加,先 `clearInput()`。
|
|
437
328
|
|
|
438
|
-
|
|
439
|
-
|
|
329
|
+
**滚轮设不中**
|
|
330
|
+
`setValue` 要传界面上显示的选项文案;确认类型是 `PickerWheel`;多列时选对那一列。
|