public static void AreEqual(Bitmap expected, UiElement element) { WaitForStartAnimation(element); if (Equal(expected, element, RetryTime)) { return; } using (var actualBmp = Capture.Rectangle(element.Bounds)) { AreEqual(expected, actualBmp); } }
private static void Details(StringBuilder stringBuilder, UiElement uiElement, string displayPadding) { const string indent = " "; WriteDetail(uiElement, stringBuilder, displayPadding); WritePattern(uiElement, stringBuilder, displayPadding); var children = uiElement.CachedChildren; foreach (var child in children) { Details(stringBuilder, child, displayPadding + indent); } }
public static void RealizeItems(UiElement itemContainerElement) { if (itemContainerElement is null) { throw new System.ArgumentNullException(nameof(itemContainerElement)); } // We save the scroll value to restore it afterwards if (itemContainerElement.AutomationElement.TryGetScrollPattern(out var scrollPattern)) { var currHScroll = scrollPattern.Current.HorizontalScrollPercent; var currVScroll = scrollPattern.Current.VerticalScrollPercent; // First we try with the item container pattern and realize each item if (itemContainerElement.AutomationElement.TryGetItemContainerPattern(out var itemContainerPattern)) { // There's the item container pattern so we can go thru all elements and just realize them AutomationElement?currElement = null; while (true) { currElement = itemContainerPattern.FindItemByProperty(currElement, null, null); if (currElement is null) { break; } if (currElement.TryGetVirtualizedItemPattern(out var virtualizedItemPattern)) { virtualizedItemPattern.Realize(); } } } else { // Second we use the scroll pattern to scroll from top to bottom scrollPattern.SetScrollPercent(0, 0); do { scrollPattern.Scroll(ScrollAmount.NoAmount, ScrollAmount.SmallIncrement); }while (scrollPattern.Current.VerticalScrollPercent < 100); } ResetScroll(scrollPattern, currHScroll, currVScroll); return; } // Third we try by using the scrollbar controls itself { // TODO } }
public static bool UntilResponsive(UiElement uiElement, TimeSpan timeout) { if (uiElement is null) { throw new ArgumentNullException(nameof(uiElement)); } if (uiElement.TryGetWindow(out var window)) { return(UntilResponsive(window.NativeWindowHandle, timeout)); } return(false); }
/// <summary> /// Compare Capture.Rectangle(element.Bounds) to the expected image. /// </summary> /// <param name="fileName">Can be a full file name relative filename or the name of a resource.</param> /// <param name="element">The automation element.</param> /// <param name="onFail">Useful for saving the actual image on error for example.</param> public static void AreEqual(string fileName, UiElement element, OnImageAssertFail onFail) { if (fileName is null) { throw new ArgumentNullException(nameof(fileName)); } if (element is null) { throw new ArgumentNullException(nameof(element)); } if (onFail is null) { throw new ArgumentNullException(nameof(onFail)); } if (TryGetStream(fileName, Assembly.GetCallingAssembly(), out var stream)) { using (stream) { using var expected = (Bitmap)Image.FromStream(stream); if (Equal(expected, element, RetryTime)) { return; } using var actual = Capture.Rectangle(element.Bounds); if (Debugger.IsAttached) { ImageDiffWindow.Show(expected, actual); } onFail(expected, actual, fileName); throw ImageMatchException(expected, actual, fileName); } } else { using var actual = Capture.Rectangle(element.Bounds); if (Debugger.IsAttached) { ImageDiffWindow.Show(null, actual); } onFail(null, actual, fileName); throw MissingResourceException(actual, fileName); } }
/// <summary> /// Captures an element and saves it to a file. /// Note that a sleep may be required before if the control is newly loaded. /// </summary> public static void ElementToFile(UiElement element, string filePath) { if (element is null) { throw new ArgumentNullException(nameof(element)); } if (filePath is null) { throw new ArgumentNullException(nameof(filePath)); } using var bmp = Rectangle(element.Bounds); bmp.Save(filePath, ImageFormat.Png); }
public static bool Equal(Bitmap expected, UiElement element, TimeSpan retryTime) { var start = DateTime.Now; while (!Retry.IsTimeouted(start, retryTime)) { using (var actual = Capture.Rectangle(element.Bounds)) { if (Equal(expected, actual)) { return(true); } } Wait.For(Retry.PollInterval); } return(false); }
private static void WaitForStartAnimation(UiElement element) { if (StartAnimation <= TimeSpan.Zero) { return; } var window = element.Window; User32.GetWindowThreadProcessId(window.NativeWindowHandle, out var id); using (var process = Process.GetProcessById((int)id)) { var upTime = DateTime.Now - process.StartTime; if (upTime < StartAnimation) { Wait.For(StartAnimation - upTime); } } }
/// <summary> /// Compare Capture.Rectangle(element.Bounds) to the expected image. /// </summary> /// <param name="fileName">Can be a full file name relative filename or the name of a resource.</param> /// <param name="element">The automation element.</param> public static void AreEqual(string fileName, UiElement element) { WaitForStartAnimation(element); if (TryGetStream(fileName, Assembly.GetCallingAssembly(), out var stream)) { using (stream) { using (var expected = (Bitmap)Image.FromStream(stream)) { if (Equal(expected, element, RetryTime)) { return; } using (var actual = Capture.Rectangle(element.Bounds)) { switch (OnFail) { case OnFail.DoNothing: AreEqual(expected, actual); break; case OnFail.SaveImageToTemp: AreEqual( expected, actual, (bitmap) => bitmap.Save(TempFileName(fileName), GetImageFormat(fileName))); break; default: throw new InvalidOperationException($"Not handling OnFail {OnFail}"); } } } } } else { Capture.ElementToFile(element, TempFileName(fileName)); throw AssertException.Create($"Did not find a file nor resource named {fileName}"); } }
/// <summary> /// Compare Capture.Rectangle(element.Bounds) to the expected image. /// </summary> /// <param name="fileName">Can be a full file name relative filename or the name of a resource.</param> /// <param name="element">The automation element.</param> /// <param name="onFail">Useful for saving the actual image on error for example.</param> public static void AreEqual(string fileName, UiElement element, Action <Exception, Bitmap> onFail) { WaitForStartAnimation(element); if (TryGetStream(fileName, Assembly.GetCallingAssembly(), out var stream)) { using (stream) { using (var expected = (Bitmap)Image.FromStream(stream)) { if (Equal(expected, element, RetryTime)) { return; } using (var actual = Capture.Rectangle(element.Bounds)) { try { AreEqual(expected, actual); } catch (Exception e) { onFail(e, actual); throw; } } } } } else { var exception = AssertException.Create($"Did not find a file nor resource named {fileName}"); using (var actual = Capture.Rectangle(element.Bounds)) { onFail(exception, actual); } throw exception; } }
public static string Details(UiElement uiElement) { try { var stringBuilder = new StringBuilder(); var cr = new CacheRequest { AutomationElementMode = AutomationElementMode.None }; // Add the element properties cr.Add(AutomationElementIdentifiers.AutomationIdProperty); cr.Add(AutomationElementIdentifiers.ControlTypeProperty); cr.Add(AutomationElementIdentifiers.NameProperty); cr.Add(AutomationElementIdentifiers.HelpTextProperty); cr.Add(AutomationElementIdentifiers.BoundingRectangleProperty); cr.Add(AutomationElementIdentifiers.ClassNameProperty); cr.Add(AutomationElementIdentifiers.IsOffscreenProperty); cr.Add(AutomationElementIdentifiers.FrameworkIdProperty); cr.Add(AutomationElementIdentifiers.ProcessIdProperty); // Add the pattern availability properties cr.Add(AutomationElementIdentifiers.IsDockPatternAvailableProperty); cr.Add(AutomationElementIdentifiers.IsExpandCollapsePatternAvailableProperty); cr.Add(AutomationElementIdentifiers.IsGridPatternAvailableProperty); cr.Add(AutomationElementIdentifiers.IsGridItemPatternAvailableProperty); cr.Add(AutomationElementIdentifiers.IsInvokePatternAvailableProperty); cr.Add(AutomationElementIdentifiers.IsItemContainerPatternAvailableProperty); cr.Add(AutomationElementIdentifiers.IsMultipleViewPatternAvailableProperty); cr.Add(AutomationElementIdentifiers.IsRangeValuePatternAvailableProperty); cr.Add(AutomationElementIdentifiers.IsScrollItemPatternAvailableProperty); cr.Add(AutomationElementIdentifiers.IsScrollPatternAvailableProperty); cr.Add(AutomationElementIdentifiers.IsSelectionItemPatternAvailableProperty); cr.Add(AutomationElementIdentifiers.IsSynchronizedInputPatternAvailableProperty); cr.Add(AutomationElementIdentifiers.IsTablePatternAvailableProperty); cr.Add(AutomationElementIdentifiers.IsTableItemPatternAvailableProperty); cr.Add(AutomationElementIdentifiers.IsTextPatternAvailableProperty); cr.Add(AutomationElementIdentifiers.IsTogglePatternAvailableProperty); cr.Add(AutomationElementIdentifiers.IsTransformPatternAvailableProperty); cr.Add(AutomationElementIdentifiers.IsValuePatternAvailableProperty); cr.Add(AutomationElementIdentifiers.IsVirtualizedItemPatternAvailableProperty); cr.Add(AutomationElementIdentifiers.IsWindowPatternAvailableProperty); cr.TreeScope = TreeScope.Subtree; cr.TreeFilter = Condition.TrueCondition; // Activate the cache request using (cr.Activate()) { // Re-find the root element with caching activated uiElement = uiElement.FindFirst(TreeScope.Element, Condition.TrueCondition); Details(stringBuilder, uiElement, string.Empty); } return(stringBuilder.ToString()); } catch (Exception ex) { Console.WriteLine("Failed to dump info: " + ex); return(string.Empty); } }
/// <summary> /// Gets the XPath to the element until the desktop or the given root element. /// Warning: This is quite a heavy operation. /// </summary> public static string GetXPathToElement(UiElement element, UiElement rootElement = null) { return(GetXPathToElement(element.AutomationElement, TreeWalker.ControlViewWalker, rootElement)); }
public static GridViewCell Create(UiElement content) { return(new GridViewCell(content)); }
public AutomationElementXPathNavigator(UiElement rootElement) { this.treeWalker = TreeWalker.ControlViewWalker; this.rootElement = rootElement; this.currentElement = rootElement.AutomationElement; }
private static string GetXPathToElement(AutomationElement element, TreeWalker treeWalker, UiElement rootElement = null) { var parent = treeWalker.GetParent(element); if (parent == null || (rootElement != null && Equals(parent, rootElement.AutomationElement))) { return(string.Empty); } // Get the index var allChildren = parent.FindAllChildren(Conditions.ByControlType(element.Current.ControlType)); var currentItemText = $"{element.Current.ControlType}"; if (allChildren.Count > 1) { // There is more than one matching child, find out the index var indexInParent = 1; // Index starts with 1 foreach (var child in allChildren) { if (child.Equals(element)) { break; } indexInParent++; } currentItemText += $"[{indexInParent}]"; } return($"{GetXPathToElement(parent, treeWalker, rootElement)}/{currentItemText}"); }
/// <summary> /// Captures an element and returns the image. /// Note that a sleep may be required before if the control is newly loaded. /// </summary> public static Bitmap Element(UiElement element) { return(Rectangle(element.Bounds)); }
/// <summary> /// Dump the visual tree of <paramref name="element"/>. /// </summary> /// <param name="element">The <see cref="UiElement"/>.</param> /// <param name="allPropertiesAndPatterns">If all automation properties and patterns should be written to the result.</param> /// <returns>A string representing the automation tree of <paramref name="element"/>.</returns> public static string Recursive(UiElement element, bool allPropertiesAndPatterns = false) { return(Recursive(element.AutomationElement, allPropertiesAndPatterns)); }
/// <summary> /// Finds the first element which is in the given treescope with the given condition within the given timeout period. /// </summary> public bool TryFindAt(TreeScope treeScope, System.Windows.Automation.Condition condition, int index, TimeSpan timeOut, out UiElement result) { result = null; var start = DateTime.Now; while (!Retry.IsTimeouted(start, timeOut)) { result = this.AutomationElement.FindIndexed(treeScope, condition, index, FromAutomationElement); if (result != null) { return(true); } Wait.For(Retry.PollInterval); } return(result != null); }
public static bool UntilResponsive(UiElement uiElement) { return(UntilResponsive(uiElement, DefaultTimeout)); }
public GridViewCell(UiElement content) : base(content?.AutomationElement ?? throw new ArgumentNullException(nameof(content))) { this.Content = content; }
public AutomationElementXPathNavigator(UiElement rootElement) { this.treeWalker = TreeWalker.ControlViewWalker; this.rootElement = rootElement ?? throw new ArgumentNullException(nameof(rootElement)); this.currentElement = rootElement.AutomationElement; }