UIAutomation

[UIAutomation微软从Windows Vista开始推出的一套全新UI自动化测试技术, 简称UIA。在最新的Windows SDK中,UIA和MSAA等其它支持UI自动化技术的组件放在一起发布,叫做Windows Automation API。

  和前面的介绍相比,我倾向于认为UIA是一项自动化测试“技术”,而MSAA和Win32 API只是实现自动化测试的两种“方法”。这里区分“技术”和 “方法”的原因是, 一项“技术”往往有独立的模型,体贴的开发接口,用来专门解决某一类的问题,同时允许不同的实现细节。UIA可以被看作“技术”,是因为:

  UIA定义了全新的、针对UI自动化的接口和模式。 分别是支持对UI元素进行遍历和条件化查询的TreeWalker/FindAll。定义了读写UI元素属性的UIA Property, 包括Name、 ID、Type、ClassName、Location、 Visibility等等。这些模式高度抽象了各种UI自动化测试的需求,同时又不和传统模式相冲突。比如执行点击按钮操作, 传统方法要么模拟鼠标键盘操作,要么发送Windows Message,而Message还分为WM_COMMAND或者WM_BUTTONDOWN。 而通过UIA Pattern, 统一归类于Invoke接口,这个接口对于测试者来说就统一了。无论是Win32、 WPF还是Silverlight按钮,都可以通过统一接口执行, 从而把具体实现隔离开。同时, 调用者若希望继续沿用键盘鼠标模拟,仍旧可以通过SendKey加上UIA获取坐标的方法实现。而UIA Event和对UI元素支持条件化区域化搜索,更是极大简化了测试人员的工作。定义了UI元素行为的UIA Pattern, 比如Select、Expand、Invoke、 Check、Value等等。 还引入了UIA Event接口,可以让测试程序在某些事件发生后得到通知,比如新窗口打开事件等。

  以往的Win32和MSAA 设计出发点并不是为解决UI自动化。Win32旨在提供的通用开发接口, MSAA旨在提供程序的多种访问方式。相反,UIA的设计目的,以及新引入的模式和接口都完全是针对UI自动化测试的。]

Inspect
image-20220223173452256

Option
image-20220223173528712

Active-Hover Toolbar 可以在不进行点击的情况下对工具栏进行操作

三种视图:

Raw View 原始视图:显示未经过筛选的树 i
Control View 控制视图:以显示控制元素为主的树 i
Content View 内容视图:以显示内容元素为主的树 i
四种捕获方式:

Watch Focus 焦点
Watch Caret 插入符
Watch Cursor 鼠标
Watch Tooltips 弹出的提示
image-20220223181241518

Navigation
image-20220223180034159

定位当前元素的关联元素,同工具栏

Action
不同的元素有不同的“操作”

如按钮可以Invoke;

image-20220223184003066

如输入框可以SetValue;

image-20220223183642667

下拉框等使用DoDefaultAction可保持选项常驻页面
UIAutomation
from uia.core.automation import Automation, Element

auto = Automation()

获取根节点元素 https://docs.microsoft.com/zh-cn/windows/win32/api/uiautomationclient/nf-uiautomationclient-iuiautomation-getrootelement

pointer = auto.GetRootElement()

root = Element(point, auto)

UIA Element方法

https://docs.microsoft.com/zh-cn/windows/win32/api/uiautomationclient/nn-uiautomationclient-iuiautomationelement

print(root,
root.programmatic_name,
root.CurrentName,
root.CurrentClassName,
root.CurrentProcessId,
root.CurrentNativeWindowHandle,
root.CurrentLocalizedControlType,
root.CurrentBoundingRectangle.right, sep='\n')

print(pointer.CurrentName, root.CurrentClassName, root.CurrentControlType, ...)

...

Pane

桌面 1

32769

1012

65552

窗格

1920

和Inspect对照

image-20220224171008857

from ctypes.wintypes import tagPOINT

https://docs.microsoft.com/zh-cn/windows/win32/api/uiautomationclient/nf-uiautomationclient-iuiautomation-elementfrompoint

pointer = auto.ElementFromPoint(tagPOINT(x, y))

https://docs.microsoft.com/zh-cn/windows/win32/api/uiautomationclient/nf-uiautomationclient-iuiautomation-elementfromhandle

pointer = auto.ElementFromHandle(hwnd)

https://docs.microsoft.com/zh-cn/windows/win32/api/uiautomationclient/nf-uiautomationclient-iuiautomation-getfocusedelement

