diff --git a/README.md b/README.md index 0d055ee..6e598c6 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,8 @@ + +# Capture Winium tests + +The Capture specific SystemTest documentation can be found [here.](https://gist.github.com/ole-vegard/05ae46c2d7accd170f9f) +

English description | Описание на русском

diff --git a/Winium/Winium.Mobile.Connectivity/Deployer.cs b/Winium/Winium.Mobile.Connectivity/Deployer.cs index 906a91f..d16eb2b 100644 --- a/Winium/Winium.Mobile.Connectivity/Deployer.cs +++ b/Winium/Winium.Mobile.Connectivity/Deployer.cs @@ -128,6 +128,25 @@ public void SendFiles(List> files) } } + public void InstallDependencies(List dependencies ) + { + if (dependencies == null || !dependencies.Any()) + { + return; + } + + foreach (var dependency in dependencies) + { + InstallDependency(dependency); + } + } + + public void InstallDependency(string path) + { + var appManifest = Utils.ReadAppManifestInfoFromPackage(path); + Utils.InstallApplication(this.deviceInfo, appManifest, DeploymentOptions.None, path); + } + public void Terminate() { throw new NotImplementedException("Deployer.Terminate"); @@ -167,19 +186,6 @@ private IAppManifestInfo InstallApplicationPackage(string path) return appManifest; } - private void InstallDependencies(List dependencies) - { - if (dependencies == null || !dependencies.Any()) - { - return; - } - - foreach (var dependency in dependencies) - { - this.InstallApplicationPackage(dependency); - } - } - #endregion } } diff --git a/Winium/Winium.StoreApps.Driver/Automator/Automator.cs b/Winium/Winium.StoreApps.Driver/Automator/Automator.cs index 63e8d73..c292a4d 100644 --- a/Winium/Winium.StoreApps.Driver/Automator/Automator.cs +++ b/Winium/Winium.StoreApps.Driver/Automator/Automator.cs @@ -244,6 +244,41 @@ private void InitializeDeployer() return new Point(x, y); } + public Rectangle? RequestElementRect(JToken element) + { + var command = new Command( + DriverCommand.GetElementRect, + new Dictionary { { "ID", element } }); + + var responseBody = this.CommandForwarder.ForwardCommand(command); + + var deserializeObject = JsonConvert.DeserializeObject(responseBody); + + if (deserializeObject.Status != ResponseStatus.Success) + { + return null; + } + + var locationObject = deserializeObject.Value as JObject; + if (locationObject == null) + { + return null; + } + + var location = locationObject.ToObject>(); + + if (location == null) + { + return null; + } + + var x = location["x"]; + var y = location["y"]; + var width = location["width"]; + var height = location["height"]; + return new Rectangle(x, y, width, height); + } + #endregion #region Methods diff --git a/Winium/Winium.StoreApps.Driver/CommandExecutors/ClickElementExecutor.cs b/Winium/Winium.StoreApps.Driver/CommandExecutors/ClickElementExecutor.cs index c1885d4..ff42644 100644 --- a/Winium/Winium.StoreApps.Driver/CommandExecutors/ClickElementExecutor.cs +++ b/Winium/Winium.StoreApps.Driver/CommandExecutors/ClickElementExecutor.cs @@ -3,6 +3,7 @@ #region using Winium.StoreApps.Driver.Automator; + using System; #endregion @@ -27,9 +28,12 @@ internal static bool ClickElement(Automator automator, string elementId) protected override string DoImpl() { - ClickElement(this.Automator, this.ExecutedCommand.Parameters["ID"].ToString()); - - return this.JsonResponse(); + try { + return this.Automator.CommandForwarder.ForwardCommand(this.ExecutedCommand); + } catch (Exception e) { + ClickElement(this.Automator, this.ExecutedCommand.Parameters["ID"].ToString()); + return this.JsonResponse(); + } } #endregion diff --git a/Winium/Winium.StoreApps.Driver/CommandExecutors/ExecuteScriptExecutor.cs b/Winium/Winium.StoreApps.Driver/CommandExecutors/ExecuteScriptExecutor.cs index aa3fce7..6816b4b 100644 --- a/Winium/Winium.StoreApps.Driver/CommandExecutors/ExecuteScriptExecutor.cs +++ b/Winium/Winium.StoreApps.Driver/CommandExecutors/ExecuteScriptExecutor.cs @@ -1,4 +1,6 @@ -namespace Winium.StoreApps.Driver.CommandExecutors +using System.Collections.Generic; + +namespace Winium.StoreApps.Driver.CommandExecutors { #region @@ -50,6 +52,17 @@ internal object ExecuteMobileScript(string command) return null; } + internal object ExecuteStorageScript(string command) + { + switch (command) + { + case "ReadLocalTextFile": + return ReadLocalTextFileExecutor.ReadFile(this.Automator, ExecutedCommand); + default: + throw new AutomationException("Unknown storage command: " + command, ResponseStatus.UnknownCommand); + } + } + internal object ForwardCommand() { var responseBody = this.Automator.CommandForwarder.ForwardCommand(this.ExecutedCommand); @@ -87,6 +100,9 @@ protected override string DoImpl() case "mobile:": response = this.ExecuteMobileScript(command); break; + case "storage:": + response = this.ExecuteStorageScript(command); + break; default: response = this.ForwardCommand(); break; diff --git a/Winium/Winium.StoreApps.Driver/CommandExecutors/MouseMoveToExecutor.cs b/Winium/Winium.StoreApps.Driver/CommandExecutors/MouseMoveToExecutor.cs index a21aa13..e93d680 100644 --- a/Winium/Winium.StoreApps.Driver/CommandExecutors/MouseMoveToExecutor.cs +++ b/Winium/Winium.StoreApps.Driver/CommandExecutors/MouseMoveToExecutor.cs @@ -17,16 +17,26 @@ internal class MouseMoveToExecutor : CommandExecutorBase protected override string DoImpl() { var elementId = Automator.GetValue(this.ExecutedCommand.Parameters, "element"); - Point coordinates; - if (elementId != null) + var xOffsetParam = this.ExecutedCommand.Parameters["xoffset"]; + var yOffsetParam = this.ExecutedCommand.Parameters["yoffset"]; + + Point coordinates = new Point(); + if (xOffsetParam != null && yOffsetParam != null) { - coordinates = this.Automator.RequestElementLocation(elementId).GetValueOrDefault(); + var xOffset = Convert.ToInt32(xOffsetParam, CultureInfo.InvariantCulture); + var yOffset = Convert.ToInt32(yOffsetParam, CultureInfo.InvariantCulture); + coordinates = new Point(xOffset, yOffset); + + if (elementId != null) + { + var elementRect = Automator.RequestElementRect(elementId).GetValueOrDefault(); + coordinates.X += elementRect.X; + coordinates.Y += elementRect.Y; + } } - else + else if (elementId != null) { - var xOffset = Convert.ToInt32(this.ExecutedCommand.Parameters["xoffset"], CultureInfo.InvariantCulture); - var yOffset = Convert.ToInt32(this.ExecutedCommand.Parameters["yoffset"], CultureInfo.InvariantCulture); - coordinates = new Point(xOffset, yOffset); + coordinates = this.Automator.RequestElementLocation(elementId).GetValueOrDefault(); } this.Automator.EmulatorController.MoveCursorTo(coordinates); diff --git a/Winium/Winium.StoreApps.Driver/CommandExecutors/ReadLocalTextFileExecutor.cs b/Winium/Winium.StoreApps.Driver/CommandExecutors/ReadLocalTextFileExecutor.cs new file mode 100644 index 0000000..25a6c7a --- /dev/null +++ b/Winium/Winium.StoreApps.Driver/CommandExecutors/ReadLocalTextFileExecutor.cs @@ -0,0 +1,47 @@ +using System.Collections.Generic; +using System.IO; +using System.Threading.Tasks; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using Winium.StoreApps.Common.Exceptions; + +namespace Winium.StoreApps.Driver.CommandExecutors +{ + using System; + using System.Threading; + + using Winium.StoreApps.Common; + using Winium.StoreApps.Driver.Automator; + + internal class ReadLocalTextFileExecutor : CommandExecutorBase + { + #region Public Methods and Operators + + public static string ReadFile(Automator automator, Command command) + { + var args = command.Parameters["args"] as JArray; + if (args == null || args.Count == 0) { + throw new AutomationException("Missing file name", ResponseStatus.UnknownCommand); + } + + var filename = args[0].ToString(); + var filePath = Path.GetTempFileName(); + automator.Deployer.ReceiveFile("Local", filename, filePath); + using (var file = File.OpenText(filePath)) { + return file.ReadToEnd(); + } + } + + #endregion + + #region Methods + + protected override string DoImpl() + { + var fileContent = ReadFile(this.Automator, this.ExecutedCommand); + return this.JsonResponse(ResponseStatus.Success, fileContent); + } + + #endregion + } +} diff --git a/Winium/Winium.StoreApps.Driver/Winium.StoreApps.Driver.csproj b/Winium/Winium.StoreApps.Driver/Winium.StoreApps.Driver.csproj index d847afa..f2b981a 100644 --- a/Winium/Winium.StoreApps.Driver/Winium.StoreApps.Driver.csproj +++ b/Winium/Winium.StoreApps.Driver/Winium.StoreApps.Driver.csproj @@ -56,6 +56,7 @@ + diff --git a/Winium/Winium.StoreApps.InnerServer/Automator.cs b/Winium/Winium.StoreApps.InnerServer/Automator.cs index c784a16..c5a8159 100644 --- a/Winium/Winium.StoreApps.InnerServer/Automator.cs +++ b/Winium/Winium.StoreApps.InnerServer/Automator.cs @@ -157,6 +157,10 @@ public string ProcessCommand(string content) { commandToExecute = new CloseAppCommand(); } + else if (command.Equals(DriverCommand.ClickElement)) + { + commandToExecute = new ClickCommand {ElementId = elementId}; + } else { throw new NotImplementedException("Not implemented: " + command); diff --git a/Winium/Winium.StoreApps.InnerServer/Commands/AlertCommand.cs b/Winium/Winium.StoreApps.InnerServer/Commands/AlertCommand.cs index 8000607..1556cbd 100644 --- a/Winium/Winium.StoreApps.InnerServer/Commands/AlertCommand.cs +++ b/Winium/Winium.StoreApps.InnerServer/Commands/AlertCommand.cs @@ -40,8 +40,14 @@ protected override string DoImpl() { var buttonName = this.Action == With.Accept ? "Button1Host" : "Button2Host"; - var popup = WiniumVirtualRoot.Current.OpenPopups.FirstOrDefault(); - if (popup == null || !popup.ClassName.EndsWith(".ContentDialog")) + WiniumElement popup = null; + foreach (var winiumElement in WiniumVirtualRoot.Current.OpenPopups) { + if (winiumElement.ClassName.EndsWith(".ContentDialog")) { + popup = winiumElement; + break; + } + } + if (popup == null) { throw new AutomationException("No alert is displayed", ResponseStatus.NoAlertOpenError); } diff --git a/Winium/Winium.StoreApps.InnerServer/Commands/ClickCommand.cs b/Winium/Winium.StoreApps.InnerServer/Commands/ClickCommand.cs new file mode 100644 index 0000000..e6dfe68 --- /dev/null +++ b/Winium/Winium.StoreApps.InnerServer/Commands/ClickCommand.cs @@ -0,0 +1,44 @@ +namespace Winium.StoreApps.InnerServer.Commands +{ + #region + + using Windows.UI.Xaml.Automation.Peers; + using Windows.UI.Xaml.Automation.Provider; + using Windows.UI.Xaml.Controls; + using Common; + + #endregion + + internal class ClickCommand : CommandBase + { + #region Public Properties + + public string ElementId { private get; set; } + + #endregion + + #region Public Methods and Operators + + protected override string DoImpl() + { + var element = this.Automator.ElementsRegistry.GetRegisteredElement(this.ElementId); + Button button = element.Element as Button; + if (button != null) + { + ButtonAutomationPeer peer = new ButtonAutomationPeer(button); + IInvokeProvider invokeProv = peer.GetPattern(PatternInterface.Invoke) as IInvokeProvider; + if (invokeProv != null) + { + invokeProv.Invoke(); + return this.JsonResponse(ResponseStatus.Success, ""); + } + + return this.JsonResponse(ResponseStatus.UnknownError, "Failed to create invocation provider : " + this.ElementId); + } + + return this.JsonResponse(ResponseStatus.UnknownError, "Element is not a button" + this.ElementId); + } + + #endregion + } +} \ No newline at end of file diff --git a/Winium/Winium.StoreApps.InnerServer/Commands/CommandBase.cs b/Winium/Winium.StoreApps.InnerServer/Commands/CommandBase.cs index 95f3dac..b2c40e3 100644 --- a/Winium/Winium.StoreApps.InnerServer/Commands/CommandBase.cs +++ b/Winium/Winium.StoreApps.InnerServer/Commands/CommandBase.cs @@ -43,11 +43,11 @@ public string Do() } catch (AutomationException exception) { - response = this.JsonResponse(exception.Status, exception); + response = this.JsonResponse(exception.Status, exception.ToString()); } catch (Exception exception) { - response = this.JsonResponse(ResponseStatus.UnknownError, exception); + response = this.JsonResponse(ResponseStatus.UnknownError, exception.ToString()); } return response; @@ -93,7 +93,7 @@ private static void InvokeSync(CoreDispatcher dispatcher, Action action) if (exception != null) { - throw exception; + throw new Exception("An exception occured while execuiting a command. ", exception); } } diff --git a/Winium/Winium.StoreApps.InnerServer/Commands/GetElementAttributeCommand.cs b/Winium/Winium.StoreApps.InnerServer/Commands/GetElementAttributeCommand.cs index 825cd26..7ffaa66 100644 --- a/Winium/Winium.StoreApps.InnerServer/Commands/GetElementAttributeCommand.cs +++ b/Winium/Winium.StoreApps.InnerServer/Commands/GetElementAttributeCommand.cs @@ -56,6 +56,11 @@ internal static object GetPropertyCascade( return propertyObject; } + if (element.TryGetExtensionProperty(key, out propertyObject)) + { + return propertyObject; + } + return null; } diff --git a/Winium/Winium.StoreApps.InnerServer/Element/Helpers/ExtensionPropertyAccessor.cs b/Winium/Winium.StoreApps.InnerServer/Element/Helpers/ExtensionPropertyAccessor.cs new file mode 100644 index 0000000..e269fd7 --- /dev/null +++ b/Winium/Winium.StoreApps.InnerServer/Element/Helpers/ExtensionPropertyAccessor.cs @@ -0,0 +1,43 @@ +using Winium.StoreApps.InnerServer.Commands.Helpers; + +namespace Winium.StoreApps.InnerServer.Element.Helpers +{ + #region + + using System; + using System.Linq; + using System.Reflection; + + using Newtonsoft.Json.Linq; + + using Windows.UI.Xaml; + + using Winium.StoreApps.Common.Exceptions; + + #endregion + + internal static class ExtensionPropertyAccessor + { + #region Public Methods and Operators + + public static bool TryGetProperty(FrameworkElement element, string propertyName, out object value) + { + value = GetExtensionProperty(propertyName, element); + + return value != null; + } + + #endregion + + #region Methods + + private static object GetExtensionProperty(string propertyName, FrameworkElement element) + { + MethodInfo method = typeof(FrameworkElementExtensions).GetRuntimeMethods().First(m => m.Name.Equals(propertyName)); + object[] parameters = {element }; + return method?.Invoke(null, parameters); + } + + #endregion + } +} \ No newline at end of file diff --git a/Winium/Winium.StoreApps.InnerServer/Element/WiniumElement.GetText.cs b/Winium/Winium.StoreApps.InnerServer/Element/WiniumElement.GetText.cs index d595f1f..5aa50e5 100644 --- a/Winium/Winium.StoreApps.InnerServer/Element/WiniumElement.GetText.cs +++ b/Winium/Winium.StoreApps.InnerServer/Element/WiniumElement.GetText.cs @@ -16,7 +16,7 @@ internal string GetText() { var element = this.Element; - var propertyNames = new List { "Text", "Content" }; + var propertyNames = new List { "Text", "Content", "Label" }; foreach (var textProperty in from propertyName in propertyNames select element.GetType().GetRuntimeProperty(propertyName) diff --git a/Winium/Winium.StoreApps.InnerServer/Element/WiniumElement.IsUserVisible.cs b/Winium/Winium.StoreApps.InnerServer/Element/WiniumElement.IsUserVisible.cs index 145e0e8..7a1605a 100644 --- a/Winium/Winium.StoreApps.InnerServer/Element/WiniumElement.IsUserVisible.cs +++ b/Winium/Winium.StoreApps.InnerServer/Element/WiniumElement.IsUserVisible.cs @@ -5,6 +5,7 @@ using Windows.Foundation; using Windows.UI.Xaml; using Windows.UI.Xaml.Media; + using Windows.UI.Xaml.Controls; #endregion @@ -25,6 +26,12 @@ internal bool IsUserVisible() var currentElementSize = new Size(currentElement.ActualWidth, currentElement.ActualHeight); + if (currentElement is AppBarButton) + return CheckAppBarButtonVisible(currentElement as AppBarButton); + + if (currentElement is CommandBar) + return CheckCommandBarVisible(currentElement as CommandBar); + // Check if currentElement is of zero size if (currentElementSize.Width <= 0 || currentElementSize.Height <= 0) { @@ -46,8 +53,7 @@ internal bool IsUserVisible() while (true) { - if (currentElement.Visibility != Visibility.Visible || !currentElement.IsHitTestVisible - || !(currentElement.Opacity > 0)) + if (currentElement.Visibility != Visibility.Visible || !(currentElement.Opacity > 0)) { return false; } @@ -83,6 +89,40 @@ internal bool IsUserVisible() } } + private CommandBar FindCommandBarIfVisible(AppBarButton element) + { + FrameworkElement parent = element; + + while ( parent != null ) + { + if (parent.Visibility == Visibility.Collapsed ) + return null; + + if (parent.GetType() == typeof(AppBar) || parent.GetType() == typeof(CommandBar)) + return parent as CommandBar; + + parent = VisualTreeHelper.GetParent(parent) as FrameworkElement; + } + + return null; + } + + private bool CheckAppBarButtonVisible(AppBarButton appBarButton ) + { + if ( appBarButton == null) + return false; + + if (appBarButton.IsCompact || !appBarButton.IsEnabled || appBarButton.Visibility != Visibility.Visible ) + return false; + + return CheckCommandBarVisible(FindCommandBarIfVisible(appBarButton)); + } + + private bool CheckCommandBarVisible(CommandBar bar) + { + return ( bar != null && bar.IsEnabled && bar.Visibility == Visibility.Visible ); + } + #endregion } } diff --git a/Winium/Winium.StoreApps.InnerServer/Element/WiniumElement.cs b/Winium/Winium.StoreApps.InnerServer/Element/WiniumElement.cs index f40292c..b49a77d 100644 --- a/Winium/Winium.StoreApps.InnerServer/Element/WiniumElement.cs +++ b/Winium/Winium.StoreApps.InnerServer/Element/WiniumElement.cs @@ -170,6 +170,11 @@ public bool TryGetProperty(string attributeName, out object value) return PropertiesAccessor.TryGetProperty(this.Element, attributeName, out value); } + public bool TryGetExtensionProperty(string attributeName, out object value) + { + return ExtensionPropertyAccessor.TryGetProperty(this.Element, attributeName, out value); + } + #endregion } } diff --git a/Winium/Winium.StoreApps.InnerServer/ElementsRegistry.cs b/Winium/Winium.StoreApps.InnerServer/ElementsRegistry.cs index a2f8669..8db9a7e 100644 --- a/Winium/Winium.StoreApps.InnerServer/ElementsRegistry.cs +++ b/Winium/Winium.StoreApps.InnerServer/ElementsRegistry.cs @@ -64,6 +64,11 @@ public string RegisterElement(WiniumElement element) this.registredElements.Add(registeredKey, element); } + var staleElements = registredElements.Where(x => x.Value.IsStale).ToList(); + foreach (var staleElement in staleElements) { + this.registredElements.Remove(staleElement.Key); + } + return registeredKey; } diff --git a/Winium/Winium.StoreApps.InnerServer/Winium.StoreApps.InnerServer.csproj b/Winium/Winium.StoreApps.InnerServer/Winium.StoreApps.InnerServer.csproj index 117f724..44bed67 100644 --- a/Winium/Winium.StoreApps.InnerServer/Winium.StoreApps.InnerServer.csproj +++ b/Winium/Winium.StoreApps.InnerServer/Winium.StoreApps.InnerServer.csproj @@ -47,6 +47,7 @@ + @@ -73,6 +74,7 @@ +