parseQuery 函数详解
在进行 UI 自动化或界面元素查找时,查询条件的灵活组合尤为重要。本文将介绍一个常用的查询解析函数 parseQuery,它能根据特定的条件生成对应的查询对象,方便在自动化脚本中进行元素定位。
函数功能
parseQuery 函数用于解析查询字符串并根据解析结果生成查询对象。该函数支持多个查询条件,通过 ~ 符号连接,每个条件对应一个属性的匹配规则。支持的属性包括文本、描述、ID、类名、包名、边界等多个选项。
支持的条件格式
函数支持通过查询字符串指定多个条件,每个条件由一个选择器类型和一个匹配值组成,格式为:
多个条件可以用 ~ 分隔,表示节点多属性查找逻辑。例如:
const result = parseQuery("t=微信~i=wechat");
console.log(result);
表示查询文本为 微信 且 ID 为 wechat 的元素。
支持的选择器类型
| 选择器类型 | 说明 |
|---|---|
t | 匹配文本 |
tC | 匹配包含指定文本 |
tS | 匹配以指定文本开头 |
tE | 匹配以指定文本结尾 |
tM | 匹配正则表达式 |
d | 匹配描述 |
dC | 匹配包含指定描述 |
dS | 匹配以指定描述开头 |
dE | 匹配以指定描述结尾 |
dM | 匹配描述的正则表达式 |
i | 匹配ID |
iC | 匹配ID包含指定值 |
iS | 匹配ID以指定值开头 |
iE | 匹配ID以指定值结尾 |
iM | 匹配ID的正则表达式 |
c | 匹配类名 |
cC | 匹配类名包含指定值 |
cS | 匹配类名以指定值开头 |
cE | 匹配类名以指定值结尾 |
cM | 匹配类名的正则表达式 |
p | 匹配包名 |
pC | 匹配包名包含指定值 |
pS | 匹配包名以指定值开头 |
pE | 匹配包名以指定值结尾 |
pM | 匹配包名的正则表达式 |
b | 匹配边界 |
bI | 匹配边界内部元素 |
bC | 匹配边界包含指定元素 |
dO | 匹配绘制顺序 |
函数实现
/**
* 解析单个表达式,支持 `t=微信~i=pay` 的形式进行多条件查询
*
* 该函数接收一个查询字符串,解析字符串中的多个查询条件,并根据条件生成查询对象。
* 查询条件支持对文本、描述、id、类名、包名、边界等多个属性的匹配。
*
* @param {string} str 查询字符串,格式如 "t=微信~i=pay",支持多个条件用 `~` 分隔
* @returns {object} 返回一个查询对象,包含多个查询条件
*
* @example
* const result = parseQuery("t=微信~i=pay");
* console.log(result);
*/
parseQuery = (str) => {
let query = $selector(); // 使用 $selector() 初始化查询对象
let subConditions = str.split("~"); // 多属性 AND 逻辑,按 ~ 分割多个子条件
// 定义一个对象,映射选择器类型到相应的函数
const selectFn = {
t: "text",
tC: "textContains",
tS: "textStartsWith",
tE: "textEndsWith",
tM: "textMatches",
d: "desc",
dC: "descContains",
dS: "descStartsWith",
dE: "descEndsWith",
dM: "descMatches",
i: "id",
iC: "idContains",
iS: "idStartsWith",
iE: "idEndsWith",
iM: "idMatches",
c: "className",
cC: "classNameContains", // 类名包含
cS: "classNameStartsWith",
cE: "classNameEndsWith",
cM: "classNameMatches",
p: "packageName", // 包名匹配
pC: "packageNameContains", // 包名包含
pS: "packageNameStartsWith",
pE: "packageNameEndsWith",
pM: 'packageNameMatches',
b: "bounds",
bI: "boundsInside",
bC: "boundsContains",
dO: "drawingOrder"
};
// 遍历每个子条件
for (let cond of subConditions) {
let [key, value] = cond.split("="); // 按照 '=' 分割条件,获取选择器类型和匹配值
if (!key) continue; // 如果没有选择器类型,跳过当前条件
if (!value) {
// 如果没有给定值,默认为 `text` 类型
value = key
key = "t"
}
let objKey = selectFn[key] || (query[key] ? key : false); // 获取对应的查询方法
if (query[objKey]) {
// 处理 `true` 和 `false` 的值
if (value === 'true') {
value = true
} else if (value === 'false') {
value = false
} else if (objKey.includes("Matches")) {
// 如果是正则匹配类型,处理为正则表达式
if (new RegExp('^/.*\/$').test(value)) {
value = value.slice(1, -1)
}
value = new RegExp(value);
}
if (objKey.includes("bounds")) {
// 处理边界值,期望四个数字(左,上,右,下)
let arr = value.split(",").map(e => Number(e));
if (arr && arr.length === 4) {
query[objKey](arr[0], arr[1], arr[2], arr[3]);
} else {
console.error(objKey + " 参数错误,必须是 4 个数字");
}
} else {
query = query[objKey](value);
}
} else {
console.warn(`未知的选择器类型: ${key}`); // 当遇到无法识别的选择器类型时,给出警告
}
}
// 返回查询对象
return query;
}