微软平台UI自动化(UIA)经验集
2010-05-20 20:30 lixiong 阅读(5546) 评论(3) 编辑 收藏 举报根据我UIA自动化测试的经验, 总结了下面代码集. 在这个代码集中, 包含了:
1. 一个WPF的窗体程序
2. 一个WinForm的窗体, 这个窗体作为Model Dialog被WPF主程序打开
3. 针对这个WPF和WinForm的测试代码例子
4. 针对Win7 Calc.exe的测试代码例子
5. 一个简单的TestEngine
这个代码集的作用是:
1. 演示UIA中基本的概念, 比如AutomationID, AutomatonName, InvokePattern等的调用
2. 演示如何处理UI自动化的timing issue.
3. 演示简单WaitForReady的实现方法
4. 演示Click和Invoke的差别
5. 演示一个简单的UIA Engine
6. 演示如何通过AutomationPeer来给自绘画图案实现Invoke Pattern
7. 演示如何对WinForm实现Server side provider
8. 演示如何对WPF的databinding item设定AutomationID
WPF主窗口代码:
{
private StackPanel pane;
private TextBlock timeBlock;
private Button buttonOpenNewWindow;
private ListBox listboxStringBinding;
private ListBox listboxManual;
private SelfDrawControl selfControl;
private DispatcherTimer tmr=new DispatcherTimer();
private DispatcherTimer deplyedExecution=new DispatcherTimer();
private Button kickoffFlashWindow;
private Button startWinformHoster;
private Button buttonNoAutomationID;
private Button busyButton;
private int busyCount = 0;
public Window1()
{
InitializeComponent();
this.Name = "Window1";
CreateControls();
}
void CreateControls()
{
pane = new StackPanel();
pane.Name = "StackPane";
this.Content = pane;
timeBlock = new TextBlock();
timeBlock.Name = "TimeBlock";
pane.Children.Add(timeBlock);
buttonOpenNewWindow = new Button();
buttonOpenNewWindow.Name = "ButtonOpenNewWindow";
buttonOpenNewWindow.Content = "Open New Window";
pane.Children.Add(buttonOpenNewWindow);
//直接binding的Listbox子元素没有AutomationID
listboxStringBinding = new ListBox();
listboxStringBinding.Name = "ListBox_Item_WithoutAutomationID";
listboxStringBinding.DataContext = new string[] { "1", "2", "3" };
Binding bind = new Binding();
bind.Source = new string[] { "1", "2", "3" };
listboxStringBinding.SetBinding(ListBox.ItemsSourceProperty, bind);
pane.Children.Add(listboxStringBinding);
selfControl = new SelfDrawControl();
selfControl.Name = "SelfDrawControl";
pane.Children.Add(selfControl);
listboxManual = new ListBox();
listboxManual.Name = "ListBox_Item_ManualBindAutomationID";
for(int i=0;i<5;i++)
{
//手动添加的的Listbox子元素可以通过下面的方法指定AutomationID,或者直接指定Name
ListBoxItem item=new ListBoxItem();
item.SetValue(System.Windows.Automation.AutomationProperties.AutomationIdProperty, "ManualItem" + i.ToString());
item.Content =i;
listboxManual.Items.Add(item);
}
pane.Children.Add(listboxManual);
kickoffFlashWindow = new Button();
kickoffFlashWindow.Name = "ButtonOpenFlashWindow";
kickoffFlashWindow.Content = "Kick off Flash Window";
pane.Children.Add(kickoffFlashWindow);
kickoffFlashWindow.Click += new RoutedEventHandler(kickoffFlashWindow_Click);
startWinformHoster = new Button();
startWinformHoster.Name = "ButtonOpenWinForm";
startWinformHoster.Content = "Start Winform Hoster";
pane.Children.Add(startWinformHoster);
//startWinformHoster.Click += delegate{(new WinFormControlHoster()).ShowDialog();};
startWinformHoster.Click += new RoutedEventHandler(startWinformHoster_Click);
tmr.Interval = new TimeSpan(0,0,0,0,500);
tmr.Tick += delegate { this.timeBlock.Text = DateTime.Now.ToString("U"); };
tmr.IsEnabled = true;
buttonOpenNewWindow.Click += new RoutedEventHandler(buttonOpenNewWindow_Click);
buttonNoAutomationID = new Button();
buttonNoAutomationID.Content = "Button Wihtout AutomationID";
pane.Children.Add(buttonNoAutomationID);
busyButton = new Button();
busyButton.Name = "BusyButton";
busyButton.Content = busyCount.ToString();
pane.Children.Add(busyButton);
busyButton.Click += new RoutedEventHandler(busyButton_Click);
deplyedExecution.Interval = new TimeSpan(0, 0, 5);
deplyedExecution.Tick += new EventHandler(deplyedExecution_Tick);
deplyedExecution.IsEnabled = false;
}
void busyButton_Click(object sender, RoutedEventArgs e)
{
busyCount++;
busyButton.Content = busyCount.ToString();
for (int i = 1; i < 10; i++)
{
System.Threading.Thread.Sleep(150);
}
}
void buttonOpenNewWindow_Click(object sender, RoutedEventArgs e)
{
BindingWithAutomationID bwid = new BindingWithAutomationID();
bwid.ShowDialog();
}
void kickoffFlashWindow_Click(object sender, RoutedEventArgs e)
{
deplyedExecution.IsEnabled = true;
}
void deplyedExecution_Tick(object sender, EventArgs e)
{
FlashWindow fw = new FlashWindow(new TimeSpan(0,0,3));
fw.Show();
deplyedExecution.IsEnabled = false;
}
void startWinformHoster_Click(object sender, RoutedEventArgs e)
{
System.Windows.Forms.Form fm = new MyForm();
fm.ShowDialog();
}
}
WPF自绘窗口的代码, 以及对应AutomationPeer的实现:
{
public SelfDrawControl()
{
InitializeComponent();
}
protected override void OnRender(DrawingContext dc)
{
dc.DrawRectangle(Brushes.Blue, new Pen(Brushes.Blue, 10), new Rect(new Point(0, 0), new Point(RenderSize.Width / 2, RenderSize.Height)));
dc.DrawRectangle(Brushes.Black, new Pen(Brushes.Black, 10), new Rect(new Point(RenderSize.Width / 2, 0), new Point(RenderSize.Width, RenderSize.Height)));
}
protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e)
{
Point point = e.GetPosition(this);
if (point.X > 0 && point.X < RenderSize.Width / 2)
{
DoClick("blue");
}
else
{
DoClick("black");
}
base.OnMouseLeftButtonDown(e);
}
internal void DoClick(string color)
{
MessageBox.Show(color);
}
protected override System.Windows.Automation.Peers.AutomationPeer OnCreateAutomationPeer()
{
var peer = new SelfDrawControlAutomationPeer(this);
peer.InvalidatePeer();
return peer;
}
}
public class SelfDrawControlAutomationPeer :UserControlAutomationPeer
{
SelfDrawControl target;
List<AutomationPeer> children = null;
public SelfDrawControlAutomationPeer(SelfDrawControl target):base(target)
{
this.target = target;
}
protected override AutomationControlType GetAutomationControlTypeCore()
{
return AutomationControlType.Window;
}
protected override string GetClassNameCore()
{
return target.GetType().ToString();
}
/*
* 特别注意, 生成ChildrenCore的时候务必维护父子关系
* UI Testclient 可能在不同child parent之间遍历
* 务必保证childpeer.Parent = parent.Children[n]
* 否则会带来各种意外,这是在实现自定义Peer时候的最大陷阱
*/
protected override List<AutomationPeer> GetChildrenCore()
{
//return null;
if (children == null)
{
children = new List<AutomationPeer>();
SelfDrawControlElementAutomationPeer bluepeer = new SelfDrawControlElementAutomationPeer(target, this, "blue");
SelfDrawControlElementAutomationPeer redpeer = new SelfDrawControlElementAutomationPeer(target, this, "red");
children.Add(bluepeer);
children.Add(redpeer);
}
return children;
}
}
public class SelfDrawControlElementAutomationPeer : AutomationPeer, IInvokeProvider, IValueProvider
{
private string color;
SelfDrawControl target;
SelfDrawControlAutomationPeer parentPeer;
public SelfDrawControlElementAutomationPeer(SelfDrawControl target, SelfDrawControlAutomationPeer parentPeer, string color)
{
this.color = color;
this.target = target;
this.parentPeer = parentPeer;
var o=this.GetParent();
}
public void Invoke()
{
target.DoClick(color);
}
public bool IsReadOnly { get { return true; } }
public string Value { get { return color; } }
public void SetValue(string value) { }
protected override string GetAcceleratorKeyCore()
{
return string.Empty;
}
protected override string GetAccessKeyCore()
{
return string.Empty;
}
protected override AutomationControlType GetAutomationControlTypeCore()
{
return AutomationControlType.Window;
}
protected override string GetAutomationIdCore()
{
return target.Name+"-"+color;
}
protected override Rect GetBoundingRectangleCore()
{
Rect parentRect=parentPeer.GetBoundingRectangle();
if(color=="blue")
{
return new Rect(new Point(parentRect.X, parentRect.Y), new Point(parentRect.X + target.RenderSize.Width / 2, parentRect.Y+target.RenderSize.Height));
}
if(color=="red")
{
return new Rect(new Point(parentRect.X + target.RenderSize.Width / 2, parentRect.Y), new Point(parentRect.X + target.RenderSize.Width, parentRect.Y + target.RenderSize.Height));
}
return Rect.Empty;
}
protected override List<AutomationPeer> GetChildrenCore()
{
return null;
}
protected override string GetClassNameCore()
{
return "SelfDrawControlInnerGraphic";
}
protected override Point GetClickablePointCore()
{
return new Point(0,0);
}
protected override string GetHelpTextCore()
{
return "This is my HelpText";
}
protected override string GetItemStatusCore()
{
return "Status is active";
}
protected override string GetItemTypeCore()
{
return "This is my item type";
}
protected override AutomationPeer GetLabeledByCore()
{
return this;
}
protected override string GetNameCore()
{
return "Name is :"+color;
}
protected override AutomationOrientation GetOrientationCore()
{
return AutomationOrientation.None;
}
public override object GetPattern(PatternInterface patternInterface)
{
if(patternInterface== PatternInterface.Invoke || patternInterface== PatternInterface.Value)
{
return this;
}
return null;
}
protected override bool HasKeyboardFocusCore()
{
return false;
}
protected override bool IsContentElementCore()
{
return true;
}
protected override bool IsControlElementCore()
{
return true;
}
protected override bool IsEnabledCore()
{
return true;
}
protected override bool IsKeyboardFocusableCore()
{
return false;
}
protected override bool IsOffscreenCore()
{
return false;
}
protected override bool IsPasswordCore()
{
return false;
}
protected override bool IsRequiredForFormCore()
{
return false;
}
protected override void SetFocusCore()
{
}
}
BindingWindow的XAML和代码:
<ListBox Name="InnerListbox" >
<ListBox.Resources>
<Style TargetType="{x:Type ListBoxItem}">
<Setter Property="AutomationProperties.AutomationId" Value="{Binding RelativeSource={x:Static RelativeSource.Self}, Path=Content}" ></Setter>
</Style>
</ListBox.Resources>
</ListBox>
{
public BindingWithAutomationID()
{
InitializeComponent();
this.Name = "StyleBindingWindow";
//可以使用Style的Setter来给binding的子元素增加AutomationID
this.InnerListbox.DataContext = new string[] { "1", "2", "3" };
Binding bind = new Binding();
bind.Source = new string[] { "1", "2", "3" };
this.InnerListbox.SetBinding(ListBox.ItemsSourceProperty, bind);
}
}
WinForm的代码及其Server side provider实现:
public class MyForm : System.Windows.Forms.Form, IRawElementProviderSimple
{
private ValuePattern vp = new ValuePattern();
private Timer timer = new Timer();
private string curentNameProperty = string.Empty;
public MyForm()
{
timer.Interval = 500;
timer.Tick += new EventHandler(timer_Tick);
timer.Enabled = true;
this.Name = "WinFormWindow";
this.Text = "ServerUIAFormDemo";
Button btn = new Button();
btn.Text = "This is a WinForm Button";
btn.Name = "button1";
this.Controls.Add(btn);
curentNameProperty = string.Format("{0} {1}", this.Name, DateTime.Now.ToLongTimeString());
}
void timer_Tick(object sender, EventArgs e)
{
string newNameProperty=string.Format("{0} {1}", this.Name, DateTime.Now.ToLongTimeString());
AutomationInteropProvider.RaiseAutomationPropertyChangedEvent(this, new AutomationPropertyChangedEventArgs(AutomationElement.NameProperty, curentNameProperty, newNameProperty));
curentNameProperty = newNameProperty;
}
protected override void WndProc(ref Message m)
{
const int WM_GETOBJECT = 0x003D;
if ((m.Msg == WM_GETOBJECT) && (m.LParam.ToInt32() ==
AutomationInteropProvider.RootObjectId))
{
m.Result = AutomationInteropProvider.ReturnRawElementProvider(
this.Handle, m.WParam, m.LParam,
(IRawElementProviderSimple)this);
return;
}
base.WndProc(ref m);
}
public Object GetPatternProvider(int patternId)
{
if (patternId == ValuePatternIdentifiers.Pattern.Id)
{
//Create and return ValuePattern object
return vp;
}
else
{
return null;
}
}
//This function handles all the UIA Property reqeust
public Object GetPropertyValue(int propertyId)
{
if (propertyId == AutomationElementIdentifiers.NameProperty.Id)
{
return curentNameProperty;
}
else if (propertyId == AutomationElementIdentifiers.NativeWindowHandleProperty.Id)
{
return this.Handle;
}
else if (propertyId == AutomationElementIdentifiers.AutomationIdProperty.Id)
{
return this.Name;
}
else if (propertyId == AutomationElementIdentifiers.ClassNameProperty.Id)
{
return "RootButtonControlClass";
}
else if (propertyId == AutomationElementIdentifiers.ControlTypeProperty.Id)
{
return ControlType.Window.Id;
}
else if (propertyId == AutomationElementIdentifiers.IsContentElementProperty.Id)
{
return false;
}
else if (propertyId == AutomationElementIdentifiers.IsControlElementProperty.Id)
{
return true;
}
else
{
return AutomationElement.NotSupported;
}
}
public IRawElementProviderSimple HostRawElementProvider
{
get
{
return AutomationInteropProvider.HostProviderFromHandle(this.Handle);
}
}
public ProviderOptions ProviderOptions
{
get
{
//Indicate this is server side implementation
return ProviderOptions.ServerSideProvider;
}
}
}
[ComVisible(true)]
public class ValuePattern : IValueProvider
{
public bool IsReadOnly { get { return true; } }
public string Value
{
get
{
//Return current time as value pattern’s value
return DateTime.Now.ToLongTimeString();
}
}
public void SetValue(string value) { return; }
}
下面是测试程序:
测试演示1: 演示UIA API里面的cached property:
{
public static void Test()
{
CacheRequest cacheRequest = new CacheRequest();
cacheRequest.TreeScope = TreeScope.Element;
cacheRequest.Add(AutomationElement.NameProperty);
using (cacheRequest.Activate())
{
AutomationElement wpfRoot = AutomationElement.RootElement.FindFirst(TreeScope.Children, new PropertyCondition(AutomationElement.AutomationIdProperty, "Window1"));
AutomationElement textBlock1 = wpfRoot.FindFirst(TreeScope.Children, new PropertyCondition(AutomationElement.AutomationIdProperty, "TimeBlock"));
AutomationElement textBlock2 = wpfRoot.FindFirst(TreeScope.Children, new PropertyCondition(AutomationElement.AutomationIdProperty, "TimeBlock"));
for (int i = 0; i < 100; i++)
{
string cachedName = textBlock1.Cached.Name;
string uncachedName = textBlock2.Current.Name;
Console.WriteLine("=================");
Console.WriteLine("Cached Name is {0}", cachedName);
Console.WriteLine("UnCached Name is {0}", uncachedName);
System.Threading.Thread.Sleep(300);
}
}
Console.WriteLine("Test finishes...................");
}
}
测试演示2: timing issue导致的问题和三种应对方法: Sleep/Polling/Event:
{
static DateTime eventStartTime;
public static void NoUISyncLeadToErrorDemo()
{
CacheRequest cacheRequest = new CacheRequest();
cacheRequest.TreeScope = TreeScope.Element;
cacheRequest.Add(AutomationElement.NameProperty);
using (cacheRequest.Activate())
{
AutomationElement wpfRoot = AutomationElement.RootElement.FindFirst(TreeScope.Children, new PropertyCondition(AutomationElement.AutomationIdProperty, "Window1"));
AutomationElement btnOpenNewWindow = wpfRoot.FindFirst(TreeScope.Children, new PropertyCondition(AutomationElement.AutomationIdProperty, "ButtonOpenFlashWindow"));
InvokePattern invokPtn = (InvokePattern)btnOpenNewWindow.GetCurrentPattern(InvokePattern.Pattern);
invokPtn.Invoke();
Console.WriteLine("Button Clicked...");
AutomationElement newWindow = AutomationElement.RootElement.FindFirst(TreeScope.Children, new PropertyCondition(AutomationElement.AutomationIdProperty, "FlashWindow"));
Console.WriteLine(newWindow.Current.Name);
}
Console.WriteLine("Test finishes...................");
}
public static void SimpleSleepSyncDemo()
{
DateTime startTime = DateTime.Now;
CacheRequest cacheRequest = new CacheRequest();
cacheRequest.TreeScope = TreeScope.Element;
cacheRequest.Add(AutomationElement.NameProperty);
using (cacheRequest.Activate())
{
AutomationElement wpfRoot = AutomationElement.RootElement.FindFirst(TreeScope.Children, new PropertyCondition(AutomationElement.AutomationIdProperty, "Window1"));
AutomationElement btnOpenNewWindow = wpfRoot.FindFirst(TreeScope.Children, new PropertyCondition(AutomationElement.AutomationIdProperty, "ButtonOpenFlashWindow"));
InvokePattern invokPtn = (InvokePattern)btnOpenNewWindow.GetCurrentPattern(InvokePattern.Pattern);
invokPtn.Invoke();
Console.WriteLine("Button Clicked...");
Console.WriteLine("Sleeping 6 seconds to wait...");
System.Threading.Thread.Sleep(1000 * 6);
AutomationElement newWindow = AutomationElement.RootElement.FindFirst(TreeScope.Children, new PropertyCondition(AutomationElement.AutomationIdProperty, "FlashWindow"));
Console.WriteLine(newWindow.Current.Name);
}
Console.WriteLine("Test finishes...................");
Console.WriteLine("Test cost {0} seconds", (DateTime.Now - startTime).TotalSeconds);
}
public static void PollingSyncDemo()
{
DateTime startTime = DateTime.Now;
CacheRequest cacheRequest = new CacheRequest();
cacheRequest.TreeScope = TreeScope.Element;
cacheRequest.Add(AutomationElement.NameProperty);
using (cacheRequest.Activate())
{
AutomationElement wpfRoot = AutomationElement.RootElement.FindFirst(TreeScope.Children, new PropertyCondition(AutomationElement.AutomationIdProperty, "Window1"));
AutomationElement btnOpenNewWindow = wpfRoot.FindFirst(TreeScope.Children, new PropertyCondition(AutomationElement.AutomationIdProperty, "ButtonOpenFlashWindow"));
InvokePattern invokPtn = (InvokePattern)btnOpenNewWindow.GetCurrentPattern(InvokePattern.Pattern);
invokPtn.Invoke();
Console.WriteLine("Button Clicked...");
int timeout = 10 * 1000;
while (true)
{
AutomationElement newWindow = AutomationElement.RootElement.FindFirst(TreeScope.Children, new PropertyCondition(AutomationElement.AutomationIdProperty, "FlashWindow"));
if (newWindow == null)
{
Console.WriteLine("Cannot find expected window. Sleep for a short time");
System.Threading.Thread.Sleep(500);
timeout -= 300;
if (timeout <= 0)
{
throw new TimeoutException("Window probing process times out after 10 seconds.");
}
}
else
{
Console.WriteLine(newWindow.Current.Name);
break;
}
}
}
Console.WriteLine("Test finishes...................");
Console.WriteLine("Test cost {0} seconds", (DateTime.Now - startTime).TotalSeconds);
}
public static void EventSyncDemo()
{
eventStartTime = DateTime.Now;
CacheRequest cacheRequest = new CacheRequest();
cacheRequest.TreeScope = TreeScope.Element;
cacheRequest.Add(AutomationElement.NameProperty);
using (cacheRequest.Activate())
{
AutomationElement wpfRoot = AutomationElement.RootElement.FindFirst(TreeScope.Children, new PropertyCondition(AutomationElement.AutomationIdProperty, "Window1"));
AutomationElement btnOpenNewWindow = wpfRoot.FindFirst(TreeScope.Children, new PropertyCondition(AutomationElement.AutomationIdProperty, "ButtonOpenFlashWindow"));
InvokePattern invokPtn = (InvokePattern)btnOpenNewWindow.GetCurrentPattern(InvokePattern.Pattern);
Console.WriteLine("Register Event");
AutomationEventHandler eventHandler = new AutomationEventHandler(OnWindowOpenOrClose);
Automation.AddAutomationEventHandler(WindowPattern.WindowOpenedEvent, AutomationElement.RootElement, TreeScope.Children, eventHandler);
Automation.AddAutomationEventHandler(WindowPattern.WindowClosedEvent, AutomationElement.RootElement, TreeScope.Subtree, eventHandler);
invokPtn.Invoke();
Console.WriteLine("Button Clicked and Wait...");
Console.ReadLine();
}
}
static void OnWindowOpenOrClose(object src, AutomationEventArgs e)
{
Console.WriteLine("OnWindowOpenOrClose event triggers");
if (e.EventId != WindowPattern.WindowOpenedEvent)
{
Console.WriteLine("It is NOT WindowOpenedEvent.Ignore");
return;
}
AutomationElement sourceElement;
try
{
sourceElement = src as AutomationElement;
if (sourceElement.Current.AutomationId == "FlashWindow")
{
Console.WriteLine(sourceElement.Current.Name);
}
}
catch (ElementNotAvailableException)
{
return;
}
Console.WriteLine("Test finishes...................");
Console.WriteLine("Test cost {0} seconds", (DateTime.Now - eventStartTime).TotalSeconds);
}
}
测试演示3: 通过Waiter Pattern来简化timing issue的处理:
{
private AutomationEvent _eventId;
private AutomationElement _element;
private TreeScope _scope;
private System.Threading.AutoResetEvent _event;
private Condition _condition;
private AutomationEventHandler _eventHandler;
public SimpleWaiter(AutomationEvent eventId, AutomationElement element, TreeScope scope, Condition condition)
{
this._eventId = eventId;
this._element = element;
this._scope = scope;
this._condition = condition;
_event = new System.Threading.AutoResetEvent(false);
_eventHandler = new AutomationEventHandler(Handler);
Automation.AddAutomationEventHandler(_eventId, _element, _scope, _eventHandler);
}
void Handler(object src, AutomationEventArgs e)
{
AutomationElement sourceElement;
sourceElement = src as AutomationElement;
var finditem = sourceElement.FindFirst(TreeScope.Element, _condition);
if (finditem != null && finditem.Equals(sourceElement))
{
_event.Set();
Automation.RemoveAutomationEventHandler(_eventId, _element, _eventHandler);
}
}
public void Wait(int timeOut)
{
_event.WaitOne(timeOut);
}
public static void WaiterDemo()
{
DateTime startTime = DateTime.Now;
CacheRequest cacheRequest = new CacheRequest();
cacheRequest.TreeScope = TreeScope.Element;
cacheRequest.Add(AutomationElement.NameProperty);
using (cacheRequest.Activate())
{
AutomationElement wpfRoot = AutomationElement.RootElement.FindFirst(TreeScope.Children, new PropertyCondition(AutomationElement.AutomationIdProperty, "Window1"));
AutomationElement btnOpenNewWindow = wpfRoot.FindFirst(TreeScope.Children, new PropertyCondition(AutomationElement.AutomationIdProperty, "ButtonOpenFlashWindow"));
InvokePattern invokPtn = (InvokePattern)btnOpenNewWindow.GetCurrentPattern(InvokePattern.Pattern);
SimpleWaiter waiter = new SimpleWaiter(WindowPattern.WindowOpenedEvent, AutomationElement.RootElement, TreeScope.Children, new PropertyCondition(AutomationElement.AutomationIdProperty, "FlashWindow"));
invokPtn.Invoke();
Console.WriteLine("Button Clicked and Wait...");
waiter.Wait(1000 * 1000);
AutomationElement newWindow = AutomationElement.RootElement.FindFirst(TreeScope.Children, new PropertyCondition(AutomationElement.AutomationIdProperty, "FlashWindow"));
Console.WriteLine(newWindow.Current.Name);
Console.WriteLine("Test finishes...................");
Console.WriteLine("Test cost {0} seconds", (DateTime.Now - startTime).TotalSeconds);
}
}
}
测试演示3: 如何模拟真实的鼠标click, 以及如何确保测试目标位于前台:
public struct HARDWAREINPUT
{
public uint msg;
public ushort paramL;
public ushort paramH;
}
[StructLayout(LayoutKind.Sequential)]
public struct KEYBDINPUT
{
public ushort virtualKeyCode;
public ushort scanCode;
public uint flags;
public uint time;
public IntPtr extraInfo;
}
[StructLayout(LayoutKind.Sequential)]
public struct MOUSEINPUT
{
public int dx;
public int dy;
public uint mouseData;
public uint flags;
public uint time;
public IntPtr extraInfo;
}
[StructLayout(LayoutKind.Explicit)]
public struct InputTypeUnion
{
// Fields
[FieldOffset(0)]
public HARDWAREINPUT hi;
[FieldOffset(0)]
public KEYBDINPUT ki;
[FieldOffset(0)]
public MOUSEINPUT mi;
}
[StructLayout(LayoutKind.Sequential)]
public struct INPUT
{
public uint type;
public InputTypeUnion data;
}
class Clicker
{
[DllImport("user32.dll")]
static extern uint SendInput(uint inputCount, ref INPUT inputs, int inputSize);
static public void Click(Point point)
{
MovePointTo (point.X,point.Y);
Click();
}
static public Point GetClickPoint(AutomationElement ele)
{
Point clickablePt;
if(ele.TryGetClickablePoint(out clickablePt))
{
return clickablePt;
}
var boundingRect = ele.Current.BoundingRectangle;
return new Point(boundingRect.X + (boundingRect.Width / 2), boundingRect.Y + (boundingRect.Height / 2));
}
static void MovePointTo(double absX, double absY)
{
INPUT input = new INPUT();
var virtualScreen = System.Windows.Forms.SystemInformation.VirtualScreen;
absX = (((absX - virtualScreen.X) + 0.5) * 65536.0) / ((double)virtualScreen.Width);
absY = (((absY - virtualScreen.Y) + 0.5) * 65536.0) / ((double)virtualScreen.Height);
input.type = 0;
input.data.mi.dx = (int)absX;
input.data.mi.dy = (int)absY;
input.data.mi.flags = 0xc001;
SendInput(1, ref input, Marshal.SizeOf(input));
}
static void MouseDown()
{
INPUT input = new INPUT();
input.type = 0;
input.data.mi.flags = 2; //DOWN
input.data.mi.mouseData |= 1;
SendInput(1, ref input, Marshal.SizeOf(input));
}
static void MouseUp()
{
INPUT input = new INPUT();
input.type = 0;
input.data.mi.flags = 4; //UP
input.data.mi.mouseData |= 1;
SendInput(1, ref input, Marshal.SizeOf(input));
}
static void Click()
{
MouseDown();
MouseUp();
}
}
{
DateTime startTime = DateTime.Now;
CacheRequest cacheRequest = new CacheRequest();
cacheRequest.TreeScope = TreeScope.Element;
cacheRequest.Add(AutomationElement.NameProperty);
using (cacheRequest.Activate())
{
AutomationElement wpfRoot = AutomationElement.RootElement.FindFirst(TreeScope.Children, new PropertyCondition(AutomationElement.AutomationIdProperty, "Window1"));
WindowPattern wndptn = (WindowPattern)wpfRoot.GetCurrentPattern(WindowPattern.Pattern);
var currentStates = wndptn.Current.WindowVisualState;
if (currentStates != WindowVisualState.Minimized)
{
wndptn.SetWindowVisualState(currentStates);
}
else
{
wndptn.SetWindowVisualState(WindowVisualState.Normal);
}
AutomationElement btnOpenNewWindow = wpfRoot.FindFirst(TreeScope.Children, new PropertyCondition(AutomationElement.AutomationIdProperty, "ButtonOpenFlashWindow"));
InvokePattern invokPtn = (InvokePattern)btnOpenNewWindow.GetCurrentPattern(InvokePattern.Pattern);
SimpleWaiter waiter = new SimpleWaiter(WindowPattern.WindowOpenedEvent, AutomationElement.RootElement, TreeScope.Children, new PropertyCondition(AutomationElement.AutomationIdProperty, "FlashWindow"));
var poi = Clicker.GetClickPoint(btnOpenNewWindow);
Clicker.Click(poi);
//invokPtn.Invoke();
Console.WriteLine("Button Clicked and Wait...");
waiter.Wait(1000 * 1000);
AutomationElement newWindow = AutomationElement.RootElement.FindFirst(TreeScope.Children, new PropertyCondition(AutomationElement.AutomationIdProperty, "FlashWindow"));
Console.WriteLine(newWindow.Current.Name);
Console.WriteLine("Test finishes...................");
Console.WriteLine("Test cost {0} seconds", (DateTime.Now - startTime).TotalSeconds);
}
}
测试演示4,5演示WaitForReady以及简单的Engine实现, 代码比较多, 就请自己下载吧.
https://files.cnblogs.com/lixiong/UIAutoDemo.zip