ms-vite-plugin 1.4.41 → 1.4.43
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/node.md +238 -349
- package/docs/apicn/node.md +241 -366
- package/docs/apipython/node.md +184 -288
- package/package.json +1 -1
package/docs/api/node.md
CHANGED
|
@@ -1,439 +1,328 @@
|
|
|
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()` | 清空输入内容 |
|
|
83
|
+
| `input(text)` | 追加输入(不先清空) |
|
|
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
|
-
|
|
146
|
-
|
|
147
|
-
##### 文本相关
|
|
117
|
+
### 筛选条件
|
|
148
118
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
label(label: string): NodeSelector
|
|
152
|
-
|
|
153
|
-
// 标签模糊匹配
|
|
154
|
-
labelMatch(match: string): NodeSelector
|
|
119
|
+
可链式调用,多个条件同时满足(并且关系)。
|
|
120
|
+
带 `Match` 的为模糊/正则匹配。
|
|
155
121
|
|
|
156
|
-
|
|
157
|
-
title(title: string): NodeSelector
|
|
122
|
+
**文本**
|
|
158
123
|
|
|
159
|
-
|
|
160
|
-
|
|
124
|
+
| 方法 | 说明 |
|
|
125
|
+
| --- | --- |
|
|
126
|
+
| `label` / `labelMatch` | 标签 |
|
|
127
|
+
| `title` / `titleMatch` | 标题 |
|
|
128
|
+
| `identifier` / `identifierMatch` | 标识符 |
|
|
129
|
+
| `value` / `valueMatch` | 值 |
|
|
130
|
+
| `placeholderValue` / `placeholderValueMatch` | 占位文字 |
|
|
161
131
|
|
|
162
|
-
|
|
163
|
-
identifier(identifier: string): NodeSelector
|
|
132
|
+
**类型与状态**
|
|
164
133
|
|
|
165
|
-
|
|
166
|
-
|
|
134
|
+
| 方法 | 说明 |
|
|
135
|
+
| --- | --- |
|
|
136
|
+
| `type` / `typeMatch` | 类型,如 `XCUIElementTypeButton` |
|
|
137
|
+
| `enabled` | 是否启用 |
|
|
167
138
|
|
|
168
|
-
|
|
169
|
-
value(value: string): NodeSelector
|
|
139
|
+
**位置与结构**
|
|
170
140
|
|
|
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
|
-
```
|
|
207
|
-
|
|
208
|
-
**示例:**
|
|
171
|
+
### 是否可点 / 滚到可见
|
|
209
172
|
|
|
210
173
|
```typescript
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
.typeMatch("XCUIElementTypeTextField")
|
|
218
|
-
.getNodeInfo(3000);
|
|
219
|
-
|
|
220
|
-
// 查找启用的元素
|
|
221
|
-
const enabledElements = selector.enabled(true).getNodeInfo(3000);
|
|
222
|
-
|
|
223
|
-
selector.releaseNode();
|
|
174
|
+
if (node.hittable()) {
|
|
175
|
+
node.tap();
|
|
176
|
+
} else {
|
|
177
|
+
node.scrollToVisible();
|
|
178
|
+
node.tap();
|
|
179
|
+
}
|
|
224
180
|
```
|
|
225
181
|
|
|
226
|
-
|
|
182
|
+
- `hittable()`:当前能否点到
|
|
183
|
+
- `scrollToVisible()`:手动滚到可见
|
|
184
|
+
- `tap` / `doubleTap` / `press`:不可见时会自动尝试滚到可见再操作
|
|
227
185
|
|
|
228
|
-
|
|
229
|
-
// 索引位置
|
|
230
|
-
index(index: number): NodeSelector
|
|
231
|
-
|
|
232
|
-
// 层级深度
|
|
233
|
-
depth(depth: number): NodeSelector
|
|
186
|
+
### 点击
|
|
234
187
|
|
|
235
|
-
|
|
236
|
-
|
|
188
|
+
| 方法 | 说明 |
|
|
189
|
+
| --- | --- |
|
|
190
|
+
| `tap()` | 推荐,单击 |
|
|
191
|
+
| `doubleTap()` | 双击 |
|
|
192
|
+
| `press(ms?)` | 长按,默认 500ms |
|
|
193
|
+
| `clickCenter()` | 点中心坐标 |
|
|
194
|
+
| `clickRandom()` | 范围内随机点 |
|
|
237
195
|
|
|
238
|
-
|
|
239
|
-
|
|
196
|
+
```typescript
|
|
197
|
+
const btn = selector
|
|
198
|
+
.label("确定")
|
|
199
|
+
.type("XCUIElementTypeButton")
|
|
200
|
+
.getOneNodeInfo(3000);
|
|
240
201
|
|
|
241
|
-
|
|
242
|
-
|
|
202
|
+
if (btn) {
|
|
203
|
+
btn.tap();
|
|
204
|
+
btn.doubleTap();
|
|
205
|
+
btn.press(800);
|
|
206
|
+
}
|
|
243
207
|
```
|
|
244
208
|
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
```typescript
|
|
248
|
-
const selector = createNodeSelector();
|
|
249
|
-
|
|
250
|
-
// 查找第一个元素
|
|
251
|
-
const firstElement = selector.index(0).getOneNodeInfo(2000);
|
|
209
|
+
### 输入与设值
|
|
252
210
|
|
|
253
|
-
|
|
254
|
-
|
|
211
|
+
| 方法 | 说明 |
|
|
212
|
+
| --- | --- |
|
|
213
|
+
| `clearInput()` | 清空 |
|
|
214
|
+
| `input(text)` | 追加输入,不清空原内容 |
|
|
215
|
+
| `setValue(text)` | 按控件类型设置 |
|
|
255
216
|
|
|
256
|
-
|
|
257
|
-
const parentElements = selector.childCount(">0").getNodeInfo(3000);
|
|
217
|
+
**`setValue` 行为**
|
|
258
218
|
|
|
259
|
-
|
|
260
|
-
|
|
219
|
+
| 控件类型 | 传参 | 效果 |
|
|
220
|
+
| --- | --- | --- |
|
|
221
|
+
| `PickerWheel` | 选项文案,如 `"1小时"` | 滚到该选项 |
|
|
222
|
+
| 文本框类(TextField 等) | 字符串 | 聚焦后追加输入,不先清空 |
|
|
261
223
|
|
|
262
|
-
|
|
263
|
-
const xpathElements = selector.xpath("//*[@title='快点JS']").getNodeInfo(3000);
|
|
224
|
+
覆盖原内容请先清空:
|
|
264
225
|
|
|
265
|
-
|
|
226
|
+
```typescript
|
|
227
|
+
field.clearInput();
|
|
228
|
+
field.setValue("hello");
|
|
229
|
+
// 或
|
|
230
|
+
field.clearInput();
|
|
231
|
+
field.input("hello");
|
|
266
232
|
```
|
|
267
233
|
|
|
268
|
-
### 节点操作
|
|
269
|
-
|
|
270
|
-
#### 点击操作
|
|
271
|
-
|
|
272
234
|
```typescript
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
node.clickRandom(): boolean;
|
|
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;
|
|
235
|
+
const wheel = selector.type("XCUIElementTypePickerWheel").getOneNodeInfo(3000);
|
|
236
|
+
if (wheel) {
|
|
237
|
+
wheel.setValue("1小时");
|
|
238
|
+
}
|
|
302
239
|
```
|
|
303
240
|
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
通过无障碍 API 直接操作节点,不依赖坐标点击。
|
|
307
|
-
|
|
308
|
-
| 方法 | 参数 | 说明 |
|
|
309
|
-
| --- | --- | --- |
|
|
310
|
-
| `tap()` | 无 | 无障碍单击 |
|
|
311
|
-
| `doubleTap()` | 无 | 无障碍双击 |
|
|
312
|
-
| `press(duration)` | `duration`:长按持续时间,单位毫秒 | 无障碍长按 |
|
|
313
|
-
|
|
314
|
-
**示例:**
|
|
241
|
+
### 树导航
|
|
315
242
|
|
|
316
243
|
```typescript
|
|
317
|
-
const
|
|
318
|
-
|
|
319
|
-
const
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
// 无障碍双击
|
|
325
|
-
btn.doubleTap();
|
|
326
|
-
|
|
327
|
-
// 无障碍长按 800ms
|
|
328
|
-
btn.press(800);
|
|
244
|
+
const field = selector.type("XCUIElementTypeTextField").getOneNodeInfo(3000);
|
|
245
|
+
if (field) {
|
|
246
|
+
const parent = field.parent();
|
|
247
|
+
const children = parent?.allChildren() ?? [];
|
|
248
|
+
const prev = field.previousSiblings();
|
|
249
|
+
const next = field.nextSiblings();
|
|
250
|
+
const second = parent?.child(1);
|
|
329
251
|
}
|
|
330
|
-
|
|
331
|
-
selector.releaseNode();
|
|
332
252
|
```
|
|
333
253
|
|
|
334
|
-
|
|
254
|
+
---
|
|
335
255
|
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
| 控件类型 | 传参示例 | 说明 |
|
|
339
|
-
| --- | --- | --- |
|
|
340
|
-
| `XCUIElementTypePickerWheel` | `"1小时"` | 滚轮选到对应选项文案 |
|
|
341
|
-
| `XCUIElementTypeTextField` | `"hello"` | 先聚焦再输入文本 |
|
|
342
|
-
| `XCUIElementTypeSecureTextField` | `"123456"` | 同上 |
|
|
343
|
-
| `XCUIElementTypeTextView` | `"多行内容"` | 同上 |
|
|
344
|
-
| `XCUIElementTypeSearchField` | `"关键词"` | 同上 |
|
|
256
|
+
## 示例
|
|
345
257
|
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
**示例:**
|
|
258
|
+
### 登录
|
|
349
259
|
|
|
350
260
|
```typescript
|
|
351
261
|
const selector = createNodeSelector();
|
|
352
262
|
|
|
353
|
-
|
|
354
|
-
const
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
263
|
+
try {
|
|
264
|
+
const user = selector.type("XCUIElementTypeTextField").getOneNodeInfo(5000);
|
|
265
|
+
if (user) {
|
|
266
|
+
user.clearInput();
|
|
267
|
+
user.setValue("demo_user");
|
|
268
|
+
}
|
|
358
269
|
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
270
|
+
const pass = selector
|
|
271
|
+
.clearSelector()
|
|
272
|
+
.type("XCUIElementTypeSecureTextField")
|
|
273
|
+
.getOneNodeInfo(3000);
|
|
274
|
+
if (pass) {
|
|
275
|
+
pass.clearInput();
|
|
276
|
+
pass.setValue("password");
|
|
277
|
+
}
|
|
362
278
|
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
279
|
+
const login = selector
|
|
280
|
+
.clearSelector()
|
|
281
|
+
.labelMatch(".*登录.*")
|
|
282
|
+
.type("XCUIElementTypeButton")
|
|
283
|
+
.getOneNodeInfo(3000);
|
|
284
|
+
if (login) {
|
|
285
|
+
login.tap();
|
|
368
286
|
}
|
|
287
|
+
} finally {
|
|
288
|
+
selector.releaseNode();
|
|
369
289
|
}
|
|
290
|
+
```
|
|
370
291
|
|
|
371
|
-
|
|
372
|
-
const field = selector.type("XCUIElementTypeTextField").getOneNodeInfo(3000);
|
|
373
|
-
if (field) {
|
|
374
|
-
field.clearInput();
|
|
375
|
-
field.setValue("hello");
|
|
376
|
-
}
|
|
292
|
+
### 列表里靠后的项
|
|
377
293
|
|
|
378
|
-
|
|
379
|
-
const
|
|
380
|
-
|
|
381
|
-
|
|
294
|
+
```typescript
|
|
295
|
+
const selector = createNodeSelector({ mode: 2 });
|
|
296
|
+
const item = selector.label("第 30 项").getOneNodeInfo(5000);
|
|
297
|
+
if (item) {
|
|
298
|
+
item.tap(); // 不在可见区域时会自动滚动
|
|
382
299
|
}
|
|
383
|
-
|
|
384
|
-
// 随机点击避免检测
|
|
385
|
-
const randomClick = confirmBtn?.clickRandom();
|
|
386
|
-
|
|
387
300
|
selector.releaseNode();
|
|
388
301
|
```
|
|
389
302
|
|
|
390
|
-
|
|
303
|
+
### 查看界面结构
|
|
391
304
|
|
|
392
305
|
```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[]
|
|
306
|
+
const selector = createNodeSelector();
|
|
307
|
+
const xml = selector.xml(8000);
|
|
308
|
+
if (xml) {
|
|
309
|
+
logi(xml);
|
|
310
|
+
}
|
|
311
|
+
selector.releaseNode();
|
|
410
312
|
```
|
|
411
313
|
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
```typescript
|
|
415
|
-
const selector = createNodeSelector();
|
|
314
|
+
---
|
|
416
315
|
|
|
417
|
-
|
|
418
|
-
const textField = selector
|
|
419
|
-
.type("XCUIElementTypeTextField")
|
|
420
|
-
.getOneNodeInfo(3000);
|
|
316
|
+
## 常见问题
|
|
421
317
|
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
if (container) {
|
|
425
|
-
logi(`容器类型: ${container.type}`);
|
|
318
|
+
**找不到节点**
|
|
319
|
+
加大超时;减少条件或改用 `*Match`;换 `mode`;用 `xml()` 看实际文案和类型。
|
|
426
320
|
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
logi(`容器包含 ${siblings.length} 个子元素`);
|
|
321
|
+
**点了没反应**
|
|
322
|
+
先看 `hittable()`;确认点到的是目标控件;可对比 `clickCenter()`。
|
|
430
323
|
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
(node) => node.type === "XCUIElementTypeButton",
|
|
434
|
-
);
|
|
435
|
-
}
|
|
436
|
-
}
|
|
324
|
+
**输入叠在旧文字后面**
|
|
325
|
+
`input` / 文本类 `setValue` 是追加,先 `clearInput()`。
|
|
437
326
|
|
|
438
|
-
|
|
439
|
-
|
|
327
|
+
**滚轮设不中**
|
|
328
|
+
`setValue` 要传界面上显示的选项文案;确认类型是 `PickerWheel`;多列时选对那一列。
|