pointer = auto.GetFocusedElement()
通过元素查找元素

(先手动打开电脑上的计算器)

calc = root.find_elements_by_selector(':root>Window[Name="计算器"]')[0] # 如果未找到匹配条件元素则返回空列表
calc = root.find_element_by_selector(':root>Window[Name="计算器"]')

原始方法检索与指定条件匹配的第一个元素

https://docs.microsoft.com/zh-cn/windows/win32/api/uiautomationclient/nf-uiautomationclient-iuiautomationelement-findfirst

pointer = element.FindFirst(scope, condition)

原始检索与指定条件匹配的所有元素(返回匹配元素数组的指针。如果没有找到匹配的元素,则返回一个空数组。)

https://docs.microsoft.com/zh-cn/windows/win32/api/uiautomationclient/nf-uiautomationclient-iuiautomationelement-findall#parameters

pointer_array = element.FindAll(scope, condition)

https://docs.microsoft.com/en-us/windows/win32/api/uiautomationclient/nn-uiautomationclient-iuiautomationelementarray

print(pointer_array.Lenght)

pointer_array.GetElement(index)

Scope
UIAutomationClient.TreeScope_Element -> 1
UIAutomationClient.TreeScope_Children -> 2
UIAutomationClient.TreeScope_Element_And_Children -> 3
UIAutomationClient.TreeScope_Descendants -> 4
UIAutomationClient.TreeScope_Element_And_Descendants -> 5
UIAutomationClient.TreeScope_Parent -> 8
UIAutomationClient.TreeScope_Ancestors -> 16
from comtypes.gen import UIAutomationClient

scope = UIAutomationClient.TreeScope_Element
print(scope)
1

Condition

RawViewCondition

auto.RawViewConditon

ControlViewCondition

auto.ControlCondition

ContentViewCondition

auto.ContentViewCondition

ControlType

print(UIAutomationClient.UIA_PaneControlTypeId, hex(UIAutomationClient.UIA_PaneControlTypeId))
50033 0xc371

UIA_AppBarControlTypeId 50040
UIA_ButtonControlTypeId 50000
UIA_CalendarControlTypeId 50001
UIA_CheckBoxControlTypeId 50002
UIA_ComboBoxControlTypeId 50003
UIA_CustomControlTypeId 50025
UIA_DataGridControlTypeId 50028
UIA_DataItemControlTypeId 50029
UIA_DocumentControlTypeId 50030
UIA_EditControlTypeId 50004
UIA_GroupControlTypeId 50026
UIA_HeaderControlTypeId 50034
UIA_HeaderItemControlTypeId 50035
UIA_HyperlinkControlTypeId 50005
UIA_ImageControlTypeId 50006
UIA_ListControlTypeId 50008
UIA_ListItemControlTypeId 50007
UIA_MenuBarControlTypeId 50010
UIA_MenuControlTypeId 50009
UIA_MenuItemControlTypeId 50011
UIA_PaneControlTypeId 50033
UIA_ProgressBarControlTypeId 50012
UIA_RadioButtonControlTypeId 50013
UIA_ScrollBarControlTypeId 50014
UIA_SemanticZoomControlTypeId 50039
UIA_SeparatorControlTypeId 50038
UIA_SliderControlTypeId 50015
UIA_SpinnerControlTypeId 50016
UIA_SplitButtonControlTypeId 50031
UIA_StatusBarControlTypeId 50017
UIA_TabControlTypeId 50018
UIA_TabItemControlTypeId 50019
UIA_TableControlTypeId 50036
UIA_TextControlTypeId 50020
UIA_ThumbControlTypeId 50027
UIA_TitleBarControlTypeId 50037
UIA_ToolBarControlTypeId 50021
UIA_ToolTipControlTypeId 50022
UIA_TreeControlTypeId 50023
UIA_TreeItemControlTypeId 50024
UIA_WindowControlTypeId 50032
UIA_xxxPropertyId ...

元素有很多的属性,不同的属性有各自的标识符

print(UIAutomationClient.UIA_BoundingRectanglePropertyId)

可使用GetcurrentPropertyValue获取该属性值

https://docs.microsoft.com/zh-cn/windows/win32/api/uiautomationclient/nf-uiautomationclient-iuiautomationelement-getcurrentpropertyvalue

print(calc.GetcurrentPropertyValue(30001))
30001

