获取对象API
获取对象API是那些用于从模型中获取测试对象的API,或者在描述式编程中通过属性生成对象的API,有如下:
类型定义文件
export interface IWinContainer {
/* 获取单个对象 */
getButton(objectName:string, property?: Criteria): IWinButton;
getButton(property: Criteria): IWinButton;
getEdit(objectName:string, property?: Criteria): IWinEdit;
getEdit(property: Criteria): IWinEdit;
// ... //
// ... //
// ... //
/* 获取复数个对象 */
findControls(objectName:string, property?: Criteria): Promise<IWinControl[]>;
findControls(property: Criteria): Promise<IWinControl[]>;
}
控件筛选条件Criteria
类
CukeTest使用Criteria
对象作为条件对当前环境中的控件进行筛选,来获取目标控件并操作。
interface Criteria {
accessKey?: string;
attachedtext?: string;
automationId?: string;
boundingRectangle?: string;
className?: string;
className~?: string;
helptext?: string;
hwnd?: number;
index?: number;
name?: string;
name~?: string;
text?: string;
text~?: string;
value?: string;
value~?: string;
processid?: number;
type?: ControlType
title?: string;
url?: string;
}
class Criteria():
accessKey: str
appName: str
attachedtext: str
automationd: str
className: str
index: int
helptext: str
hwnd: int
name: str
processid: int
type: str
title: str
url: str
从上述控件选项类型的定义可以看出来,既可以传入一个对象名称;也可以传入一个对象,代表识别属性;同时传对象名称和识别属性也是可行的。
在内部,获取对象API对对象名称和识别属性的行为是有区别的:
- 只传入对象名称: 读取模型中的目标测试对象中的识别属性。如果模型文件中不存在则抛出无法在模型文件中找到测试对象的错误
1009:CannotFindTestObjectInModel
。 - 只传入识别属性: 在被测应用中匹配所有满足条件的控件并返回,除
findControls()
方法以外,所有获取对象API都只返回满足条件的首个可见控件。如果没有满足条件的控件则返回null
。 - 同时传入对象名称和识别属性: 会使用该测试对象在被测应用中匹配目标控件并返回,除
findControls()
方法以外,所有获取对象API都只返回满足条件的首个可见控件。如果匹配到控件或没有满足条件的控件则返回null
。
使用模糊匹配(正则匹配)获取控件
观察获取对象使用的Criteria对象
可以发现里面有几个特殊属性,它们以~
作为后缀,同时在Criteria对象
中有同名的属性。
这些带~
后缀的属性代表这些属性允许使用模糊匹配,或者也叫作正则匹配。可以在模型管理器中修改匹配模式。也可以在脚本中使用如下方式进行匹配:
// 全文匹配
await model.getButton({"name": "数字5"}).click();
// 匹配所有name属性包含“数字”的控件
await model.getButton({"name~": "数字"}).click();
// 匹配所有name属性以“5”结尾的控件
await model.getButton({"name~": "数字$"}).click();
# 全文匹配
model.getButton({"name": "数字5"}).click()
# 匹配所有name属性包含“数字”的控件
model.getButton({"name~": "数字"}).click()
# 匹配所有name属性以“5”结尾的控件
model.getButton({"name~": "数字$"}).click()
这些属性都可以在模型管理器里面通过“[复制节点属性]”功能直接生成,不一定需要自己手动构造。
get[ControlType]
CukeTest针对每个控件类型都提供了一个方法,该方法返回这种类型控件的测试对象。例如,按钮控件Button
可以通过getButton
方法获得,列表视图控件List
可以通过getList
方法获得。
这些API对象调用后返回的是另一个子容器对象,意味着可以对这些对象级联调用这些容器API,获得控件树更深层次的对象。
conditions有下面两种类型的参数:
- 对象名称,
string
类型,是模型文件中的某个对象名称。在运行时会从模型中获取对象中的识别属性并用于过滤。 - 识别属性,
Object
类型,包含多个键/值对,这些属性用于过滤,过滤出满足所有条件的对象。如果同时指定了对象名称,那么将会拼接和覆盖(相同字段)模型中的识别属性后进行过滤。
调用方式有如下几种:
默认方式:只传入对象名称
只提供对象名给get[ControlType]
方法,调用格式形如:
await model.getButton("五").click();
model.getButton("五").click()
由于这种调用方式是基于对象模型,因此首先要在模型管理器中侦测添加该控件。
描述式编程:只传入识别属性对象
在描述式编程中,你可以从属性直接构造对象,而不用加载模型,这些属性的可选值参考Criteria类。
const { WinAuto } = require('leanpro.win');
await WinAuto.getWindow({
"className": "ApplicationFrameWindow",
"title": "计算器 "
}).getWindow({
"className": "Windows.UI.Core.CoreWindow",
"title": "计算器"
}).getGeneric({
"type": "Group",
"automationId": "NumberPad",
"name": "数字键盘"
}).getButton({
"automationId": "num5Button"
}).click();
from leanproAuto import Auto
Auto.getWindow({
"className": "ApplicationFrameWindow",
"title": "计算器 "
}).getWindow({
"className": "Windows.UI.Core.CoreWindow",
"title": "计算器"
}).getGeneric({
"type": "Group",
"automationId": "NumberPad",
"name": "数字键盘"
}).getButton({
"automationId": "num5Button"
}).click()
上述例子中,每个调用都是传一个对象参数,这个对象包含了多个键/值对,组合在一起后形成过滤条件寻找对象。
更多关于描述模式开发的内容请参见描述模式.
混合模式:同时传入对象名称与对象属性
在默认方式方式中第一个参数是对象名,而接着传入第二个参数就变为混合模式,第二个参数表示识别属性,可选的值参考Criteria类。
具体用法可参考描述模式
findControls() 方法
findControls()
方法是一个强大且灵活的API,用于在测试中查找满足特定条件的控件集合。该方法提供了两种调用方式,使用户能够根据需要精确地定位和操作应用中的控件。
findControls(objectName:string, property?: Criteria): Promise<IWinControl[]>;
findControls(property: Criteria): Promise<IWinControl[]>;
用户可以选择传入模型对象名称objectName
或筛选条件对象Criteria
。来执行查询。
// 传入模型对象名称
let controls = await model.findControls("Normal");
// 使用筛选条件对象
let controls = await model.getWindow("SimpleStyles").findControls({
"type": "Button",
"className": "Button"
});
此方法返回一个异步的控件数组,其中包含所有满足指定条件的控件。这种实时获取应用中匹配控件的能力特别适用于需要批量处理界面上多个相似控件的情况。
应用场景
场景一: 对一组控件执行集合操作
例如,如果您的应用界面上有多个CheckBox控件,您可能需要将它们全部选中。下面是一个使用CukeTest样例“SimpleStyles”的示例:
要将所有CheckBox标记为选中,首先需要在模型中添加一个包含CheckBox控件的对象:
注意:"Name"属性已从标识属性中删除,以匹配所有CheckBox对象。接下来,使用
findControls
方法,并传入相应的模型对象名称:
(async function() {
let controls = await model.findControls("Normal");
console.log('控件数量', controls.length); // 输出匹配到的控件数量
for (let control of controls) {
console.log(await control.name()); // 打印每个CheckBox的名称
await control.check(); // 选中每个CheckBox
}
})()
在此场景中,通过findControls
方法快速获取所有CheckBox控件,并对它们进行统一操作,如选中或验证,提高了测试脚本的效率和可读性。
all() 方法
all()
方法返回与当前控件的“标识属性”相匹配的所有同级控件数组,它简化了在测试中对同一层级多个控件的批量操作,尤其适用于处理表格、列表或一组按钮等场景。
与findControls()
相比,all()
更适用于无需特定筛选条件就要操作或检查当前控件同级所有控件的情况。
使用示例
假设在一个应用界面中有多个按钮排列在一起,您可能需要验证这些按钮的可用性或者依次进行点击。使用all()
方法,可以轻松获取这一层级的所有按钮控件,并进行遍历操作。
(async function() {
let controls = await model.getButton("Normal").all()
console.log('控件数量', controls.length); // 输出匹配到的控件数量
for(let control of controls) {
console.log(await control.name()); // 打印每个控件的名称
// 执行其他操作...
}
})()
在这个示例中,model.getButton("Normal").all()
调用会返回当前模型下所有按钮控件的数组。然后,通过遍历这个数组,可以实现对每个按钮的文本打印和点击操作。这种方法简化了对同级多个控件的操作流程,提高了测试脚本的效率和可读性。
附录:完整的类型定义
在类型文件中,容器对象的类型定义:
export interface IWinContainer {
findControls(...conditions: ConditionFilter[]): Promise<IWinControl[]>;
getWindow(...conditions: ConditionFilter[]): IWinWindow;
getButton(...conditions: ConditionFilter[]): IWinButton;
getCheckBox(...conditions: ConditionFilter[]): IWinCheckBox;
getComboBox(...conditions: ConditionFilter[]): IWinComboBox;
getCustom(...conditions: ConditionFilter[]): IWinCustom;
getDataItem(...conditions: ConditionFilter[]): IWinControl;
getDataGrid(...conditions: ConditionFilter[]): IWinDataGrid
getDocument(...conditions: ConditionFilter[]): IWinDocument;
getEdit(...conditions: ConditionFilter[]): IWinEdit;
getGeneric(...conditions: ConditionFilter[]): IWinGeneric;
getImage(...conditions: ConditionFilter[]): IWinImage;
getList(...conditions: ConditionFilter[]): IWinList;
getListItem(...conditions: ConditionFilter[]): IWinListItem;
getMenuBar(...conditions: ConditionFilter[]): IWinMenuBar;
getMenuItem(...conditions: ConditionFilter[]): IWinMenuItem
getMenu(...conditions: ConditionFilter[]): IWinMenu;
getPane(...conditions: ConditionFilter[]): IWinPane;
getRadioButton(...conditions: ConditionFilter[]): IWinRadioButton;
getScrollBar(...conditions: ConditionFilter[]): IWinScrollBar;
getSlider(...conditions: ConditionFilter[]): IWinSlider;
getSpinner(...conditions: ConditionFilter[]): IWinSpinner;
getTab(...conditions: ConditionFilter[]): IWinTab;
getTabItem(...conditions: ConditionFilter[]): IWinTabItem;
getTable(...conditions: ConditionFilter[]): IWinTable;
getTree(...conditions: ConditionFilter[]): IWinTree;
getTreeItem(...conditions: ConditionFilter[]): IWinTreeItem;
getText(...conditions: ConditionFilter[]): IWinText;
getVirtual(...conditions: ConditionFilter[]): IWinVirtual
}
export type ConditionFilter = string | Criteria;
interface Criteria {
accessKey?: string;
attachedtext?: string;
automationId?: string;
boundingRectangle?: string;
className?: string;
helptext?: string;
hwnd?: number;
name?: string;
processid?: number;
type?: ControlType
title?: string;
url?: string;
}
class WinContainer():
def getWindow(*conditions: List[Union[str, Dict]]) -> "WinWindow"
def getButton(*conditions: List[Union[str, Dict]]) -> "WinButton"
def getCheckBox(*conditions: List[Union[str, Dict]]) -> "WinCheckBox"
def getComboBox(*conditions: List[Union[str, Dict]]) -> "WinComboBox"
def getCustom(*conditions: List[Union[str, Dict]]) -> "WinCustom"
def getDataItem(*conditions: List[Union[str, Dict]]) -> "WinTableCell"
def getDataGrid(*conditions: List[Union[str, Dict]]) -> "WinDataGrid"
def getDocument(*conditions: List[Union[str, Dict]]) -> "WinDocument"
def getEdit(*conditions: List[Union[str, Dict]]) -> "WinEdit"
def getGeneric(*conditions: List[Union[str, Dict]]) -> "WinGeneric"
def getHyperlink(*conditions: List[Union[str, Dict]]) -> "WinHyperlink"
def getImage(*conditions: List[Union[str, Dict]]) -> "WinImage"
def getList(*conditions: List[Union[str, Dict]]) -> "WinList"
def getListtem(*conditions: List[Union[str, Dict]]) -> "WinListItem"
def getMenuBar(*conditions: List[Union[str, Dict]]) -> "WinMenuBar"
def getMenuItem(*conditions: List[Union[str, Dict]]) -> "WinMenuItem"
def getMenu(*conditions: List[Union[str, Dict]]) -> "WinMenu"
def getPane(*conditions: List[Union[str, Dict]]) -> "WinPane"
def getRadioButton(*conditions: List[Union[str, Dict]]) -> "WinRadioButton"
def getScrollBar(*conditions: List[Union[str, Dict]]) -> "WinScrollBar"
def getSlider(*conditions: List[Union[str, Dict]]) -> "WinSlider"
def getSpinner(*conditions: List[Union[str, Dict]]) -> "WinSpinner"
def getTab(*conditions: List[Union[str, Dict]]) -> "WinTab"
def getTabItem(*conditions: List[Union[str, Dict]]) -> "WinTabItem"
def getTable(*conditions: List[Union[str, Dict]]) -> "WinTable"
def getTableRow(*conditions: List[Union[str, Dict]]) -> "WinTableRow"
def getTableCell(*conditions: List[Union[str, Dict]]) -> "WinTableCell"
def getToolBar(*conditions: List[Union[str, Dict]]) -> "WinToolBar"
def getTree(*conditions: List[Union[str, Dict]]) -> "WinTree"
def getTreeItem(*conditions: List[Union[str, Dict]]) -> "WinTreeItem"
def getTreeCell(*conditions: List[Union[str, Dict]]) -> "WinTreeCell"
def getText(*conditions: List[Union[str, Dict]]) -> "WinText"
def getVirtual(*conditions: List[Union[str, Dict]]) -> "Virtual"
def getPattern(*conditions: List[Union[str, Dict]]) -> "Pattern"
class Criteria():
accessKey: str
appName: str
attachedtext: str
automationd: str
className: str
index: int
helptext: str
hwnd: int
name: str
processid: int
type: str
title: str
url: str
事实上,所有获取对象API其实都有等效的写法:
model.getWindow("NotePad");
// 可以写作
model.getGeneric("NotePad", {type: "Window"})
model.getWindow("NotePad")
# 可以写作
model.getGeneric("NotePad", {type: "Window"})
Window
控件类型的方法。