(45.0, 434.0, 513.0, 512.0)

UIA_xxxPatternId ...

不同的元素拥有不同的模式,各模式也有各自的标识符

print(UIAutomationClient.UIA_InvokePatternId)
10000

一旦获得控制模式接口,就可以像使用元素本身一样使用它,方法是直接调用控制模式方法或访问控制模式属性

https://docs.microsoft.com/zh-cn/windows/win32/api/uiautomationclient/nf-uiautomationclient-iuiautomationelement-getcurrentpattern

pattern = calc.GetCurrentPattern(UIAutomationClient.UIA_WindowPatternId) # 控制模式
interface = UIAutomationClient.IUIAutomationWindowPattern # 接口

计算器程窗口能否最小化

print(pattern.QueryInterface(interface).CurrentCanMinimize)

关闭计算器窗口

pattern.QueryInterface(interface).Close()
1

获取输入框文本值

IsValuePatternAvailable: true

先手工打开“运行”

input_box = root.find_element_by_selector(':root>Window[Name="运行"]>ComboBox') # 输入框
pattern = input_box.GetCurrentPattern(UIAutomationClient.UIA_ValuePatternId)
interface = UIAutomationClient.IUIAutomationValuePattern
print(pattern.QueryInterface(interface).CurrentValue)

封装好的方法,,可直接调用控制模式方法或访问控制模式属性

print(input_box.query('ValuePattern').CurrentValue)

calc

设置输入框文本值

pattern.QueryInterface(interface).SetValue('123456')

input_box.query('ValuePattern').SetValue('654321000')

通过键盘输入

input_box.write('456789')

元素点击

IsInvokePatternAvailable: true

confirm_btn = root.find_element_by_selector(':root>Window[Name="运行"]>Button[Name="确定"]') # 确定按钮
pattern = confirm_btn.GetCurrentPattern(UIAutomationClient.UIA_InvokePatternId)
interface = UIAutomationClient.IUIAutomationInvokePattern

“点击”确定按钮

pattern.QueryInterface(interface).Invoke()

封装好的方法

confirm_btn.query('InvokePattern').Invoke()

鼠标移动至元素中心位置,然后进行点击

confirm_btn.click()

聚焦元素

confirm_btn.SetFocus()
获取元素属性

封装好的方法,直接获取属性值

print(confirm_btn.query('NameProperty'))

print(confirm_btn.query('ClassNameProperty'))

print(confim_btn.query('BoundingRectangleProperty'))

print(input_box.query("AccessKeyProperty")) # 输入框快捷键
Alt+o

print(input_box.query('ExpandCollapseExpandCollapseStateProperty'))
0

展开元素

IsExpandCollapsePatternAvailable: true

input_box.query('ExpandCollapsePattern').Expand()
折叠元素

input_box.query('ExpandCollapsePattern').Collapse()
滚动条设置

​ 打开“画图”,使用Inspect捕获垂直滚动条

image-20220225173358845

image-20220225173534937

bar = root.find_element_by_selector(":root>Window[Name='无标题 - 画图'] ScrollBar[Name='垂直滚动条']")

print(bar.query('RangeValuePattern').CurrentValue)
33.0

bar.query('RangeValuePattern').SetValue(100.0)
滚动条自动滚动至最底部。

获取表格行、列数

先手动打开网页 http://rpa-mock.datagrand.com/#/table

table = auto.element_from_point((263, 265))

row_num = table.query('GridPattern').CurrentRowCount

column_num = table.query('GridPattern').CurrentColumnCount

获取表格数据

print(table.query('GridPattern').GetItem(0,0).CurrentName)

result = table.query('GridPattern')

info = []

for row in range(row_num):

single_info = []

for col in range(column_num):

single_info.append(result.GetItem(row, col).CurrentName)

info.append(single_info)

print(info)

通过treewalker查找元素
inspect上基于一个元素,找它的父元素等。

image-20220225104114090

https://docs.microsoft.com/zh-cn/windows/win32/api/uiautomationclient/nn-uiautomationclient-iuiautomationtreewalker

walker = auto.RawViewWalker

ptr = walker.GetFirstElement(root.pElement)
child1 = Element(walker.GetFirstChildElement(ptr, auto)
print(child1)
...

GetParentElement

GetNextSiblingElement

GetPreviousSiblingElement

GetLastChildElement

posted @ 2024-05-27 20:34  中华强  阅读(303)  评论(0)    收藏  举报