Add more UWP automated testing capabilities (#1252)
* Implement Clear(marked) method * For Windows tests where we need to query a value we can't get, mark inconclusive * Fix queries for multi-line "marked" values * Force frames with text in them to be "tappable" * Actually rerun queries during retry * Update images for package deployment * Allow the tests to restart the Control Gallery if it crashes * UWP tests can now activate context menus * Make double-tap (really double click) work for UWP desktop * Get some basic scroll up/down functions working * ScrollTo functions * Modify ListViewRenderer automation peer to prevent freezing on ListViews * Allow automation to find tabs * Temporarily ignore some of the tests which don't do much * Make ListViews with string/value type lists work * Add note about 29257/60478 * Use toggle button for test 30353 for UWP * Handle getting screen bounds consistently * Make test for G2414 use ActivateContextMenu extension method * Simplify UI test for 31330 and make it runnable on Windows * Add notes on failing tests * Add query for MoreButton on G2809 test * Ignore ActivityIndicator IsRunning test for UWP * Use ScrollDownTo instead of ScrollForElement method on Windows * CellsGalleryTestCellList now working on UWP * Cells tests working on UWP * Re-add Tap to ScrollAndTap * Get rid of custom automation peer stuff and just fix the tests * Viewport caching and multi-monitor support for scroll * Modified scroll values to hopefully get this running correctly on high density screen * Clear messages so Appearing tests don't freeze up automation on UWP * Make test for 32230 compatible with UWP * Make test 32615 compatible with UWP * Use ActivateContextMenu to simplify 34561 test * Add notes for 34912 failure * Make 36171 test compatible with UWP tests Add directions for running the tests locally * PR cleanup
|
@ -43,6 +43,8 @@ Xamarin.Forms.UITest.Validator/UITestCoverage/index.html
|
|||
!Xamarin.Forms.UITest.TestCloud/test-cloud.exe
|
||||
Xamarin.Forms.ControlGallery.Windows/AppPackages/
|
||||
Xamarin.Forms.ControlGallery.WindowsPhone/AppPackages/
|
||||
Xamarin.Forms.ControlGallery.WindowsUniversal/AppPackages/
|
||||
Xamarin.Forms.ControlGallery.WindowsUniversal/BundleArtifacts/
|
||||
Xamarin.Forms.Controls/secrets.txt
|
||||
Xamarin.Forms.Controls/controlgallery.config
|
||||
Xamarin.Forms.ControlGallery.Android/Properties/MapsKey.cs
|
||||
|
|
До Ширина: | Высота: | Размер: 801 B После Ширина: | Высота: | Размер: 801 B |
До Ширина: | Высота: | Размер: 222 B После Ширина: | Высота: | Размер: 222 B |
До Ширина: | Высота: | Размер: 2.1 KiB После Ширина: | Высота: | Размер: 2.1 KiB |
До Ширина: | Высота: | Размер: 429 B После Ширина: | Высота: | Размер: 429 B |
|
@ -0,0 +1,45 @@
|
|||
using Xamarin.Forms.ControlGallery.WindowsUniversal;
|
||||
using Xamarin.Forms.Controls;
|
||||
using Xamarin.Forms.Platform.UWP;
|
||||
|
||||
[assembly: ExportRenderer(typeof(DisposePage), typeof(DisposePageRenderer))]
|
||||
[assembly: ExportRenderer(typeof(DisposeLabel), typeof(DisposeLabelRenderer))]
|
||||
|
||||
namespace Xamarin.Forms.ControlGallery.WindowsUniversal
|
||||
{
|
||||
public class DisposePageRenderer : PageRenderer
|
||||
{
|
||||
bool _disposed;
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (_disposed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_disposed = true;
|
||||
|
||||
if (disposing)
|
||||
{
|
||||
((DisposePage)Element).SendRendererDisposed();
|
||||
}
|
||||
|
||||
base.Dispose(disposing);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public class DisposeLabelRenderer : LabelRenderer
|
||||
{
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
|
||||
if (disposing)
|
||||
{
|
||||
((DisposeLabel)Element).SendRendererDisposed();
|
||||
}
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Package xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10" xmlns:mp="http://schemas.microsoft.com/appx/2014/phone/manifest" xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10" IgnorableNamespaces="uap mp">
|
||||
<Identity Name="0d4424f6-1e29-4476-ac00-ba22c3789cb6" Publisher="CN=XamarinForms" Version="1.0.0.0" />
|
||||
<Identity Name="0d4424f6-1e29-4476-ac00-ba22c3789cb6" Publisher="CN=XamarinForms" Version="1.0.2.0" />
|
||||
<mp:PhoneIdentity PhoneProductId="0d4424f6-1e29-4476-ac00-ba22c3789cb6" PhonePublisherId="00000000-0000-0000-0000-000000000000" />
|
||||
<Properties>
|
||||
<DisplayName>Xamarin.Forms.ControlGallery.WindowsUniversal</DisplayName>
|
||||
|
|
|
@ -20,6 +20,9 @@
|
|||
<ProjectTypeGuids>{A5A43C5B-DE2A-4C0C-9213-0A381AF9435A};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
|
||||
<PackageCertificateKeyFile>Xamarin.Forms.ControlGallery.WindowsUniversal_TemporaryKey.pfx</PackageCertificateKeyFile>
|
||||
<PackageCertificateThumbprint>D160433BDC22781DEAE0558325140B91C02A6A20</PackageCertificateThumbprint>
|
||||
<AppxAutoIncrementPackageRevision>True</AppxAutoIncrementPackageRevision>
|
||||
<AppxBundle>Always</AppxBundle>
|
||||
<AppxBundlePlatforms>x86|x64|arm</AppxBundlePlatforms>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|ARM'">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
|
@ -120,6 +123,7 @@
|
|||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="DisposePageRenderer.cs" />
|
||||
<Compile Include="_57114Renderer.cs" />
|
||||
<Compile Include="_58406EffectRenderer.cs" />
|
||||
<Compile Include="_60122ImageRenderer.cs" />
|
||||
|
@ -187,10 +191,10 @@
|
|||
</AppxManifest>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="Assets\Logo.scale-100.png" />
|
||||
<Content Include="Assets\SmallLogo.scale-100.png" />
|
||||
<Content Include="Assets\SplashScreen.scale-100.png" />
|
||||
<Content Include="Assets\StoreLogo.scale-100.png" />
|
||||
<Content Include="Assets\Logo.png" />
|
||||
<Content Include="Assets\SmallLogo.png" />
|
||||
<Content Include="Assets\SplashScreen.png" />
|
||||
<Content Include="Assets\StoreLogo.png" />
|
||||
<Content Include="Assets\WideLogo.scale-100.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
|
|
|
@ -22,7 +22,7 @@ namespace Xamarin.Forms.Controls.Issues
|
|||
{
|
||||
var listview = new ListView ();
|
||||
listview.ItemTemplate = new DataTemplate (typeof (ItemTemplate));
|
||||
listview.ItemsSource = new string[] { "item", "item", "item", "item", "item" };
|
||||
listview.ItemsSource = new string[] { "item1", "item2", "item3", "item4", "item5" };
|
||||
var btnBack = new Button { Text = "back", Command = new Command (() => Navigation.PopAsync ()) };
|
||||
listview.ItemSelected += (s, e) => Navigation.PushAsync (new ContentPage { Content = btnBack });
|
||||
var btnPush = new Button {
|
||||
|
|
|
@ -23,6 +23,7 @@ namespace Xamarin.Forms.Controls.Issues
|
|||
|
||||
protected override void Init ()
|
||||
{
|
||||
s_disposeCount = 0;
|
||||
s_lbl = new Label { AutomationId = "lblDisposedCound" };
|
||||
var tab1 = new DisposePage { Title = "Tab1" };
|
||||
var tab2 = new DisposePage { Title = "Tab2" };
|
||||
|
@ -52,7 +53,6 @@ namespace Xamarin.Forms.Controls.Issues
|
|||
RunningApp.Tap (q => q.Marked ("Tab1"));
|
||||
RunningApp.Tap (q => q.Marked ("Pop"));
|
||||
RunningApp.WaitForElement (q => q.Marked (string.Format ("Dispose {0} pages", 2)));
|
||||
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
|
|
@ -10,6 +10,8 @@ using NUnit.Framework;
|
|||
|
||||
namespace Xamarin.Forms.Controls.Issues
|
||||
{
|
||||
// Note that this test currently fails on UWP because of https://bugzilla.xamarin.com/show_bug.cgi?id=60478
|
||||
|
||||
[Preserve (AllMembers = true)]
|
||||
[Issue (IssueTracker.Bugzilla, 29257, "CarouselPage.CurrentPage Does Not Work Properly When Used Inside a NavigationPage ")]
|
||||
public class Bugzilla29257 : TestContentPage
|
||||
|
|
|
@ -116,7 +116,7 @@ namespace Xamarin.Forms.Controls.Issues
|
|||
|
||||
void Back()
|
||||
{
|
||||
#if __IOS__
|
||||
#if __IOS__ || __WINDOWS__
|
||||
RunningApp.Tap (q => q.Marked ("Toggle"));
|
||||
#else
|
||||
RunningApp.Back();
|
||||
|
|
|
@ -6,6 +6,7 @@ using System.Windows.Input;
|
|||
using Xamarin.Forms.Internals;
|
||||
|
||||
#if UITEST
|
||||
using Xamarin.Forms.Core.UITests;
|
||||
using Xamarin.UITest.iOS;
|
||||
using Xamarin.UITest;
|
||||
using NUnit.Framework;
|
||||
|
@ -13,6 +14,8 @@ using NUnit.Framework;
|
|||
|
||||
namespace Xamarin.Forms.Controls.Issues
|
||||
{
|
||||
// Note that this test fails on UWP due to https://bugzilla.xamarin.com/show_bug.cgi?id=59650
|
||||
|
||||
[Preserve (AllMembers = true)]
|
||||
[Issue (IssueTracker.Bugzilla, 31330, "Disabled context actions appear enabled")]
|
||||
public class Bugzilla31330 : TestContentPage
|
||||
|
@ -132,30 +135,15 @@ namespace Xamarin.Forms.Controls.Issues
|
|||
public void Bugzilla31330Test ()
|
||||
{
|
||||
RunningApp.WaitForElement (c => c.Marked ("Something 2"));
|
||||
var screenBounds = RunningApp.Query (q => q.Raw ("* index:0"))[0].Rect;
|
||||
|
||||
var cell = RunningApp.Query (c => c.Marked ("Something 1")) [0];
|
||||
var cell2 = RunningApp.Query (c => c.Marked ("Something 2")) [0];
|
||||
#if __IOS__
|
||||
RunningApp.DragCoordinates (screenBounds.Width - 10, cell.Rect.CenterY, 0, cell.Rect.CenterY);
|
||||
RunningApp.WaitForElement (c => c.Marked ("Delete"));
|
||||
RunningApp.Tap (c => c.Marked ("Delete"));
|
||||
RunningApp.WaitForElement (c => c.Marked ("Something 1"));
|
||||
RunningApp.ActivateContextMenu("Something 1");
|
||||
RunningApp.WaitForElement(c => c.Marked("Delete"));
|
||||
RunningApp.Tap(c => c.Marked("Delete"));
|
||||
RunningApp.DismissContextMenu();
|
||||
RunningApp.Tap (c => c.Marked ("Something 2"));
|
||||
RunningApp.DragCoordinates (screenBounds.Width - 10, cell2.Rect.CenterY, 0, cell2.Rect.CenterY);
|
||||
RunningApp.Tap (c => c.Marked ("Delete"));
|
||||
RunningApp.ActivateContextMenu("Something 2");
|
||||
RunningApp.WaitForElement(c => c.Marked("Delete"));
|
||||
RunningApp.Tap(c => c.Marked("Delete"));
|
||||
RunningApp.WaitForNoElement (c => c.Marked ("Something 2"));
|
||||
#else
|
||||
RunningApp.TouchAndHoldCoordinates (cell.Rect.CenterX, cell.Rect.CenterY);
|
||||
RunningApp.WaitForElement (c => c.Marked ("Delete"));
|
||||
RunningApp.Tap (c => c.Marked ("Delete"));
|
||||
RunningApp.Back ();
|
||||
RunningApp.WaitForElement (c => c.Marked ("Something 1"));
|
||||
RunningApp.Tap (c => c.Marked ("Something 2"));
|
||||
RunningApp.TouchAndHoldCoordinates (cell2.Rect.CenterX, cell2.Rect.CenterY);
|
||||
RunningApp.Tap (c => c.Marked ("Delete"));
|
||||
RunningApp.WaitForNoElement (c => c.Marked ("Something 2"));
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
|
|
@ -50,10 +50,10 @@ namespace Xamarin.Forms.Controls.Issues
|
|||
public void Bugzilla32230Test ()
|
||||
{
|
||||
RunningApp.Tap (q => q.Marked ("btnOpen"));
|
||||
Assert.AreEqual ("1", RunningApp.Query (q => q.Marked ("lblCount"))[0].Text);
|
||||
RunningApp.WaitForElement("1");
|
||||
RunningApp.Tap (q => q.Marked ("btnClose"));
|
||||
RunningApp.Tap (q => q.Marked ("btnOpen"));
|
||||
Assert.AreEqual ("3", RunningApp.Query (q => q.Marked ("lblCount"))[0].Text);
|
||||
RunningApp.WaitForElement("3");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
|
|
@ -63,8 +63,7 @@ namespace Xamarin.Forms.Controls.Issues
|
|||
RunningApp.Tap (q => q.Marked ("btnModal"));
|
||||
RunningApp.Tap (q => q.Marked ("btnPop"));
|
||||
await Task.Delay (1000);
|
||||
var lbl = RunningApp.WaitForElement (c => c.Marked("lblCount"));
|
||||
Assert.AreEqual ("1", lbl [0].Text);
|
||||
RunningApp.WaitForElement ("1");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@ using Xamarin.Forms.Internals;
|
|||
using Xamarin.UITest;
|
||||
using NUnit.Framework;
|
||||
using Xamarin.UITest.iOS;
|
||||
using Xamarin.Forms.Core.UITests;
|
||||
#endif
|
||||
|
||||
namespace Xamarin.Forms.Controls.Issues
|
||||
|
@ -74,13 +75,7 @@ namespace Xamarin.Forms.Controls.Issues
|
|||
{
|
||||
RunningApp.WaitForElement (q => q.Marked ("ListViewItem"));
|
||||
|
||||
#if __IOS__
|
||||
var listItem = RunningApp.Query (q => q.Marked ("ListViewItem"))[0].Rect;
|
||||
RunningApp.DragCoordinates(listItem.CenterX, listItem.CenterY, 0, listItem.CenterY);
|
||||
#else
|
||||
RunningApp.TouchAndHold (q => q.Marked ("ListViewItem"));
|
||||
#endif
|
||||
|
||||
RunningApp.ActivateContextMenu("ListViewItem");
|
||||
RunningApp.WaitForElement (q => q.Marked ("Click"));
|
||||
RunningApp.Tap (q => q.Marked ("Click"));
|
||||
RunningApp.WaitForElement (q => q.Marked ("NextPageLabel"));
|
||||
|
|
|
@ -16,6 +16,8 @@ namespace Xamarin.Forms.Controls.Issues
|
|||
[Category(UITestCategories.ListView)]
|
||||
#endif
|
||||
|
||||
// Note: Fails on UWP due to https://bugzilla.xamarin.com/show_bug.cgi?id=60521
|
||||
|
||||
[Preserve (AllMembers = true)]
|
||||
[Issue (IssueTracker.Bugzilla, 34912, "ListView.IsEnabled has no effect on iOS")]
|
||||
public class Bugzilla34912 : TestContentPage // or TestMasterDetailPage, etc ...
|
||||
|
|
|
@ -16,7 +16,7 @@ namespace Xamarin.Forms.Controls.Issues
|
|||
{
|
||||
protected override void Init ()
|
||||
{
|
||||
var entry = new Entry ();
|
||||
var entry = new Entry { AutomationId = "36171Entry" };
|
||||
var editor = new Editor();
|
||||
var focuseEntryButton = new Button { Text = "Start Entry" };
|
||||
var focuseEditorButton = new Button { Text = "Start Editor" };
|
||||
|
@ -88,7 +88,7 @@ namespace Xamarin.Forms.Controls.Issues
|
|||
var entry2 = RunningApp.Query (q => q.Text("1234"));
|
||||
Assert.That(entry2.Length >= 1);
|
||||
|
||||
RunningApp.ClearText();
|
||||
RunningApp.ClearText("36171Entry");
|
||||
|
||||
RunningApp.WaitForElement ("Start Editor");
|
||||
RunningApp.Tap ("Start Editor");
|
||||
|
|
|
@ -5,6 +5,7 @@ using Xamarin.Forms.CustomAttributes;
|
|||
using Xamarin.Forms.Internals;
|
||||
|
||||
#if UITEST
|
||||
using Xamarin.Forms.Core.UITests;
|
||||
using NUnit.Framework;
|
||||
using Xamarin.UITest;
|
||||
#endif
|
||||
|
@ -38,7 +39,7 @@ namespace Xamarin.Forms.Controls.Issues
|
|||
RunningApp.Screenshot ("All elements exist");
|
||||
|
||||
#if !__MACOS__
|
||||
var scrollRect = RunningApp.Query (q => q.Raw ("* index:0"))[0].Rect;
|
||||
var scrollRect = RunningApp.RootViewRect();
|
||||
Xamarin.Forms.Core.UITests.Gestures.ScrollForElement (RunningApp, "* marked:'9'", new Xamarin.Forms.Core.UITests.Drag (scrollRect, Xamarin.Forms.Core.UITests.Drag.Direction.BottomToTop, Xamarin.Forms.Core.UITests.Drag.DragLength.Long));
|
||||
RunningApp.Screenshot ("I see 9");
|
||||
#endif
|
||||
|
|
|
@ -1,13 +1,14 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
using Xamarin.Forms;
|
||||
using Xamarin.Forms.CustomAttributes;
|
||||
using System.Windows.Input;
|
||||
using System.Diagnostics;
|
||||
|
||||
using Xamarin.Forms.Internals;
|
||||
|
||||
#if UITEST
|
||||
using Xamarin.Forms.Core.UITests;
|
||||
using NUnit.Framework;
|
||||
using Xamarin.UITest;
|
||||
using Xamarin.UITest.iOS;
|
||||
|
@ -61,7 +62,7 @@ namespace Xamarin.Forms.Controls.Issues
|
|||
var disable1 = RunningApp.Query (c => c.Marked ("txtCellDisableContextActions1")) [0];
|
||||
Assert.IsFalse (disable1.Enabled);
|
||||
|
||||
var screenBounds = RunningApp.Query (q => q.Raw ("* index:0")) [0].Rect;
|
||||
var screenBounds = RunningApp.RootViewRect();
|
||||
|
||||
RunningApp.DragCoordinates (screenBounds.Width - 10, disable1.Rect.CenterY, 10, disable1.Rect.CenterY);
|
||||
|
||||
|
@ -87,7 +88,7 @@ namespace Xamarin.Forms.Controls.Issues
|
|||
var disable1 = RunningApp.Query (c => c.Marked ("txtCellEnabledContextActions1")) [0];
|
||||
Assert.IsTrue (disable1.Enabled);
|
||||
|
||||
var screenBounds = RunningApp.Query (q => q.Raw ("* index:0")) [0].Rect;
|
||||
var screenBounds = RunningApp.RootViewRect();
|
||||
|
||||
RunningApp.DragCoordinates (screenBounds.Width - 10, disable1.Rect.CenterY, 10, disable1.Rect.CenterY);
|
||||
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
using System;
|
||||
|
||||
using Xamarin.Forms;
|
||||
using Xamarin.Forms.CustomAttributes;
|
||||
using Xamarin.Forms.Internals;
|
||||
|
@ -7,6 +6,7 @@ using Xamarin.Forms.Internals;
|
|||
#if UITEST
|
||||
using NUnit.Framework;
|
||||
using Xamarin.UITest.iOS;
|
||||
using Xamarin.Forms.Core.UITests;
|
||||
#endif
|
||||
|
||||
namespace Xamarin.Forms.Controls.Issues
|
||||
|
@ -60,48 +60,19 @@ namespace Xamarin.Forms.Controls.Issues
|
|||
[Test]
|
||||
public void TestDoesntCrashShowingContextMenu ()
|
||||
{
|
||||
RunningApp.WaitForElement(c => c.Marked("Swipe ME"));
|
||||
|
||||
var screenBounds = RunningApp.Query (q => q.Raw ("* index:0"))[0].Rect;
|
||||
|
||||
var cell = RunningApp.Query(c => c.Marked("Swipe ME")) [0];
|
||||
#if __IOS__
|
||||
RunningApp.DragCoordinates (screenBounds.Width - 10, cell.Rect.CenterY, 0, cell.Rect.CenterY);
|
||||
//TODO: fix this when context menu bug is fixed
|
||||
RunningApp.WaitForElement (c => c.Marked ("Text4"));
|
||||
#else
|
||||
RunningApp.TouchAndHoldCoordinates (cell.Rect.CenterX, cell.Rect.CenterY);
|
||||
RunningApp.ActivateContextMenu("Swipe ME");
|
||||
RunningApp.WaitForElement (c => c.Marked ("Text0"));
|
||||
#endif
|
||||
RunningApp.Screenshot ("Didn't crash");
|
||||
RunningApp.TapCoordinates (screenBounds.CenterX, screenBounds.CenterY);
|
||||
|
||||
#if __ANDROID__
|
||||
RunningApp.Tap(c => c.Marked("Text0"));
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestShowContextMenuItemsInTheRightOrder ()
|
||||
{
|
||||
RunningApp.WaitForElement(c => c.Marked("Swipe ME"));
|
||||
|
||||
var screenBounds = RunningApp.Query (q => q.Raw ("* index:0"))[0].Rect;
|
||||
|
||||
var cell = RunningApp.Query (c => c.Marked ("Swipe ME")) [0];
|
||||
#if __IOS__
|
||||
RunningApp.DragCoordinates (screenBounds.Width -10, cell.Rect.CenterY, 0, cell.Rect.CenterY);
|
||||
#else
|
||||
RunningApp.TouchAndHoldCoordinates (cell.Rect.CenterX, cell.Rect.CenterY);
|
||||
#endif
|
||||
RunningApp.ActivateContextMenu("Swipe ME");
|
||||
RunningApp.WaitForElement (c => c.Marked ("Text0"));
|
||||
RunningApp.Screenshot ("Are the menuitems in the right order?");
|
||||
|
||||
#if __ANDROID__
|
||||
RunningApp.Tap(c => c.Marked("Text0"));
|
||||
#endif
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
|
|
|
@ -129,5 +129,7 @@ namespace Xamarin.Forms.Controls.Issues
|
|||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
// Note: this fails on UWP because we can't currently inspect listview headers
|
||||
}
|
||||
|
||||
|
|
|
@ -41,6 +41,8 @@ namespace Xamarin.Forms.Controls.Issues
|
|||
#if __ANDROID__
|
||||
//show secondary menu
|
||||
RunningApp.Tap (c => c.Class ("OverflowMenuButton"));
|
||||
#elif __WINDOWS__
|
||||
RunningApp.Tap ("MoreButton");
|
||||
#endif
|
||||
}
|
||||
|
||||
|
|
|
@ -88,3 +88,5 @@ namespace Xamarin.Forms.Controls.Issues
|
|||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
// Note: this fails on UWP because we can't currently inspect listview headers
|
|
@ -6,6 +6,7 @@ using Xamarin.Forms.CustomAttributes;
|
|||
using Xamarin.Forms.Internals;
|
||||
|
||||
#if UITEST
|
||||
using Xamarin.Forms.Core.UITests;
|
||||
using NUnit.Framework;
|
||||
using Xamarin.UITest;
|
||||
using Xamarin.UITest.iOS;
|
||||
|
@ -48,7 +49,7 @@ namespace Xamarin.Forms.Controls.Issues
|
|||
RunningApp.WaitForElement (q => q.Marked ("Swipe lightly left and right to crash this page"));
|
||||
System.Threading.Thread.Sleep (3);
|
||||
|
||||
var mainBounds = RunningApp.Query (q => q.Raw ("* index:0")) [0].Rect;
|
||||
var mainBounds = RunningApp.RootViewRect();
|
||||
|
||||
Xamarin.Forms.Core.UITests.Gestures.Pan (RunningApp, new Xamarin.Forms.Core.UITests.Drag (mainBounds, 0, 125, 75, 125, Xamarin.Forms.Core.UITests.Drag.Direction.LeftToRight));
|
||||
System.Threading.Thread.Sleep (3);
|
||||
|
|
|
@ -15,6 +15,8 @@ namespace Xamarin.Forms.Controls
|
|||
|
||||
public AppearingGalleryPage ()
|
||||
{
|
||||
App.AppearingMessages.Clear();
|
||||
|
||||
var initalPage = new AppearingPage (1);
|
||||
var initalPage2 = new AppearingPage (2);
|
||||
|
||||
|
|
|
@ -29,6 +29,10 @@ namespace Xamarin.Forms.Controls
|
|||
|
||||
public class CellTypeList : ListView
|
||||
{
|
||||
CellNavigation _last;
|
||||
|
||||
public const string CellTestContainerId = "CellTestContainer";
|
||||
|
||||
// TODO Add gallerys for ViewCell, ListView and TableView
|
||||
public CellTypeList ()
|
||||
{
|
||||
|
@ -52,12 +56,23 @@ namespace Xamarin.Forms.Controls
|
|||
|
||||
ItemTemplate = template;
|
||||
ItemSelected += (s, e) => {
|
||||
|
||||
if (SelectedItem == null)
|
||||
return;
|
||||
|
||||
var cellNav = (CellNavigation) e.SelectedItem;
|
||||
|
||||
if (cellNav == _last)
|
||||
{
|
||||
_last = null;
|
||||
return;
|
||||
}
|
||||
|
||||
Navigation.PushAsync (cellNav.Page);
|
||||
_last = cellNav;
|
||||
#if !__WINDOWS__
|
||||
SelectedItem = null;
|
||||
#endif
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,6 +31,7 @@ namespace Xamarin.Forms.Controls
|
|||
};
|
||||
|
||||
var listView = new ListView {
|
||||
AutomationId = CellTypeList.CellTestContainerId,
|
||||
ItemsSource = Enumerable.Range (0, 100).Select (i => new EntryCellTest {
|
||||
Label = "Label " + i,
|
||||
LabelColor = i % 2 == 0 ? Color.Red : Color.Blue,
|
||||
|
|
|
@ -73,6 +73,7 @@ namespace Xamarin.Forms.Controls
|
|||
};
|
||||
|
||||
var table = new TableView {
|
||||
AutomationId = CellTypeList.CellTestContainerId,
|
||||
Root = root
|
||||
};
|
||||
|
||||
|
|
|
@ -48,7 +48,7 @@ namespace Xamarin.Forms.Controls
|
|||
var label = new Label { Text = "I have not been selected" };
|
||||
|
||||
var listView = new ListView {
|
||||
AutomationId = "ImageCellListView",
|
||||
AutomationId = CellTypeList.CellTestContainerId,
|
||||
ItemsSource = Enumerable.Range (0, 100).Select (i => new ImageCellTest {
|
||||
Text = "Text " + i,
|
||||
TextColor = i % 2 == 0 ? Color.Red : Color.Blue,
|
||||
|
|
|
@ -56,6 +56,7 @@ namespace Xamarin.Forms.Controls
|
|||
};
|
||||
|
||||
var table = new TableView {
|
||||
AutomationId = CellTypeList.CellTestContainerId,
|
||||
Root = root,
|
||||
};
|
||||
|
||||
|
|
|
@ -29,6 +29,7 @@ namespace Xamarin.Forms.Controls
|
|||
var label = new Label { Text = "I have not been selected" };
|
||||
|
||||
var listView = new ListView {
|
||||
AutomationId = CellTypeList.CellTestContainerId,
|
||||
ItemsSource = Enumerable.Range (0, 100).Select (i => new SwitchCellItem {
|
||||
Label = "Label " + i,
|
||||
SwitchOn = i % 2 == 0 ? false : true,
|
||||
|
|
|
@ -53,6 +53,7 @@ namespace Xamarin.Forms.Controls
|
|||
};
|
||||
|
||||
var table = new TableView {
|
||||
AutomationId = CellTypeList.CellTestContainerId,
|
||||
Root = root,
|
||||
};
|
||||
|
||||
|
|
|
@ -29,6 +29,7 @@ namespace Xamarin.Forms.Controls
|
|||
dataTemplate.SetBinding (TextCell.DetailColorProperty, new Binding ("DetailColor"));
|
||||
|
||||
var listView = new ListView {
|
||||
AutomationId = CellTypeList.CellTestContainerId,
|
||||
ItemsSource = Enumerable.Range (0, 100).Select (i => new TextCellTest {
|
||||
Text = "Text " + i,
|
||||
TextColor = i % 2 == 0 ? Color.Red : Color.Blue,
|
||||
|
|
|
@ -46,6 +46,7 @@ namespace Xamarin.Forms.Controls
|
|||
};
|
||||
|
||||
var table = new TableView {
|
||||
AutomationId = CellTypeList.CellTestContainerId,
|
||||
Root = root,
|
||||
};
|
||||
|
||||
|
|
|
@ -93,6 +93,7 @@ namespace Xamarin.Forms.Controls
|
|||
var label = new Label { Text = "I have not been selected" };
|
||||
|
||||
var listView = new ListView {
|
||||
AutomationId = CellTypeList.CellTestContainerId,
|
||||
ItemsSource = Enumerable.Range (0, albums.Length).Select (i => new {
|
||||
Text = "Text " + i,
|
||||
TextColor = i % 2 == 0 ? Color.Red : Color.Blue,
|
||||
|
|
|
@ -102,7 +102,7 @@ namespace Xamarin.Forms.Core.UITests
|
|||
public static AppRect RootViewRect (this IApp app)
|
||||
{
|
||||
#if __WINDOWS__
|
||||
return app.Query("Xamarin.Forms.ControlGallery.WindowsUniversal")[0].Rect;
|
||||
return app.Query(WinDriverApp.AppName)[0].Rect;
|
||||
#else
|
||||
return app.Query (q => q.Raw ("* index:0"))[0].Rect;
|
||||
#endif
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
using NUnit.Framework;
|
||||
using System;
|
||||
using NUnit.Framework;
|
||||
using Xamarin.UITest.Queries;
|
||||
|
||||
namespace Xamarin.Forms.Core.UITests
|
||||
|
@ -156,10 +157,16 @@ namespace Xamarin.Forms.Core.UITests
|
|||
|
||||
void ScrollAndTap(string actionSheet)
|
||||
{
|
||||
#if !__MACOS__
|
||||
App.ScrollForElement(string.Format("* text:'{0}'", actionSheet), new Drag(App.Query(q => q.Marked("ActionSheetPage"))[0].Rect, Drag.Direction.BottomToTop, Drag.DragLength.Long));
|
||||
var queryString = $"* text:'{actionSheet}'";
|
||||
Func<AppQuery, AppQuery> actionSheetQuery = q => q.Raw (queryString);
|
||||
#if __WINDOWS__
|
||||
App.ScrollDownTo(actionSheetQuery);
|
||||
#elif __MACOS__
|
||||
App.Tap(actionSheetQuery);
|
||||
#else
|
||||
App.ScrollForElement(queryString, new Drag(App.Query(q => q.Marked("ActionSheetPage"))[0].Rect, Drag.Direction.BottomToTop, Drag.DragLength.Long));
|
||||
#endif
|
||||
App.Tap(q => q.Raw(string.Format("* text:'{0}'", actionSheet)));
|
||||
App.Tap(actionSheetQuery);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -54,6 +54,8 @@ namespace Xamarin.Forms.Core.UITests
|
|||
remote.GoTo();
|
||||
#if __MACOS__
|
||||
Assert.Inconclusive("Not tested yet");
|
||||
#elif __WINDOWS__
|
||||
Assert.Inconclusive(PleaseInspect);
|
||||
#else
|
||||
var isRunning = remote.GetProperty<bool> (ActivityIndicator.IsRunningProperty);
|
||||
Assert.IsTrue (isRunning);
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
using System.Linq;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using NUnit.Framework;
|
||||
|
@ -13,7 +14,9 @@ namespace Xamarin.Forms.Core.UITests
|
|||
[Category(UITestCategories.Cells)]
|
||||
internal class CellsGalleryTests : BaseTestFixture
|
||||
{
|
||||
// TODO find a way to test individula elements of cells
|
||||
public const string CellTestContainerId = "CellTestContainer";
|
||||
|
||||
// TODO find a way to test individual elements of cells
|
||||
// TODO port to new framework
|
||||
|
||||
protected override void NavigateToGallery()
|
||||
|
@ -21,23 +24,39 @@ namespace Xamarin.Forms.Core.UITests
|
|||
App.NavigateToGallery(GalleryQueries.CellsGalleryLegacy);
|
||||
}
|
||||
|
||||
void SelectTest(string testName)
|
||||
{
|
||||
#if __WINDOWS__
|
||||
App.ScrollDownTo(testName);
|
||||
#else
|
||||
App.ScrollForElement($"* marked:'{testName}'",
|
||||
new Drag(ScreenBounds, Drag.Direction.BottomToTop, Drag.DragLength.Medium));
|
||||
#endif
|
||||
App.Tap(q => q.Marked(testName));
|
||||
}
|
||||
|
||||
[Test]
|
||||
[Description("ListView with TextCells, all are present")]
|
||||
[UiTest(typeof(ListView))]
|
||||
[UiTest(typeof(TextCell))]
|
||||
public void CellsGalleryTextCellList()
|
||||
{
|
||||
App.ScrollForElement("* marked:'TextCell List'",
|
||||
new Drag(ScreenBounds, Drag.Direction.BottomToTop, Drag.DragLength.Medium));
|
||||
App.Tap(q => q.Marked("TextCell List"));
|
||||
SelectTest("TextCell List");
|
||||
|
||||
App.WaitForElement(q => q.Marked("Text 0"), "Timeout : Text 0");
|
||||
|
||||
App.Screenshot("At TextCell List Gallery");
|
||||
|
||||
App.ScrollForElement("* marked:'Detail 99'",
|
||||
new Drag(ScreenBounds, Drag.Direction.BottomToTop, Drag.DragLength.Medium));
|
||||
string target = "Detail 99";
|
||||
|
||||
App.WaitForElement(q => q.Marked("Detail 99"), "Timeout : Detail 99");
|
||||
#if __WINDOWS__
|
||||
App.ScrollDownTo(target, CellTestContainerId, timeout: TimeSpan.FromMinutes(1));
|
||||
#else
|
||||
App.ScrollForElement($"* marked:'{target}'",
|
||||
new Drag(ScreenBounds, Drag.Direction.BottomToTop, Drag.DragLength.Medium));
|
||||
#endif
|
||||
|
||||
App.WaitForElement(q => q.Marked(target), $"Timeout : {target}");
|
||||
|
||||
App.Screenshot("All TextCells are present");
|
||||
}
|
||||
|
@ -48,18 +67,22 @@ namespace Xamarin.Forms.Core.UITests
|
|||
[UiTest(typeof(TextCell))]
|
||||
public void CellsGalleryTextCellTable()
|
||||
{
|
||||
App.ScrollForElement("* marked:'TextCell Table'",
|
||||
new Drag(ScreenBounds, Drag.Direction.BottomToTop, Drag.DragLength.Medium));
|
||||
SelectTest("TextCell Table");
|
||||
|
||||
App.Tap(q => q.Marked("TextCell Table"));
|
||||
App.WaitForElement(q => q.Marked("Text 1"), "Timeout : Text 1");
|
||||
|
||||
App.Screenshot("At TextCell Table Gallery");
|
||||
|
||||
App.ScrollForElement("* marked:'Detail 12'",
|
||||
new Drag(ScreenBounds, Drag.Direction.BottomToTop, Drag.DragLength.Medium));
|
||||
string target = "Detail 12";
|
||||
|
||||
App.WaitForElement(q => q.Marked("Detail 12"), "Timeout : Detail 12");
|
||||
#if __WINDOWS__
|
||||
App.ScrollDownTo(target, CellTestContainerId, timeout: TimeSpan.FromMinutes(1));
|
||||
#else
|
||||
App.ScrollForElement($"* marked:'{target}'",
|
||||
new Drag(ScreenBounds, Drag.Direction.BottomToTop, Drag.DragLength.Medium));
|
||||
#endif
|
||||
|
||||
App.WaitForElement(q => q.Marked(target), $"Timeout : {target}");
|
||||
|
||||
App.Screenshot("All TextCells are present");
|
||||
}
|
||||
|
@ -72,43 +95,46 @@ namespace Xamarin.Forms.Core.UITests
|
|||
{
|
||||
Thread.Sleep(2000);
|
||||
|
||||
App.ScrollForElement("* marked:'ImageCell List'",
|
||||
new Drag(ScreenBounds, Drag.Direction.BottomToTop, Drag.DragLength.Medium));
|
||||
SelectTest("ImageCell List");
|
||||
|
||||
Thread.Sleep(2000);
|
||||
|
||||
App.Tap(q => q.Marked("ImageCell List"));
|
||||
App.WaitForElement(q => q.Marked("Text 0"), "Timeout : Text 0");
|
||||
|
||||
App.Screenshot("At ImageCell List Gallery");
|
||||
|
||||
var scollBounds = App.Query(q => q.Marked("ImageCellListView")).First().Rect;
|
||||
App.ScrollForElement("* marked:'Detail 99'",
|
||||
new Drag(scollBounds, Drag.Direction.BottomToTop, Drag.DragLength.Medium));
|
||||
string target = "Detail 99";
|
||||
|
||||
App.WaitForElement(q => q.Marked("Detail 99"), "Timeout : Detail 99");
|
||||
#if __WINDOWS__
|
||||
App.ScrollDownTo(target, CellTestContainerId, timeout: TimeSpan.FromMinutes(3));
|
||||
#else
|
||||
var scrollBounds = App.Query(q => q.Marked(CellTestContainerId)).First().Rect;
|
||||
App.ScrollForElement($"* marked:'{target}'",
|
||||
new Drag(scrollBounds, Drag.Direction.BottomToTop, Drag.DragLength.Medium));
|
||||
#endif
|
||||
|
||||
App.WaitForElement(q => q.Marked(target), $"Timeout : {target}");
|
||||
|
||||
App.Screenshot("All ImageCells are present");
|
||||
|
||||
#if !__WINDOWS__
|
||||
var numberOfImages = App.Query(q => q.Raw(PlatformViews.Image)).Length;
|
||||
// Check that there are images present. In Android,
|
||||
// have to make sure that there are more than 2 for navigation.
|
||||
Assert.IsTrue(numberOfImages > 2);
|
||||
#endif
|
||||
|
||||
App.Screenshot("Images are present");
|
||||
}
|
||||
|
||||
[Test]
|
||||
[Ignore("Ignore because is only failing on iOS10 at XTC")]
|
||||
[Ignore("Ignore because is only failing on iOS10 at XTC")] // TODO hartez also probably failing because the urls used on the test page are now invalid
|
||||
[Description("ListView with ImageCells, file access problems")]
|
||||
[UiTest(typeof(ListView))]
|
||||
[UiTest(typeof(ImageCell))]
|
||||
public async Task CellsGalleryImageUrlCellList()
|
||||
{
|
||||
App.ScrollForElement("* marked:'ImageCell Url List'",
|
||||
new Drag(ScreenBounds, Drag.Direction.BottomToTop, Drag.DragLength.Medium));
|
||||
|
||||
App.Tap(q => q.Marked("ImageCell Url List"));
|
||||
SelectTest("ImageCell Url List");
|
||||
|
||||
//var scollBounds = App.Query(q => q.Marked("ImageUrlCellListView")).First().Rect;
|
||||
//App.ScrollForElement("* marked:'Detail 200'", new Drag(scollBounds, Drag.Direction.BottomToTop, Drag.DragLength.Medium));
|
||||
|
@ -144,25 +170,31 @@ namespace Xamarin.Forms.Core.UITests
|
|||
[UiTest(typeof(ImageCell))]
|
||||
public void CellsGalleryImageCellTable()
|
||||
{
|
||||
App.ScrollForElement("* marked:'ImageCell Table'",
|
||||
new Drag(ScreenBounds, Drag.Direction.BottomToTop, Drag.DragLength.Medium));
|
||||
SelectTest("ImageCell Table");
|
||||
|
||||
App.Tap(q => q.Marked("ImageCell Table"));
|
||||
App.WaitForElement(q => q.Marked("Text 1"), "Timeout : Text 1");
|
||||
|
||||
App.Screenshot("At ImageCell Table Gallery");
|
||||
|
||||
App.ScrollForElement("* marked:'Detail 12'",
|
||||
new Drag(ScreenBounds, Drag.Direction.BottomToTop, Drag.DragLength.Medium));
|
||||
string target = "Detail 12";
|
||||
|
||||
App.WaitForElement(q => q.Marked("Detail 12"), "Timeout : Detail 12");
|
||||
#if __WINDOWS__
|
||||
App.ScrollDownTo(target, CellTestContainerId, timeout: TimeSpan.FromMinutes(1));
|
||||
#else
|
||||
App.ScrollForElement($"* marked:'{target}'",
|
||||
new Drag(ScreenBounds, Drag.Direction.BottomToTop, Drag.DragLength.Medium));
|
||||
#endif
|
||||
|
||||
App.WaitForElement(q => q.Marked(target), $"Timeout : {target}");
|
||||
|
||||
App.Screenshot("All ImageCells are present");
|
||||
|
||||
#if !__WINDOWS__
|
||||
var numberOfImages = App.Query(q => q.Raw(PlatformViews.Image)).Length;
|
||||
// Check that there are images present. In Android,
|
||||
// have to make sure that there are more than 2 for navigation.
|
||||
Assert.IsTrue(numberOfImages > 2);
|
||||
#endif
|
||||
|
||||
App.Screenshot("Images are present");
|
||||
}
|
||||
|
@ -173,19 +205,23 @@ namespace Xamarin.Forms.Core.UITests
|
|||
[UiTest(typeof(SwitchCell))]
|
||||
public void CellsGallerySwitchCellList()
|
||||
{
|
||||
App.ScrollForElement("* marked:'SwitchCell List'",
|
||||
new Drag(ScreenBounds, Drag.Direction.BottomToTop, Drag.DragLength.Medium));
|
||||
SelectTest("SwitchCell List");
|
||||
|
||||
App.Tap(q => q.Marked("SwitchCell List"));
|
||||
App.WaitForElement(q => q.Marked("Label 0"), "Timeout : Label 0");
|
||||
|
||||
App.Screenshot("At SwitchCell List Gallery");
|
||||
|
||||
App.ScrollForElement("* marked:'Label 99'",
|
||||
string target = "Label 99";
|
||||
|
||||
#if __WINDOWS__
|
||||
App.ScrollDownTo(target, CellTestContainerId, timeout: TimeSpan.FromMinutes(1));
|
||||
#else
|
||||
App.ScrollForElement($"* marked:'{target}'",
|
||||
new Drag(ScreenBounds, Drag.Direction.BottomToTop, Drag.DragLength.Medium));
|
||||
|
||||
var numberOfSwitches = App.Query(q => q.Raw(PlatformViews.Switch)).Length;
|
||||
Assert.IsTrue(numberOfSwitches > 2);
|
||||
#endif
|
||||
|
||||
App.Screenshot("Switches are present");
|
||||
}
|
||||
|
@ -196,18 +232,23 @@ namespace Xamarin.Forms.Core.UITests
|
|||
[UiTest(typeof(SwitchCell))]
|
||||
public void CellsGallerySwitchCellTable()
|
||||
{
|
||||
App.ScrollForElement("* marked:'SwitchCell Table'",
|
||||
new Drag(ScreenBounds, Drag.Direction.BottomToTop, Drag.DragLength.Medium));
|
||||
SelectTest("SwitchCell Table");
|
||||
|
||||
App.Tap(q => q.Marked("SwitchCell Table"));
|
||||
App.WaitForElement(q => q.Marked("text 1"), "Timeout : text 1");
|
||||
|
||||
App.Screenshot("At SwitchCell Table Gallery");
|
||||
|
||||
App.ScrollForElement("* marked:'text 32'", new Drag(ScreenBounds, Drag.Direction.BottomToTop, Drag.DragLength.Medium));
|
||||
string target = "text 32";
|
||||
|
||||
#if __WINDOWS__
|
||||
App.ScrollDownTo(target, CellTestContainerId, timeout: TimeSpan.FromMinutes(1));
|
||||
#else
|
||||
App.ScrollForElement($"* marked:'{target}'",
|
||||
new Drag(ScreenBounds, Drag.Direction.BottomToTop, Drag.DragLength.Medium));
|
||||
|
||||
var numberOfSwitches = App.Query(q => q.Raw(PlatformViews.Switch)).Length;
|
||||
Assert.IsTrue(numberOfSwitches > 2);
|
||||
#endif
|
||||
|
||||
App.Screenshot("Switches are present");
|
||||
}
|
||||
|
@ -218,16 +259,20 @@ namespace Xamarin.Forms.Core.UITests
|
|||
[UiTest(typeof(EntryCell))]
|
||||
public void CellsGalleryEntryCellList()
|
||||
{
|
||||
App.ScrollForElement("* marked:'EntryCell List'",
|
||||
new Drag(ScreenBounds, Drag.Direction.BottomToTop, Drag.DragLength.Medium));
|
||||
SelectTest("EntryCell List");
|
||||
|
||||
App.Tap(q => q.Marked("EntryCell List"));
|
||||
App.WaitForElement(q => q.Marked("Label 0"), "Timeout : Label 0");
|
||||
|
||||
App.Screenshot("At EntryCell List Gallery");
|
||||
|
||||
App.ScrollForElement("* marked:'Label 99'",
|
||||
string target = "Label 99";
|
||||
|
||||
#if __WINDOWS__
|
||||
App.ScrollDownTo(target, CellTestContainerId, timeout: TimeSpan.FromMinutes(3));
|
||||
#else
|
||||
App.ScrollForElement($"* marked:'{target}'",
|
||||
new Drag(ScreenBounds, Drag.Direction.BottomToTop, Drag.DragLength.Medium));
|
||||
#endif
|
||||
|
||||
App.Screenshot("All EntryCells are present");
|
||||
}
|
||||
|
@ -238,15 +283,20 @@ namespace Xamarin.Forms.Core.UITests
|
|||
[UiTest(typeof(EntryCell))]
|
||||
public void CellsGalleryEntryCellTable()
|
||||
{
|
||||
App.ScrollForElement("* marked:'EntryCell Table'",
|
||||
new Drag(ScreenBounds, Drag.Direction.BottomToTop, Drag.DragLength.Medium));
|
||||
SelectTest("EntryCell Table");
|
||||
|
||||
App.Tap(q => q.Marked("EntryCell Table"));
|
||||
App.WaitForElement(q => q.Marked("Text 2"), "Timeout : Text 2");
|
||||
|
||||
App.Screenshot("At EntryCell Table Gallery");
|
||||
|
||||
App.ScrollForElement("* marked:'Text 32'", new Drag(ScreenBounds, Drag.Direction.BottomToTop, Drag.DragLength.Medium));
|
||||
string target = "Text 32";
|
||||
|
||||
#if __WINDOWS__
|
||||
App.ScrollDownTo(target, CellTestContainerId, timeout: TimeSpan.FromMinutes(1));
|
||||
#else
|
||||
App.ScrollForElement($"* marked:'{target}'",
|
||||
new Drag(ScreenBounds, Drag.Direction.BottomToTop, Drag.DragLength.Medium));
|
||||
#endif
|
||||
|
||||
App.Screenshot("All EntryCells are present");
|
||||
}
|
||||
|
@ -257,19 +307,24 @@ namespace Xamarin.Forms.Core.UITests
|
|||
[UiTest(typeof(EntryCell), "Completed")]
|
||||
public void CellsGalleryEntryCellCompleted()
|
||||
{
|
||||
App.ScrollForElement("* marked:'EntryCell Table'",
|
||||
new Drag(ScreenBounds, Drag.Direction.BottomToTop, Drag.DragLength.Medium));
|
||||
SelectTest("EntryCell Table");
|
||||
|
||||
App.Tap(q => q.Marked("EntryCell Table"));
|
||||
App.WaitForElement(q => q.Marked("Text 2"), "Timeout : Text 2");
|
||||
|
||||
App.Screenshot("At EntryCell Table Gallery");
|
||||
App.ScrollForElement("* marked:'Enter text'",
|
||||
|
||||
string target = "Enter text";
|
||||
|
||||
#if __WINDOWS__
|
||||
App.ScrollDownTo(target, CellTestContainerId, timeout: TimeSpan.FromMinutes(1));
|
||||
#else
|
||||
App.ScrollForElement($"* marked:'{target}'",
|
||||
new Drag(ScreenBounds, Drag.Direction.BottomToTop, Drag.DragLength.Medium));
|
||||
#endif
|
||||
|
||||
App.Screenshot("Before clicking Entry");
|
||||
|
||||
#if !__IOS__
|
||||
#if !__IOS__ && !__WINDOWS__
|
||||
App.Tap(PlatformQueries.EntryCellWithPlaceholder("I am a placeholder"));
|
||||
App.EnterText(PlatformQueries.EntryCellWithPlaceholder("I am a placeholder"), "Hi");
|
||||
App.Screenshot("Entered Text");
|
||||
|
|
|
@ -73,9 +73,14 @@ namespace Xamarin.Forms.Core.UITests
|
|||
{
|
||||
foreach (var page in rootPages)
|
||||
{
|
||||
#if __WINDOWS__
|
||||
App.ScrollDownTo(page.ButtonId, "ChoosePageScrollView");
|
||||
#else
|
||||
var scrollViewArea = App.Query(q => q.Marked("ChoosePageScrollView")).First().Rect;
|
||||
App.ScrollForElement(string.Format("* marked:'{0}'", page.ButtonId),
|
||||
new Drag(scrollViewArea, Drag.Direction.BottomToTop, Drag.DragLength.Long));
|
||||
#endif
|
||||
|
||||
App.Tap(q => q.Marked(page.ButtonId));
|
||||
|
||||
bool ios = false;
|
||||
|
|
|
@ -14,6 +14,8 @@ namespace Xamarin.Forms.Core.UITests
|
|||
[Category("ViewBaseTests")]
|
||||
internal abstract class _ViewUITests : BaseTestFixture
|
||||
{
|
||||
protected const string PleaseInspect = "Test framework cannout currently check this value; please inspect visually";
|
||||
|
||||
/* Under score prefixes ensure inherited properties run first in test suite */
|
||||
//[Test]
|
||||
//[Category ("View")]
|
||||
|
@ -145,6 +147,8 @@ namespace Xamarin.Forms.Core.UITests
|
|||
remote.GoTo();
|
||||
#if __MACOS__
|
||||
Assert.Inconclusive("needs testing");
|
||||
#elif __WINDOWS__
|
||||
Assert.Inconclusive(PleaseInspect);
|
||||
#else
|
||||
float opacity = -1f;
|
||||
opacity = remote.GetProperty<float> (View.OpacityProperty);
|
||||
|
@ -169,7 +173,10 @@ namespace Xamarin.Forms.Core.UITests
|
|||
Matrix generatedMatrix = NumericExtensions.CalculateRotationMatrixForDegrees (10, Axis.Z);
|
||||
Assert.AreEqual (generatedMatrix, rotationMatrix);
|
||||
#endif
|
||||
}
|
||||
#if __WINDOWS__
|
||||
Assert.Inconclusive(PleaseInspect);
|
||||
#endif
|
||||
}
|
||||
|
||||
[Test]
|
||||
[UiTest (typeof (VisualElement), "RotationX")]
|
||||
|
@ -186,6 +193,9 @@ namespace Xamarin.Forms.Core.UITests
|
|||
var rotationXMatrix = remote.GetProperty<Matrix> (View.RotationXProperty);
|
||||
Matrix matrix = NumericExtensions.CalculateRotationMatrixForDegrees (33.0f, Axis.X);
|
||||
Assert.AreEqual (matrix, rotationXMatrix);
|
||||
#endif
|
||||
#if __WINDOWS__
|
||||
Assert.Inconclusive(PleaseInspect);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -204,6 +214,9 @@ namespace Xamarin.Forms.Core.UITests
|
|||
var rotationYMatrix = remote.GetProperty<Matrix> (View.RotationYProperty);
|
||||
Matrix matrix = NumericExtensions.CalculateRotationMatrixForDegrees (10.0f, Axis.Y);
|
||||
Assert.AreEqual (matrix, rotationYMatrix);
|
||||
#endif
|
||||
#if __WINDOWS__
|
||||
Assert.Inconclusive(PleaseInspect);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -216,10 +229,14 @@ namespace Xamarin.Forms.Core.UITests
|
|||
#if __MACOS__
|
||||
Assert.Inconclusive("needs testing");
|
||||
#else
|
||||
#if __WINDOWS__
|
||||
Assert.Inconclusive(PleaseInspect);
|
||||
#endif
|
||||
var scaleMatrix = remote.GetProperty<Matrix>(View.ScaleProperty);
|
||||
Matrix generatedMatrix = NumericExtensions.BuildScaleMatrix(0.5f);
|
||||
Assert.AreEqual(generatedMatrix, scaleMatrix);
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
@ -229,6 +246,9 @@ namespace Xamarin.Forms.Core.UITests
|
|||
{
|
||||
var remote = new ViewContainerRemote (App, Test.VisualElement.TranslationX, PlatformViewType);
|
||||
remote.GoTo ();
|
||||
#if __WINDOWS__
|
||||
Assert.Inconclusive(PleaseInspect);
|
||||
#endif
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
@ -238,6 +258,9 @@ namespace Xamarin.Forms.Core.UITests
|
|||
{
|
||||
var remote = new ViewContainerRemote (App, Test.VisualElement.TranslationY, PlatformViewType);
|
||||
remote.GoTo ();
|
||||
#if __WINDOWS__
|
||||
Assert.Inconclusive(PleaseInspect);
|
||||
#endif
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
|
|
@ -9,12 +9,12 @@ namespace Xamarin.Forms.Core.UITests
|
|||
{
|
||||
public static bool ScrollForElement (this IApp app, string query, Drag drag, int maxSteps = 25)
|
||||
{
|
||||
Func<AppQuery, AppQuery> elementQuery = q => q.Raw (query);
|
||||
|
||||
int count = 0;
|
||||
|
||||
int centerTolerance = 50;
|
||||
|
||||
Func<AppQuery, AppQuery> elementQuery = q => q.Raw (query);
|
||||
|
||||
// Visible elements
|
||||
if (app.Query (elementQuery).Length > 1) {
|
||||
throw new UITestQueryMultipleResultsException (query);
|
||||
|
@ -117,10 +117,27 @@ namespace Xamarin.Forms.Core.UITests
|
|||
rect.CenterY,
|
||||
rect.X + (0.25f * rect.Width),
|
||||
rect.CenterY);
|
||||
#else
|
||||
#elif __ANDROID__
|
||||
app.TouchAndHold(target);
|
||||
#elif __WINDOWS__
|
||||
// Since we know we're on desktop for the moment, just use ContextClick. If we get this running
|
||||
// on actual touch devices at some point, we'll need to check for that and use TouchAndHold
|
||||
app.Invoke("ContextClick", target);
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
public static void DismissContextMenu(this IApp app)
|
||||
{
|
||||
#if __IOS__
|
||||
var screenbounds = app.RootViewRect();
|
||||
app.TapCoordinates (screenbounds.CenterX, screenbounds.CenterY);
|
||||
#elif __ANDROID__
|
||||
app.Back();
|
||||
#elif __WINDOWS__
|
||||
var screenbounds = app.RootViewRect();
|
||||
app.TapCoordinates (screenbounds.CenterX, screenbounds.CenterY);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,992 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Diagnostics;
|
||||
using System.Drawing;
|
||||
using System.Drawing.Imaging;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Threading.Tasks;
|
||||
using OpenQA.Selenium;
|
||||
using OpenQA.Selenium.Appium.Windows;
|
||||
using OpenQA.Selenium.Remote;
|
||||
using Xamarin.UITest;
|
||||
using Xamarin.UITest.Queries;
|
||||
using Xamarin.UITest.Queries.Tokens;
|
||||
|
||||
namespace Xamarin.Forms.Core.UITests
|
||||
{
|
||||
public class WinDriverApp : IApp
|
||||
{
|
||||
public const string AppName = "Xamarin.Forms.ControlGallery.WindowsUniversal";
|
||||
|
||||
readonly Dictionary<string, string> _controlNameToTag = new Dictionary<string, string>
|
||||
{
|
||||
{ "button", "ControlType.Button" }
|
||||
};
|
||||
|
||||
readonly WindowsDriver<WindowsElement> _session;
|
||||
|
||||
readonly Dictionary<string, string> _translatePropertyAccessor = new Dictionary<string, string>
|
||||
{
|
||||
{ "getAlpha", "Opacity" }
|
||||
};
|
||||
|
||||
int _scrollBarOffset = 5;
|
||||
|
||||
WindowsElement _viewPort;
|
||||
|
||||
WindowsElement _window;
|
||||
|
||||
public WinDriverApp(WindowsDriver<WindowsElement> session)
|
||||
{
|
||||
_session = session;
|
||||
TestServer = new WindowsTestServer(_session);
|
||||
}
|
||||
|
||||
public void Back()
|
||||
{
|
||||
QueryWindows("Back").First().Click();
|
||||
}
|
||||
|
||||
public void ClearText(Func<AppQuery, AppQuery> query)
|
||||
{
|
||||
QueryWindows(query).First().Clear();
|
||||
}
|
||||
|
||||
public void ClearText(Func<AppQuery, AppWebQuery> query)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void ClearText(string marked)
|
||||
{
|
||||
QueryWindows(marked).First().Clear();
|
||||
}
|
||||
|
||||
public void ClearText()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public IDevice Device { get; }
|
||||
|
||||
public void DismissKeyboard()
|
||||
{
|
||||
// No-op for Desktop, which is all we're doing right now
|
||||
}
|
||||
|
||||
public void DoubleTap(Func<AppQuery, AppQuery> query)
|
||||
{
|
||||
DoubleTap(WinQuery.FromQuery(query));
|
||||
}
|
||||
|
||||
public void DoubleTap(string marked)
|
||||
{
|
||||
DoubleTap(WinQuery.FromMarked(marked));
|
||||
}
|
||||
|
||||
public void DoubleTapCoordinates(float x, float y)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void DragAndDrop(Func<AppQuery, AppQuery> from, Func<AppQuery, AppQuery> to)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void DragAndDrop(string from, string to)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void DragCoordinates(float fromX, float fromY, float toX, float toY)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void EnterText(string text)
|
||||
{
|
||||
_session.Keyboard.SendKeys(text);
|
||||
}
|
||||
|
||||
public void EnterText(Func<AppQuery, AppQuery> query, string text)
|
||||
{
|
||||
QueryWindows(query).First().SendKeys(text);
|
||||
}
|
||||
|
||||
public void EnterText(string marked, string text)
|
||||
{
|
||||
QueryWindows(marked).First().SendKeys(text);
|
||||
}
|
||||
|
||||
public void EnterText(Func<AppQuery, AppWebQuery> query, string text)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public AppResult[] Flash(Func<AppQuery, AppQuery> query = null)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public AppResult[] Flash(string marked)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public object Invoke(string methodName, object argument = null)
|
||||
{
|
||||
return Invoke(methodName, new[] { argument });
|
||||
}
|
||||
|
||||
public object Invoke(string methodName, object[] arguments)
|
||||
{
|
||||
if (methodName == "ContextClick")
|
||||
{
|
||||
// The IApp interface doesn't have a context click concept, and mapping TouchAndHold to
|
||||
// context clicking would box us in if we have the option of running these tests on touch
|
||||
// devices later. So we're going to use the back door.
|
||||
ContextClick(arguments[0].ToString());
|
||||
return null;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public void PinchToZoomIn(Func<AppQuery, AppQuery> query, TimeSpan? duration = null)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void PinchToZoomIn(string marked, TimeSpan? duration = null)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void PinchToZoomInCoordinates(float x, float y, TimeSpan? duration)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void PinchToZoomOut(Func<AppQuery, AppQuery> query, TimeSpan? duration = null)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void PinchToZoomOut(string marked, TimeSpan? duration = null)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void PinchToZoomOutCoordinates(float x, float y, TimeSpan? duration)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void PressEnter()
|
||||
{
|
||||
_session.Keyboard.PressKey(Keys.Enter);
|
||||
}
|
||||
|
||||
public void PressVolumeDown()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void PressVolumeUp()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public AppPrintHelper Print { get; }
|
||||
|
||||
public AppResult[] Query(Func<AppQuery, AppQuery> query = null)
|
||||
{
|
||||
ReadOnlyCollection<WindowsElement> elements = QueryWindows(WinQuery.FromQuery(query));
|
||||
return elements.Select(ToAppResult).ToArray();
|
||||
}
|
||||
|
||||
public AppResult[] Query(string marked)
|
||||
{
|
||||
ReadOnlyCollection<WindowsElement> elements = QueryWindows(marked);
|
||||
return elements.Select(ToAppResult).ToArray();
|
||||
}
|
||||
|
||||
public AppWebResult[] Query(Func<AppQuery, AppWebQuery> query)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public T[] Query<T>(Func<AppQuery, AppTypedSelector<T>> query)
|
||||
{
|
||||
AppTypedSelector<T> appTypedSelector = query(new AppQuery(QueryPlatform.iOS));
|
||||
|
||||
// Swiss-Army Chainsaw time
|
||||
// We'll use reflection to dig into the query and get the element selector
|
||||
// and the property value invocation in text form
|
||||
BindingFlags bindingFlags = BindingFlags.Instance | BindingFlags.NonPublic;
|
||||
Type selectorType = appTypedSelector.GetType();
|
||||
PropertyInfo tokensProperty = selectorType.GetProperties(bindingFlags)
|
||||
.First(t => t.PropertyType == typeof(IQueryToken[]));
|
||||
|
||||
var tokens = (IQueryToken[])tokensProperty.GetValue(appTypedSelector);
|
||||
|
||||
string selector = tokens[0].ToQueryString(QueryPlatform.iOS);
|
||||
string invoke = tokens[1].ToCodeString();
|
||||
|
||||
// Now that we have them in text form, we can reinterpret them for Windows
|
||||
WinQuery winQuery = WinQuery.FromRaw(selector);
|
||||
// TODO hartez 2017/07/19 17:08:44 Make this a bit more resilient if the translation isn't there
|
||||
string attribute = _translatePropertyAccessor[invoke.Substring(8).Replace("\")", "")];
|
||||
|
||||
ReadOnlyCollection<WindowsElement> elements = QueryWindows(winQuery);
|
||||
|
||||
foreach (WindowsElement e in elements)
|
||||
{
|
||||
string x = e.GetAttribute(attribute);
|
||||
Debug.WriteLine($">>>>> WinDriverApp Query 261: {x}");
|
||||
}
|
||||
|
||||
// TODO hartez 2017/07/19 17:09:14 Alas, for now this simply doesn't work. Waiting for WinAppDriver to implement it
|
||||
return elements.Select(e => (T)Convert.ChangeType(e.GetAttribute(attribute), typeof(T))).ToArray();
|
||||
}
|
||||
|
||||
public string[] Query(Func<AppQuery, InvokeJSAppQuery> query)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void Repl()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public FileInfo Screenshot(string title)
|
||||
{
|
||||
// TODO hartez 2017/07/18 10:16:56 Verify that this is working; seems a bit too simple
|
||||
string filename = $"{title}.png";
|
||||
|
||||
Screenshot screenshot = _session.GetScreenshot();
|
||||
screenshot.SaveAsFile(filename, ImageFormat.Png);
|
||||
return new FileInfo(filename);
|
||||
}
|
||||
|
||||
public void ScrollDown(Func<AppQuery, AppQuery> withinQuery = null, ScrollStrategy strategy = ScrollStrategy.Auto,
|
||||
double swipePercentage = 0.67,
|
||||
int swipeSpeed = 500, bool withInertia = true)
|
||||
{
|
||||
if (withinQuery == null)
|
||||
{
|
||||
Scroll(null, true);
|
||||
return;
|
||||
}
|
||||
|
||||
WinQuery winQuery = WinQuery.FromQuery(withinQuery);
|
||||
Scroll(winQuery, true);
|
||||
}
|
||||
|
||||
public void ScrollDown(string withinMarked, ScrollStrategy strategy = ScrollStrategy.Auto,
|
||||
double swipePercentage = 0.67,
|
||||
int swipeSpeed = 500, bool withInertia = true)
|
||||
{
|
||||
WinQuery winQuery = WinQuery.FromMarked(withinMarked);
|
||||
Scroll(winQuery, true);
|
||||
}
|
||||
|
||||
public void ScrollDownTo(string toMarked, string withinMarked = null, ScrollStrategy strategy = ScrollStrategy.Auto,
|
||||
double swipePercentage = 0.67, int swipeSpeed = 500, bool withInertia = true, TimeSpan? timeout = null)
|
||||
{
|
||||
ScrollTo(WinQuery.FromMarked(toMarked), withinMarked == null ? null : WinQuery.FromMarked(withinMarked), timeout);
|
||||
}
|
||||
|
||||
public void ScrollDownTo(Func<AppQuery, AppWebQuery> toQuery, string withinMarked,
|
||||
ScrollStrategy strategy = ScrollStrategy.Auto, double swipePercentage = 0.67,
|
||||
int swipeSpeed = 500, bool withInertia = true, TimeSpan? timeout = null)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void ScrollDownTo(Func<AppQuery, AppQuery> toQuery, Func<AppQuery, AppQuery> withinQuery = null,
|
||||
ScrollStrategy strategy = ScrollStrategy.Auto, double swipePercentage = 0.67,
|
||||
int swipeSpeed = 500, bool withInertia = true, TimeSpan? timeout = null)
|
||||
{
|
||||
ScrollTo(WinQuery.FromQuery(toQuery), withinQuery == null ? null : WinQuery.FromQuery(withinQuery), timeout);
|
||||
}
|
||||
|
||||
public void ScrollDownTo(Func<AppQuery, AppWebQuery> toQuery, Func<AppQuery, AppQuery> withinQuery = null,
|
||||
ScrollStrategy strategy = ScrollStrategy.Auto, double swipePercentage = 0.67,
|
||||
int swipeSpeed = 500, bool withInertia = true, TimeSpan? timeout = null)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void ScrollTo(string toMarked, string withinMarked = null, ScrollStrategy strategy = ScrollStrategy.Auto,
|
||||
double swipePercentage = 0.67, int swipeSpeed = 500, bool withInertia = true, TimeSpan? timeout = null)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void ScrollUp(Func<AppQuery, AppQuery> query = null, ScrollStrategy strategy = ScrollStrategy.Auto,
|
||||
double swipePercentage = 0.67, int swipeSpeed = 500,
|
||||
bool withInertia = true)
|
||||
{
|
||||
if (query == null)
|
||||
{
|
||||
Scroll(null, false);
|
||||
return;
|
||||
}
|
||||
|
||||
WinQuery winQuery = WinQuery.FromQuery(query);
|
||||
Scroll(winQuery, false);
|
||||
}
|
||||
|
||||
public void ScrollUp(string withinMarked, ScrollStrategy strategy = ScrollStrategy.Auto,
|
||||
double swipePercentage = 0.67, int swipeSpeed = 500,
|
||||
bool withInertia = true)
|
||||
{
|
||||
WinQuery winQuery = WinQuery.FromMarked(withinMarked);
|
||||
Scroll(winQuery, false);
|
||||
}
|
||||
|
||||
public void ScrollUpTo(string toMarked, string withinMarked = null, ScrollStrategy strategy = ScrollStrategy.Auto,
|
||||
double swipePercentage = 0.67, int swipeSpeed = 500, bool withInertia = true, TimeSpan? timeout = null)
|
||||
{
|
||||
ScrollTo(WinQuery.FromMarked(toMarked), withinMarked == null ? null : WinQuery.FromMarked(withinMarked), timeout,
|
||||
down: false);
|
||||
}
|
||||
|
||||
public void ScrollUpTo(Func<AppQuery, AppWebQuery> toQuery, string withinMarked,
|
||||
ScrollStrategy strategy = ScrollStrategy.Auto, double swipePercentage = 0.67,
|
||||
int swipeSpeed = 500, bool withInertia = true, TimeSpan? timeout = null)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void ScrollUpTo(Func<AppQuery, AppQuery> toQuery, Func<AppQuery, AppQuery> withinQuery = null,
|
||||
ScrollStrategy strategy = ScrollStrategy.Auto, double swipePercentage = 0.67,
|
||||
int swipeSpeed = 500, bool withInertia = true, TimeSpan? timeout = null)
|
||||
{
|
||||
ScrollTo(WinQuery.FromQuery(toQuery), withinQuery == null ? null : WinQuery.FromQuery(withinQuery), timeout,
|
||||
down: false);
|
||||
}
|
||||
|
||||
public void ScrollUpTo(Func<AppQuery, AppWebQuery> toQuery, Func<AppQuery, AppQuery> withinQuery = null,
|
||||
ScrollStrategy strategy = ScrollStrategy.Auto, double swipePercentage = 0.67,
|
||||
int swipeSpeed = 500, bool withInertia = true, TimeSpan? timeout = null)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void SetOrientationLandscape()
|
||||
{
|
||||
// Deliberately leaving this as a no-op for now
|
||||
// Trying to set the orientation on the Desktop (the only version of UWP we're testing for the moment)
|
||||
// gives us a 405 Method Not Allowed, which makes sense. Haven't figured out how to determine
|
||||
// whether we're in a mode which allows orientation, but if we were, the next line is probably how to set it.
|
||||
//_session.Orientation = ScreenOrientation.Landscape;
|
||||
}
|
||||
|
||||
public void SetOrientationPortrait()
|
||||
{
|
||||
// Deliberately leaving this as a no-op for now
|
||||
// Trying to set the orientation on the Desktop (the only version of UWP we're testing for the moment)
|
||||
// gives us a 405 Method Not Allowed, which makes sense. Haven't figured out how to determine
|
||||
// whether we're in a mode which allows orientation, but if we were, the next line is probably how to set it.
|
||||
//_session.Orientation = ScreenOrientation.Portrait;
|
||||
}
|
||||
|
||||
public void SetSliderValue(string marked, double value)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void SetSliderValue(Func<AppQuery, AppQuery> query, double value)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void SwipeLeft()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void SwipeLeftToRight(double swipePercentage = 0.67, int swipeSpeed = 500, bool withInertia = true)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void SwipeLeftToRight(string marked, double swipePercentage = 0.67, int swipeSpeed = 500,
|
||||
bool withInertia = true)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void SwipeLeftToRight(Func<AppQuery, AppQuery> query, double swipePercentage = 0.67, int swipeSpeed = 500,
|
||||
bool withInertia = true)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void SwipeLeftToRight(Func<AppQuery, AppWebQuery> query, double swipePercentage = 0.67, int swipeSpeed = 500,
|
||||
bool withInertia = true)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void SwipeRight()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void SwipeRightToLeft(double swipePercentage = 0.67, int swipeSpeed = 500, bool withInertia = true)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void SwipeRightToLeft(string marked, double swipePercentage = 0.67, int swipeSpeed = 500,
|
||||
bool withInertia = true)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void SwipeRightToLeft(Func<AppQuery, AppQuery> query, double swipePercentage = 0.67, int swipeSpeed = 500,
|
||||
bool withInertia = true)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void SwipeRightToLeft(Func<AppQuery, AppWebQuery> query, double swipePercentage = 0.67, int swipeSpeed = 500,
|
||||
bool withInertia = true)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void Tap(Func<AppQuery, AppQuery> query)
|
||||
{
|
||||
WinQuery winQuery = WinQuery.FromQuery(query);
|
||||
Tap(winQuery);
|
||||
}
|
||||
|
||||
public void Tap(string marked)
|
||||
{
|
||||
WinQuery winQuery = WinQuery.FromMarked(marked);
|
||||
Tap(winQuery);
|
||||
}
|
||||
|
||||
public void Tap(Func<AppQuery, AppWebQuery> query)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void TapCoordinates(float x, float y)
|
||||
{
|
||||
// Okay, this one's a bit complicated. For some reason, _session.Tap() with coordinates does not work
|
||||
// (Filed https://github.com/Microsoft/WinAppDriver/issues/229 for that)
|
||||
// But we can do the equivalent by manipulating the mouse. The mouse methods all take an ICoordinates
|
||||
// object, and you'd think that the "coordinates" part of ICoordinates would have something do with
|
||||
// where the mouse clicks. You'd be wrong. The coordinates parts of that object are ignored and it just
|
||||
// clicks the center of whatever WindowsElement the ICoordinates refers to in 'AuxiliaryLocator'
|
||||
|
||||
// If we could just use the element, we wouldn't be tapping at specific coordinates, so that's not
|
||||
// very helpful.
|
||||
|
||||
// Instead, we'll use MouseClickAt
|
||||
|
||||
MouseClickAt(x, y);
|
||||
}
|
||||
|
||||
public ITestServer TestServer { get; }
|
||||
|
||||
public void TouchAndHold(Func<AppQuery, AppQuery> query)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void TouchAndHold(string marked)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void TouchAndHoldCoordinates(float x, float y)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void WaitFor(Func<bool> predicate, string timeoutMessage = "Timed out waiting...", TimeSpan? timeout = null,
|
||||
TimeSpan? retryFrequency = null, TimeSpan? postTimeout = null)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public AppResult[] WaitForElement(Func<AppQuery, AppQuery> query,
|
||||
string timeoutMessage = "Timed out waiting for element...",
|
||||
TimeSpan? timeout = null, TimeSpan? retryFrequency = null, TimeSpan? postTimeout = null)
|
||||
{
|
||||
Func<ReadOnlyCollection<WindowsElement>> result = () => QueryWindows(query);
|
||||
return WaitForAtLeastOne(result, timeoutMessage, timeout, retryFrequency).Select(ToAppResult).ToArray();
|
||||
}
|
||||
|
||||
public AppResult[] WaitForElement(string marked, string timeoutMessage = "Timed out waiting for element...",
|
||||
TimeSpan? timeout = null, TimeSpan? retryFrequency = null, TimeSpan? postTimeout = null)
|
||||
{
|
||||
Func<ReadOnlyCollection<WindowsElement>> result = () => QueryWindows(marked);
|
||||
return WaitForAtLeastOne(result, timeoutMessage, timeout, retryFrequency).Select(ToAppResult).ToArray();
|
||||
}
|
||||
|
||||
public AppWebResult[] WaitForElement(Func<AppQuery, AppWebQuery> query,
|
||||
string timeoutMessage = "Timed out waiting for element...",
|
||||
TimeSpan? timeout = null, TimeSpan? retryFrequency = null, TimeSpan? postTimeout = null)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void WaitForNoElement(Func<AppQuery, AppQuery> query,
|
||||
string timeoutMessage = "Timed out waiting for no element...",
|
||||
TimeSpan? timeout = null, TimeSpan? retryFrequency = null, TimeSpan? postTimeout = null)
|
||||
{
|
||||
Func<ReadOnlyCollection<WindowsElement>> result = () => QueryWindows(query);
|
||||
WaitForNone(result, timeoutMessage, timeout, retryFrequency);
|
||||
}
|
||||
|
||||
public void WaitForNoElement(string marked, string timeoutMessage = "Timed out waiting for no element...",
|
||||
TimeSpan? timeout = null, TimeSpan? retryFrequency = null, TimeSpan? postTimeout = null)
|
||||
{
|
||||
Func<ReadOnlyCollection<WindowsElement>> result = () => QueryWindows(marked);
|
||||
WaitForNone(result, timeoutMessage, timeout, retryFrequency);
|
||||
}
|
||||
|
||||
public void WaitForNoElement(Func<AppQuery, AppWebQuery> query,
|
||||
string timeoutMessage = "Timed out waiting for no element...",
|
||||
TimeSpan? timeout = null, TimeSpan? retryFrequency = null, TimeSpan? postTimeout = null)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void ContextClick(string marked)
|
||||
{
|
||||
WindowsElement element = QueryWindows(marked).First();
|
||||
PointF point = ElementToClickablePoint(element);
|
||||
|
||||
MouseClickAt(point.X, point.Y, ClickType.ContextClick);
|
||||
}
|
||||
|
||||
internal void MouseClickAt(float x, float y, ClickType clickType = ClickType.SingleClick)
|
||||
{
|
||||
// Mouse clicking with ICoordinates doesn't work the way we'd like (see TapCoordinates comments),
|
||||
// so we have to do some math on our own to get the mouse in the right spot
|
||||
|
||||
// So here's how we're working around it for the moment:
|
||||
// 1. Get the Window viewport (which is a known-to-exist element)
|
||||
// 2. Using the Window's ICoordinates and the MouseMove() overload with x/y offsets, move the pointer
|
||||
// to the location we care about
|
||||
// 3. Use the (undocumented, except in https://github.com/Microsoft/WinAppDriver/issues/118#issuecomment-269404335)
|
||||
// null parameter for Mouse.Click() to click at the current pointer location
|
||||
|
||||
WindowsElement viewPort = GetViewPort();
|
||||
int xOffset = viewPort.Coordinates.LocationInViewport.X;
|
||||
int yOffset = viewPort.Coordinates.LocationInViewport.Y;
|
||||
_session.Mouse.MouseMove(viewPort.Coordinates, (int)x - xOffset, (int)y - yOffset);
|
||||
|
||||
switch (clickType)
|
||||
{
|
||||
case ClickType.DoubleClick:
|
||||
_session.Mouse.DoubleClick(null);
|
||||
break;
|
||||
case ClickType.ContextClick:
|
||||
_session.Mouse.ContextClick(null);
|
||||
break;
|
||||
case ClickType.SingleClick:
|
||||
default:
|
||||
_session.Mouse.Click(null);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void ClickOrTapElement(WindowsElement element)
|
||||
{
|
||||
try
|
||||
{
|
||||
// For most stuff, a simple click will work
|
||||
element.Click();
|
||||
}
|
||||
catch (InvalidOperationException)
|
||||
{
|
||||
// Some elements aren't "clickable" from an automation perspective (e.g., Frame renders as a Border
|
||||
// with content in it; if the content is just a TextBlock, we'll end up here)
|
||||
|
||||
// All is not lost; we can figure out the location of the element in in the application window
|
||||
// and Tap in that spot
|
||||
PointF p = ElementToClickablePoint(element);
|
||||
TapCoordinates(p.X, p.Y);
|
||||
}
|
||||
}
|
||||
|
||||
void DoubleClickElement(WindowsElement element)
|
||||
{
|
||||
PointF point = ElementToClickablePoint(element);
|
||||
|
||||
MouseClickAt(point.X, point.Y, clickType: ClickType.DoubleClick);
|
||||
}
|
||||
|
||||
void DoubleTap(WinQuery query)
|
||||
{
|
||||
WindowsElement element = FindFirstElement(query);
|
||||
|
||||
if (element == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
DoubleClickElement(element);
|
||||
}
|
||||
|
||||
PointF ElementToClickablePoint(WindowsElement element)
|
||||
{
|
||||
PointF clickablePoint = GetClickablePoint(element);
|
||||
|
||||
WindowsElement window = GetWindow();
|
||||
PointF origin = GetOriginOfBoundingRectangle(window);
|
||||
|
||||
// Use the coordinates in the app window's viewport relative to the window's origin
|
||||
return new PointF(clickablePoint.X - origin.X, clickablePoint.Y - origin.Y);
|
||||
}
|
||||
|
||||
ReadOnlyCollection<WindowsElement> FilterControlType(IEnumerable<WindowsElement> elements, string controlType)
|
||||
{
|
||||
string tag = controlType;
|
||||
|
||||
if (tag == "*")
|
||||
{
|
||||
return new ReadOnlyCollection<WindowsElement>(elements.ToList());
|
||||
}
|
||||
|
||||
if (_controlNameToTag.ContainsKey(controlType))
|
||||
{
|
||||
tag = _controlNameToTag[controlType];
|
||||
}
|
||||
|
||||
return new ReadOnlyCollection<WindowsElement>(elements.Where(element => element.TagName == tag).ToList());
|
||||
}
|
||||
|
||||
WindowsElement FindFirstElement(WinQuery query)
|
||||
{
|
||||
Func<ReadOnlyCollection<WindowsElement>> fquery = () => QueryWindows(query);
|
||||
|
||||
string timeoutMessage = $"Timed out waiting for element: {query.Raw}";
|
||||
|
||||
ReadOnlyCollection<WindowsElement> results = WaitForAtLeastOne(fquery, timeoutMessage);
|
||||
|
||||
WindowsElement element = results.FirstOrDefault();
|
||||
|
||||
return element;
|
||||
}
|
||||
|
||||
static PointF GetBottomRightOfBoundingRectangle(WindowsElement element)
|
||||
{
|
||||
string vpcpString = element.GetAttribute("BoundingRectangle");
|
||||
|
||||
// returned string format looks like:
|
||||
// Left:-1868 Top:382 Width:1013 Height:680
|
||||
|
||||
string[] vpparts = vpcpString.Split(new[] { ':', ' ' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
float vpx = float.Parse(vpparts[1]);
|
||||
float vpy = float.Parse(vpparts[3]);
|
||||
|
||||
float vpw = float.Parse(vpparts[5]);
|
||||
float vph = float.Parse(vpparts[7]);
|
||||
|
||||
return new PointF(vpx + vpw, vpy + vph);
|
||||
}
|
||||
|
||||
static PointF GetClickablePoint(WindowsElement element)
|
||||
{
|
||||
string cpString = element.GetAttribute("ClickablePoint");
|
||||
string[] parts = cpString.Split(',');
|
||||
float x = float.Parse(parts[0]);
|
||||
float y = float.Parse(parts[1]);
|
||||
|
||||
return new PointF(x, y);
|
||||
}
|
||||
|
||||
static PointF GetOriginOfBoundingRectangle(WindowsElement element)
|
||||
{
|
||||
string vpcpString = element.GetAttribute("BoundingRectangle");
|
||||
|
||||
// returned string format looks like:
|
||||
// Left:-1868 Top:382 Width:1013 Height:680
|
||||
|
||||
string[] vpparts = vpcpString.Split(new[] { ':', ' ' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
float vpx = float.Parse(vpparts[1]);
|
||||
float vpy = float.Parse(vpparts[3]);
|
||||
|
||||
return new PointF(vpx, vpy);
|
||||
}
|
||||
|
||||
static PointF GetTopRightOfBoundingRectangle(WindowsElement element)
|
||||
{
|
||||
string vpcpString = element.GetAttribute("BoundingRectangle");
|
||||
|
||||
// returned string format looks like:
|
||||
// Left:-1868 Top:382 Width:1013 Height:680
|
||||
|
||||
string[] vpparts = vpcpString.Split(new[] { ':', ' ' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
float vpx = float.Parse(vpparts[1]);
|
||||
float vpy = float.Parse(vpparts[3]);
|
||||
|
||||
float vpw = float.Parse(vpparts[5]);
|
||||
|
||||
return new PointF(vpx + vpw, vpy);
|
||||
}
|
||||
|
||||
WindowsElement GetViewPort()
|
||||
{
|
||||
if (_viewPort != null)
|
||||
{
|
||||
return _viewPort;
|
||||
}
|
||||
|
||||
ReadOnlyCollection<WindowsElement> candidates = QueryWindows(AppName);
|
||||
_viewPort = candidates[3]; // We really just want the viewport; skip the full window, title bar, min/max buttons...
|
||||
|
||||
int xOffset = _viewPort.Coordinates.LocationInViewport.X;
|
||||
|
||||
if (xOffset > 1) // Everything having to do with scrolling right now is a horrid kludge
|
||||
{
|
||||
// This makes the scrolling stuff work correctly on a higher density screen (e.g. MBP running Windows)
|
||||
_scrollBarOffset = -70;
|
||||
}
|
||||
|
||||
return _viewPort;
|
||||
}
|
||||
|
||||
WindowsElement GetWindow()
|
||||
{
|
||||
if (_window != null)
|
||||
{
|
||||
return _window;
|
||||
}
|
||||
|
||||
_window = QueryWindows(AppName)[0];
|
||||
return _window;
|
||||
}
|
||||
|
||||
void OriginMouse()
|
||||
{
|
||||
WindowsElement viewPort = GetViewPort();
|
||||
int xOffset = viewPort.Coordinates.LocationInViewport.X;
|
||||
int yOffset = viewPort.Coordinates.LocationInViewport.Y;
|
||||
_session.Mouse.MouseMove(viewPort.Coordinates, xOffset, yOffset);
|
||||
}
|
||||
|
||||
ReadOnlyCollection<WindowsElement> QueryWindows(WinQuery query)
|
||||
{
|
||||
ReadOnlyCollection<WindowsElement> resultByAccessibilityId = _session.FindElementsByAccessibilityId(query.Marked);
|
||||
ReadOnlyCollection<WindowsElement> resultByName = _session.FindElementsByName(query.Marked);
|
||||
|
||||
IEnumerable<WindowsElement> result = resultByAccessibilityId.Concat(resultByName);
|
||||
|
||||
// TODO hartez 2017/10/30 09:47:44 Should this be == "*" || == "TextBox"?
|
||||
// what about other controls where we might be looking by content? TextBlock?
|
||||
if (query.ControlType == "*")
|
||||
{
|
||||
IEnumerable<WindowsElement> textBoxesByContent =
|
||||
_session.FindElementsByClassName("TextBox").Where(e => e.Text == query.Marked);
|
||||
result = result.Concat(textBoxesByContent);
|
||||
}
|
||||
|
||||
return FilterControlType(result, query.ControlType);
|
||||
}
|
||||
|
||||
ReadOnlyCollection<WindowsElement> QueryWindows(string marked)
|
||||
{
|
||||
WinQuery winQuery = WinQuery.FromMarked(marked);
|
||||
return QueryWindows(winQuery);
|
||||
}
|
||||
|
||||
ReadOnlyCollection<WindowsElement> QueryWindows(Func<AppQuery, AppQuery> query)
|
||||
{
|
||||
WinQuery winQuery = WinQuery.FromQuery(query);
|
||||
return QueryWindows(winQuery);
|
||||
}
|
||||
|
||||
void Scroll(WinQuery query, bool down)
|
||||
{
|
||||
if (query == null)
|
||||
{
|
||||
ScrollClick(GetWindow(), down);
|
||||
return;
|
||||
}
|
||||
|
||||
WindowsElement element = FindFirstElement(query);
|
||||
|
||||
ScrollClick(element, down);
|
||||
}
|
||||
|
||||
void ScrollClick(WindowsElement element, bool down = true)
|
||||
{
|
||||
PointF point = down ? GetBottomRightOfBoundingRectangle(element) : GetTopRightOfBoundingRectangle(element);
|
||||
|
||||
PointF origin = GetOriginOfBoundingRectangle(GetWindow());
|
||||
|
||||
var realPoint = new PointF(point.X - origin.X, point.Y - origin.Y);
|
||||
|
||||
int xOffset = _scrollBarOffset;
|
||||
if (origin.X < 0)
|
||||
{
|
||||
// The scrollbar's in a slightly different place relative to the window bounds
|
||||
// if we're running on the left monitor (which I like to do)
|
||||
xOffset = xOffset * 3;
|
||||
}
|
||||
|
||||
float finalX = realPoint.X - xOffset;
|
||||
float finalY = realPoint.Y - (down ? 15 : -15);
|
||||
|
||||
OriginMouse();
|
||||
MouseClickAt(finalX, finalY, ClickType.SingleClick);
|
||||
}
|
||||
|
||||
void ScrollTo(WinQuery toQuery, WinQuery withinQuery, TimeSpan? timeout = null, bool down = true)
|
||||
{
|
||||
timeout = timeout ?? TimeSpan.FromSeconds(5);
|
||||
DateTime start = DateTime.Now;
|
||||
|
||||
while (true)
|
||||
{
|
||||
Func<ReadOnlyCollection<WindowsElement>> result = () => QueryWindows(toQuery);
|
||||
TimeSpan iterationTimeout = TimeSpan.FromMilliseconds(0);
|
||||
TimeSpan retryFrequency = TimeSpan.FromMilliseconds(0);
|
||||
|
||||
try
|
||||
{
|
||||
ReadOnlyCollection<WindowsElement> found = WaitForAtLeastOne(result, timeoutMessage: null,
|
||||
timeout: iterationTimeout, retryFrequency: retryFrequency);
|
||||
|
||||
if (found.Count > 0)
|
||||
{
|
||||
// Success
|
||||
return;
|
||||
}
|
||||
}
|
||||
catch (TimeoutException ex)
|
||||
{
|
||||
// Haven't found it yet, keep scrolling
|
||||
}
|
||||
|
||||
long elapsed = DateTime.Now.Subtract(start).Ticks;
|
||||
if (elapsed >= timeout.Value.Ticks)
|
||||
{
|
||||
Debug.WriteLine($">>>>> {elapsed} ticks elapsed, timeout value is {timeout.Value.Ticks}");
|
||||
throw new TimeoutException($"Timed out scrolling to {toQuery}");
|
||||
}
|
||||
|
||||
Scroll(withinQuery, down);
|
||||
}
|
||||
}
|
||||
|
||||
void Tap(WinQuery query)
|
||||
{
|
||||
WindowsElement element = FindFirstElement(query);
|
||||
|
||||
if (element == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ClickOrTapElement(element);
|
||||
}
|
||||
|
||||
static AppRect ToAppRect(WindowsElement windowsElement)
|
||||
{
|
||||
try
|
||||
{
|
||||
var result = new AppRect
|
||||
{
|
||||
X = windowsElement.Location.X,
|
||||
Y = windowsElement.Location.Y,
|
||||
Height = windowsElement.Size.Height,
|
||||
Width = windowsElement.Size.Width
|
||||
};
|
||||
|
||||
result.CenterX = result.X + result.Width / 2;
|
||||
result.CenterY = result.Y + result.Height / 2;
|
||||
|
||||
return result;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine(
|
||||
$"Warning: error determining AppRect for {windowsElement}; "
|
||||
+ $"if this is a Label with a modified Text value, it might be confusing Windows automation. " +
|
||||
$"{ex}");
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
static AppResult ToAppResult(WindowsElement windowsElement)
|
||||
{
|
||||
return new AppResult
|
||||
{
|
||||
Rect = ToAppRect(windowsElement),
|
||||
Label = windowsElement.Id, // Not entirely sure about this one
|
||||
Description = windowsElement.Text, // or this one
|
||||
Enabled = windowsElement.Enabled,
|
||||
Id = windowsElement.Id
|
||||
};
|
||||
}
|
||||
|
||||
static ReadOnlyCollection<WindowsElement> Wait(Func<ReadOnlyCollection<WindowsElement>> query,
|
||||
Func<int, bool> satisfactory,
|
||||
string timeoutMessage = null,
|
||||
TimeSpan? timeout = null, TimeSpan? retryFrequency = null)
|
||||
{
|
||||
timeout = timeout ?? TimeSpan.FromSeconds(5);
|
||||
retryFrequency = retryFrequency ?? TimeSpan.FromMilliseconds(500);
|
||||
timeoutMessage = timeoutMessage ?? "Timed out on query.";
|
||||
|
||||
DateTime start = DateTime.Now;
|
||||
|
||||
ReadOnlyCollection<WindowsElement> result = query();
|
||||
|
||||
while (!satisfactory(result.Count))
|
||||
{
|
||||
long elapsed = DateTime.Now.Subtract(start).Ticks;
|
||||
if (elapsed >= timeout.Value.Ticks)
|
||||
{
|
||||
Debug.WriteLine($">>>>> {elapsed} ticks elapsed, timeout value is {timeout.Value.Ticks}");
|
||||
|
||||
throw new TimeoutException(timeoutMessage);
|
||||
}
|
||||
|
||||
Task.Delay(retryFrequency.Value.Milliseconds).Wait();
|
||||
result = query();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static ReadOnlyCollection<WindowsElement> WaitForAtLeastOne(Func<ReadOnlyCollection<WindowsElement>> query,
|
||||
string timeoutMessage = null,
|
||||
TimeSpan? timeout = null, TimeSpan? retryFrequency = null)
|
||||
{
|
||||
return Wait(query, i => i > 0, timeoutMessage, timeout, retryFrequency);
|
||||
}
|
||||
|
||||
void WaitForNone(Func<ReadOnlyCollection<WindowsElement>> query,
|
||||
string timeoutMessage = null,
|
||||
TimeSpan? timeout = null, TimeSpan? retryFrequency = null)
|
||||
{
|
||||
Wait(query, i => i == 0, timeoutMessage, timeout, retryFrequency);
|
||||
}
|
||||
|
||||
internal enum ClickType
|
||||
{
|
||||
SingleClick,
|
||||
DoubleClick,
|
||||
ContextClick
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,67 @@
|
|||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Text.RegularExpressions;
|
||||
using Xamarin.UITest.Queries;
|
||||
|
||||
namespace Xamarin.Forms.Core.UITests
|
||||
{
|
||||
internal class WinQuery
|
||||
{
|
||||
public static WinQuery FromQuery(Func<AppQuery, AppQuery> query)
|
||||
{
|
||||
var raw = GetRawQuery(query);
|
||||
return FromRaw(raw);
|
||||
}
|
||||
|
||||
public static WinQuery FromMarked(string marked)
|
||||
{
|
||||
return new WinQuery("*", marked, $"* '{marked}'");
|
||||
}
|
||||
|
||||
public static WinQuery FromRaw(string raw)
|
||||
{
|
||||
Debug.WriteLine($">>>>> Converting raw query '{raw}' to {nameof(WinQuery)}");
|
||||
|
||||
var match = Regex.Match(raw, @"(.*)\s(marked|text):'((.|\n)*)'");
|
||||
|
||||
var controlType = match.Groups[1].Captures[0].Value;
|
||||
var marked = match.Groups[3].Captures[0].Value;
|
||||
|
||||
// Just ignoring everything else for now (parent, index statements, etc)
|
||||
var result = new WinQuery(controlType, marked, raw);
|
||||
|
||||
Debug.WriteLine($">>>>> WinQuery is: {result}");
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static string GetRawQuery(Func<AppQuery, AppQuery> query = null)
|
||||
{
|
||||
if (query == null)
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
// When we pull out the iOS query it's got any instances of "'" escaped with "\", need to fix that up
|
||||
return query(new AppQuery(QueryPlatform.iOS)).ToString().Replace("\\'", "'");
|
||||
}
|
||||
|
||||
WinQuery(string controlType, string marked, string raw)
|
||||
{
|
||||
ControlType = controlType;
|
||||
Marked = marked;
|
||||
Raw = raw;
|
||||
}
|
||||
|
||||
public string ControlType { get; }
|
||||
|
||||
public string Marked { get; }
|
||||
|
||||
public string Raw { get; }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"{nameof(ControlType)}: {ControlType}, {nameof(Marked)}: {Marked}";
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,14 +1,7 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Diagnostics;
|
||||
using System.Drawing.Imaging;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Resources;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
using NUnit.Framework;
|
||||
using OpenQA.Selenium;
|
||||
using OpenQA.Selenium.Appium.MultiTouch;
|
||||
|
@ -17,7 +10,6 @@ using OpenQA.Selenium.Interactions.Internal;
|
|||
using OpenQA.Selenium.Remote;
|
||||
using Xamarin.Forms.Xaml;
|
||||
using Xamarin.UITest;
|
||||
using Xamarin.UITest.Queries;
|
||||
|
||||
namespace Xamarin.Forms.Core.UITests
|
||||
{
|
||||
|
@ -35,13 +27,21 @@ namespace Xamarin.Forms.Core.UITests
|
|||
appCapabilities.SetCapability("deviceName", "WindowsPC");
|
||||
Session = new WindowsDriver<WindowsElement>(new Uri(WindowsApplicationDriverUrl), appCapabilities);
|
||||
Assert.IsNotNull(Session);
|
||||
Session.Manage().Timeouts().ImplicitlyWait(TimeSpan.FromSeconds(2));
|
||||
Session.Manage().Timeouts().ImplicitlyWait(TimeSpan.FromSeconds(1));
|
||||
Reset();
|
||||
}
|
||||
|
||||
return new WinDriverApp(Session);
|
||||
}
|
||||
|
||||
internal static void HandleAppClosed(Exception ex)
|
||||
{
|
||||
if (ex is InvalidOperationException && ex.Message == "Currently selected window has been closed")
|
||||
{
|
||||
Session = null;
|
||||
}
|
||||
}
|
||||
|
||||
public static void Reset()
|
||||
{
|
||||
try
|
||||
|
@ -50,706 +50,10 @@ namespace Xamarin.Forms.Core.UITests
|
|||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
HandleAppClosed(ex);
|
||||
Debug.WriteLine($">>>>> WindowsTestBase ConfigureApp 49: {ex}");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class WinDriverApp : IApp
|
||||
{
|
||||
readonly WindowsDriver<WindowsElement> _session;
|
||||
protected static RemoteTouchScreen _touchScreen;
|
||||
|
||||
public WinDriverApp(WindowsDriver<WindowsElement> session)
|
||||
{
|
||||
_session = session;
|
||||
_touchScreen = new RemoteTouchScreen(session);
|
||||
}
|
||||
|
||||
readonly Dictionary<string, string> _controlNameToTag = new Dictionary<string, string>
|
||||
{
|
||||
{"button", "ControlType.Button"}
|
||||
};
|
||||
|
||||
ReadOnlyCollection<WindowsElement> FilterControlType(IEnumerable<WindowsElement> elements, string controlType)
|
||||
{
|
||||
var tag = controlType;
|
||||
|
||||
if (tag == "*")
|
||||
{
|
||||
return new ReadOnlyCollection<WindowsElement>(elements.ToList());
|
||||
}
|
||||
|
||||
if (_controlNameToTag.ContainsKey(controlType))
|
||||
{
|
||||
tag = _controlNameToTag[controlType];
|
||||
}
|
||||
|
||||
return new ReadOnlyCollection<WindowsElement>(elements.Where(element => element.TagName == tag).ToList());
|
||||
}
|
||||
|
||||
class WinQuery
|
||||
{
|
||||
public static WinQuery FromQuery(Func<AppQuery, AppQuery> query)
|
||||
{
|
||||
var raw = GetRawQuery(query);
|
||||
return FromRaw(raw);
|
||||
}
|
||||
|
||||
public static WinQuery FromMarked(string marked)
|
||||
{
|
||||
return new WinQuery("*", marked, $"* '{marked}'");
|
||||
}
|
||||
|
||||
public static WinQuery FromRaw(string raw)
|
||||
{
|
||||
Debug.WriteLine($">>>>> Converting raw query '{raw}' to {nameof(WinQuery)}");
|
||||
|
||||
var match = Regex.Match(raw, @"(.*)\s(marked|text):'(.*)'");
|
||||
|
||||
var controlType = match.Groups[1].Captures[0].Value;
|
||||
var marked = match.Groups[3].Captures[0].Value;
|
||||
|
||||
// Just ignoring everything else for now (parent, index statements, etc)
|
||||
|
||||
return new WinQuery(controlType, marked, raw);
|
||||
}
|
||||
|
||||
static string GetRawQuery(Func<AppQuery, AppQuery> query = null)
|
||||
{
|
||||
if (query == null)
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
return query(new AppQuery(QueryPlatform.iOS)).ToString();
|
||||
}
|
||||
|
||||
WinQuery(string controlType, string marked, string raw)
|
||||
{
|
||||
ControlType = controlType;
|
||||
Marked = marked;
|
||||
Raw = raw;
|
||||
}
|
||||
|
||||
public string ControlType { get; }
|
||||
|
||||
public string Marked { get; }
|
||||
|
||||
public string Raw { get; }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"{nameof(ControlType)}: {ControlType}, {nameof(Marked)}: {Marked}";
|
||||
}
|
||||
}
|
||||
|
||||
ReadOnlyCollection<WindowsElement> QueryWindows(WinQuery query)
|
||||
{
|
||||
var resultByName = _session.FindElementsByName(query.Marked);
|
||||
var resultByAccessibilityId = _session.FindElementsByAccessibilityId(query.Marked);
|
||||
|
||||
var result = resultByName
|
||||
.Concat(resultByAccessibilityId);
|
||||
|
||||
if (query.ControlType == "*")
|
||||
{
|
||||
var textBoxesByContent = _session.FindElementsByClassName("TextBox").Where(e => e.Text == query.Marked);
|
||||
result = result.Concat(textBoxesByContent);
|
||||
}
|
||||
|
||||
return FilterControlType(result, query.ControlType);
|
||||
}
|
||||
|
||||
ReadOnlyCollection<WindowsElement> QueryWindows(string marked)
|
||||
{
|
||||
var winQuery = WinQuery.FromMarked(marked);
|
||||
return QueryWindows(winQuery);
|
||||
}
|
||||
|
||||
ReadOnlyCollection<WindowsElement> QueryWindows(Func<AppQuery, AppQuery> query)
|
||||
{
|
||||
var winQuery = WinQuery.FromQuery(query);
|
||||
return QueryWindows(winQuery);
|
||||
}
|
||||
|
||||
static AppRect ToAppRect(WindowsElement windowsElement)
|
||||
{
|
||||
var result = new AppRect
|
||||
{
|
||||
X = windowsElement.Location.X,
|
||||
Y = windowsElement.Location.Y,
|
||||
Height = windowsElement.Size.Height,
|
||||
Width = windowsElement.Size.Width
|
||||
};
|
||||
|
||||
result.CenterX = result.X + result.Width / 2;
|
||||
result.CenterY = result.Y + result.Height / 2;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static AppResult ToAppResult(WindowsElement windowsElement)
|
||||
{
|
||||
return new AppResult
|
||||
{
|
||||
Rect = ToAppRect(windowsElement),
|
||||
Label = windowsElement.Id, // Not entirely sure about this one
|
||||
Description = windowsElement.Text, // or this one
|
||||
Enabled = windowsElement.Enabled,
|
||||
Id = windowsElement.Id
|
||||
};
|
||||
}
|
||||
|
||||
public AppResult[] Query(Func<AppQuery, AppQuery> query = null)
|
||||
{
|
||||
var elements = QueryWindows(WinQuery.FromQuery(query));
|
||||
return elements.Select(ToAppResult).ToArray();
|
||||
}
|
||||
|
||||
public AppResult[] Query(string marked)
|
||||
{
|
||||
var elements = QueryWindows(marked);
|
||||
return elements.Select(ToAppResult).ToArray();
|
||||
}
|
||||
|
||||
public AppWebResult[] Query(Func<AppQuery, AppWebQuery> query)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
readonly Dictionary<string, string> _translatePropertyAccessor = new Dictionary<string, string>
|
||||
{
|
||||
{"getAlpha", "Opacity"}
|
||||
};
|
||||
|
||||
public T[] Query<T>(Func<AppQuery, AppTypedSelector<T>> query)
|
||||
{
|
||||
var appTypedSelector = query(new AppQuery(QueryPlatform.iOS));
|
||||
|
||||
// Swiss-Army Chainsaw time
|
||||
// We'll use reflection to dig into the query and get the element selector
|
||||
// and the property value invocation in text form
|
||||
var bindingFlags = System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic;
|
||||
var selectorType = appTypedSelector.GetType();
|
||||
var tokensProperty = selectorType.GetProperties(bindingFlags)
|
||||
.First(t => t.PropertyType == typeof(Xamarin.UITest.Queries.Tokens.IQueryToken[]));
|
||||
|
||||
var tokens = (Xamarin.UITest.Queries.Tokens.IQueryToken[])tokensProperty.GetValue(appTypedSelector);
|
||||
|
||||
// Output some debugging info
|
||||
//foreach (var t in tokens)
|
||||
//{
|
||||
// Debug.WriteLine($">>>>> WinDriverApp Query 208: {t.ToQueryString(QueryPlatform.iOS)}");
|
||||
// Debug.WriteLine($">>>>> WinDriverApp Query 208: {t.ToCodeString()}");
|
||||
//}
|
||||
|
||||
var selector = tokens[0].ToQueryString(QueryPlatform.iOS);
|
||||
var invoke = tokens[1].ToCodeString();
|
||||
|
||||
// Now that we have them in text form, we can reinterpret them for Windows
|
||||
var winQuery = WinQuery.FromRaw(selector);
|
||||
// TODO hartez 2017/07/19 17:08:44 Make this a bit more resilient if the translation isn't there
|
||||
var attribute = _translatePropertyAccessor[invoke.Substring(8).Replace("\")", "")];
|
||||
|
||||
var elements = QueryWindows(winQuery);
|
||||
|
||||
// TODO hartez 2017/07/19 17:09:14 Alas, for now this simply doesn't work. Waiting for WinAppDrive to implement it
|
||||
return elements.Select(e => (T)Convert.ChangeType(e.GetAttribute(attribute), typeof(T))).ToArray();
|
||||
}
|
||||
|
||||
public string[] Query(Func<AppQuery, InvokeJSAppQuery> query)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public AppResult[] Flash(Func<AppQuery, AppQuery> query = null)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public AppResult[] Flash(string marked)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void EnterText(string text)
|
||||
{
|
||||
_session.Keyboard.SendKeys(text);
|
||||
}
|
||||
|
||||
public void EnterText(Func<AppQuery, AppQuery> query, string text)
|
||||
{
|
||||
QueryWindows(query).First().SendKeys(text);
|
||||
}
|
||||
|
||||
public void EnterText(string marked, string text)
|
||||
{
|
||||
QueryWindows(marked).First().SendKeys(text);
|
||||
}
|
||||
|
||||
public void EnterText(Func<AppQuery, AppWebQuery> query, string text)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void ClearText(Func<AppQuery, AppQuery> query)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void ClearText(Func<AppQuery, AppWebQuery> query)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void ClearText(string marked)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void ClearText()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void PressEnter()
|
||||
{
|
||||
_session.Keyboard.PressKey(Keys.Enter);
|
||||
}
|
||||
|
||||
public void DismissKeyboard()
|
||||
{
|
||||
// No-op for Desktop, which is all we're doing right now
|
||||
}
|
||||
|
||||
public void Tap(Func<AppQuery, AppQuery> query)
|
||||
{
|
||||
var winQuery = WinQuery.FromQuery(query);
|
||||
Tap(winQuery);
|
||||
}
|
||||
|
||||
public void Tap(string marked)
|
||||
{
|
||||
var winQuery = WinQuery.FromMarked(marked);
|
||||
Tap(winQuery);
|
||||
}
|
||||
|
||||
void Tap(WinQuery query, int taps = 1)
|
||||
{
|
||||
Func<ReadOnlyCollection<WindowsElement>> fquery = () => QueryWindows(query);
|
||||
|
||||
var timeoutMessage = $"Timed out waiting for element: {query.Raw}";
|
||||
|
||||
var results = WaitForAtLeastOne(fquery, timeoutMessage);
|
||||
|
||||
if (results.Any())
|
||||
{
|
||||
var element = results.First();
|
||||
|
||||
for (int n = 0; n < taps; n++)
|
||||
{
|
||||
element.Click();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Tap(Func<AppQuery, AppWebQuery> query)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void TapCoordinates(float x, float y)
|
||||
{
|
||||
// Okay, this one's a bit complicated. For some reason, _session.Tap() with coordinates does not work
|
||||
// (Filed https://github.com/Microsoft/WinAppDriver/issues/229 for that)
|
||||
// But we can do the equivalent by manipulating the mouse. The mouse methods all take an ICoordinates
|
||||
// object, and you'd think that the "coordinates" part of ICoordinates would have something do with
|
||||
// where the mouse clicks. You'd be wrong. The coordinates parts of that object are ignored and it just
|
||||
// clicks the center of whatever WindowsElement the ICoordinates refers to in 'AuxiliaryLocator'
|
||||
|
||||
// If we could just use the element, we wouldn't be tapping at specific coordinates, so that's not
|
||||
// very helpful.
|
||||
|
||||
// So here's how we're working around it for the moment:
|
||||
// 1. Get the Window viewport (which is a known-to-exist element)
|
||||
// 2. Using the Window's ICoordinates and the MouseMove() overload with x/y offsets, move the pointer
|
||||
// to the location we care about
|
||||
// 3. Use the (undocumented, except in https://github.com/Microsoft/WinAppDriver/issues/118#issuecomment-269404335)
|
||||
// null parameter for Mouse.Click() to click at the current pointer location
|
||||
|
||||
var candidates = QueryWindows("Xamarin.Forms.ControlGallery.WindowsUniversal");
|
||||
var viewPort = candidates[3]; // We really just want the viewport; skip the full window, title bar, min/max buttons...
|
||||
var xOffset = viewPort.Coordinates.LocationInViewport.X;
|
||||
var yOffset = viewPort.Coordinates.LocationInViewport.Y;
|
||||
_session.Mouse.MouseMove(viewPort.Coordinates, (int)x - xOffset, (int)y - yOffset);
|
||||
_session.Mouse.Click(null);
|
||||
}
|
||||
|
||||
public void TouchAndHold(Func<AppQuery, AppQuery> query)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void TouchAndHold(string marked)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void TouchAndHoldCoordinates(float x, float y)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void DoubleTap(Func<AppQuery, AppQuery> query)
|
||||
{
|
||||
var winQuery = WinQuery.FromQuery(query);
|
||||
Tap(winQuery, 2);
|
||||
}
|
||||
|
||||
public void DoubleTap(string marked)
|
||||
{
|
||||
var winQuery = WinQuery.FromMarked(marked);
|
||||
Tap(winQuery, 2);
|
||||
}
|
||||
|
||||
public void DoubleTapCoordinates(float x, float y)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void PinchToZoomIn(Func<AppQuery, AppQuery> query, TimeSpan? duration = null)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void PinchToZoomIn(string marked, TimeSpan? duration = null)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void PinchToZoomInCoordinates(float x, float y, TimeSpan? duration)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void PinchToZoomOut(Func<AppQuery, AppQuery> query, TimeSpan? duration = null)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void PinchToZoomOut(string marked, TimeSpan? duration = null)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void PinchToZoomOutCoordinates(float x, float y, TimeSpan? duration)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void WaitFor(Func<bool> predicate, string timeoutMessage = "Timed out waiting...", TimeSpan? timeout = null,
|
||||
TimeSpan? retryFrequency = null, TimeSpan? postTimeout = null)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
ReadOnlyCollection<WindowsElement> WaitForAtLeastOne(Func<ReadOnlyCollection<WindowsElement>> query,
|
||||
string timeoutMessage = null,
|
||||
TimeSpan? timeout = null, TimeSpan? retryFrequency = null)
|
||||
{
|
||||
return Wait(query, i => i > 0, timeoutMessage, timeout, retryFrequency);
|
||||
}
|
||||
|
||||
void WaitForNone(Func<ReadOnlyCollection<WindowsElement>> query,
|
||||
string timeoutMessage = null,
|
||||
TimeSpan? timeout = null, TimeSpan? retryFrequency = null)
|
||||
{
|
||||
Wait(query, i => i == 0, timeoutMessage, timeout, retryFrequency);
|
||||
}
|
||||
|
||||
ReadOnlyCollection<WindowsElement> Wait(Func<ReadOnlyCollection<WindowsElement>> query,
|
||||
Func<int, bool> satisfactory,
|
||||
string timeoutMessage = null,
|
||||
TimeSpan? timeout = null, TimeSpan? retryFrequency = null)
|
||||
{
|
||||
timeout = timeout ?? TimeSpan.FromSeconds(5);
|
||||
retryFrequency = retryFrequency ?? TimeSpan.FromMilliseconds(500);
|
||||
timeoutMessage = timeoutMessage ?? "Timed out on query.";
|
||||
|
||||
var start = DateTime.Now;
|
||||
|
||||
var result = query();
|
||||
|
||||
while (!satisfactory(result.Count))
|
||||
{
|
||||
if (DateTime.Now.Subtract(start).Ticks >= timeout.Value.Ticks)
|
||||
{
|
||||
throw new TimeoutException(timeoutMessage);
|
||||
}
|
||||
|
||||
Task.Delay(retryFrequency.Value.Milliseconds).Wait();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public AppResult[] WaitForElement(Func<AppQuery, AppQuery> query, string timeoutMessage = "Timed out waiting for element...",
|
||||
TimeSpan? timeout = null, TimeSpan? retryFrequency = null, TimeSpan? postTimeout = null)
|
||||
{
|
||||
Func<ReadOnlyCollection<WindowsElement>> result = () => QueryWindows(query);
|
||||
return WaitForAtLeastOne(result, timeoutMessage, timeout, retryFrequency).Select(ToAppResult).ToArray();
|
||||
}
|
||||
|
||||
public AppResult[] WaitForElement(string marked, string timeoutMessage = "Timed out waiting for element...",
|
||||
TimeSpan? timeout = null, TimeSpan? retryFrequency = null, TimeSpan? postTimeout = null)
|
||||
{
|
||||
Func<ReadOnlyCollection<WindowsElement>> result = () => QueryWindows(marked);
|
||||
return WaitForAtLeastOne(result, timeoutMessage, timeout, retryFrequency).Select(ToAppResult).ToArray();
|
||||
}
|
||||
|
||||
public AppWebResult[] WaitForElement(Func<AppQuery, AppWebQuery> query, string timeoutMessage = "Timed out waiting for element...",
|
||||
TimeSpan? timeout = null, TimeSpan? retryFrequency = null, TimeSpan? postTimeout = null)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void WaitForNoElement(Func<AppQuery, AppQuery> query, string timeoutMessage = "Timed out waiting for no element...",
|
||||
TimeSpan? timeout = null, TimeSpan? retryFrequency = null, TimeSpan? postTimeout = null)
|
||||
{
|
||||
Func<ReadOnlyCollection<WindowsElement>> result = () => QueryWindows(query);
|
||||
WaitForNone(result, timeoutMessage, timeout, retryFrequency);
|
||||
}
|
||||
|
||||
public void WaitForNoElement(string marked, string timeoutMessage = "Timed out waiting for no element...",
|
||||
TimeSpan? timeout = null, TimeSpan? retryFrequency = null, TimeSpan? postTimeout = null)
|
||||
{
|
||||
Func<ReadOnlyCollection<WindowsElement>> result = () => QueryWindows(marked);
|
||||
WaitForNone(result, timeoutMessage, timeout, retryFrequency);
|
||||
}
|
||||
|
||||
public void WaitForNoElement(Func<AppQuery, AppWebQuery> query, string timeoutMessage = "Timed out waiting for no element...",
|
||||
TimeSpan? timeout = null, TimeSpan? retryFrequency = null, TimeSpan? postTimeout = null)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public FileInfo Screenshot(string title)
|
||||
{
|
||||
// TODO hartez 2017/07/18 10:16:56 Verify that this is working; seems a bit too simple
|
||||
var filename = $"{title}.png";
|
||||
|
||||
var screenshot = _session.GetScreenshot();
|
||||
screenshot.SaveAsFile(filename, ImageFormat.Png);
|
||||
return new FileInfo(filename);
|
||||
}
|
||||
|
||||
public void SwipeRight()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void SwipeLeftToRight(double swipePercentage = 0.67, int swipeSpeed = 500, bool withInertia = true)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void SwipeLeftToRight(string marked, double swipePercentage = 0.67, int swipeSpeed = 500, bool withInertia = true)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void SwipeLeft()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void SwipeRightToLeft(double swipePercentage = 0.67, int swipeSpeed = 500, bool withInertia = true)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void SwipeRightToLeft(string marked, double swipePercentage = 0.67, int swipeSpeed = 500, bool withInertia = true)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void SwipeLeftToRight(Func<AppQuery, AppQuery> query, double swipePercentage = 0.67, int swipeSpeed = 500, bool withInertia = true)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void SwipeLeftToRight(Func<AppQuery, AppWebQuery> query, double swipePercentage = 0.67, int swipeSpeed = 500, bool withInertia = true)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void SwipeRightToLeft(Func<AppQuery, AppQuery> query, double swipePercentage = 0.67, int swipeSpeed = 500, bool withInertia = true)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void SwipeRightToLeft(Func<AppQuery, AppWebQuery> query, double swipePercentage = 0.67, int swipeSpeed = 500, bool withInertia = true)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void ScrollUp(Func<AppQuery, AppQuery> query = null, ScrollStrategy strategy = ScrollStrategy.Auto, double swipePercentage = 0.67, int swipeSpeed = 500,
|
||||
bool withInertia = true)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void ScrollUp(string withinMarked, ScrollStrategy strategy = ScrollStrategy.Auto, double swipePercentage = 0.67, int swipeSpeed = 500,
|
||||
bool withInertia = true)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void ScrollDown(Func<AppQuery, AppQuery> withinQuery = null, ScrollStrategy strategy = ScrollStrategy.Auto, double swipePercentage = 0.67,
|
||||
int swipeSpeed = 500, bool withInertia = true)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void ScrollDown(string withinMarked, ScrollStrategy strategy = ScrollStrategy.Auto, double swipePercentage = 0.67,
|
||||
int swipeSpeed = 500, bool withInertia = true)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void ScrollTo(string toMarked, string withinMarked = null, ScrollStrategy strategy = ScrollStrategy.Auto,
|
||||
double swipePercentage = 0.67, int swipeSpeed = 500, bool withInertia = true, TimeSpan? timeout = null)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void ScrollUpTo(string toMarked, string withinMarked = null, ScrollStrategy strategy = ScrollStrategy.Auto,
|
||||
double swipePercentage = 0.67, int swipeSpeed = 500, bool withInertia = true, TimeSpan? timeout = null)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void ScrollUpTo(Func<AppQuery, AppWebQuery> toQuery, string withinMarked, ScrollStrategy strategy = ScrollStrategy.Auto, double swipePercentage = 0.67,
|
||||
int swipeSpeed = 500, bool withInertia = true, TimeSpan? timeout = null)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void ScrollDownTo(string toMarked, string withinMarked = null, ScrollStrategy strategy = ScrollStrategy.Auto,
|
||||
double swipePercentage = 0.67, int swipeSpeed = 500, bool withInertia = true, TimeSpan? timeout = null)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void ScrollDownTo(Func<AppQuery, AppWebQuery> toQuery, string withinMarked, ScrollStrategy strategy = ScrollStrategy.Auto, double swipePercentage = 0.67,
|
||||
int swipeSpeed = 500, bool withInertia = true, TimeSpan? timeout = null)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void ScrollUpTo(Func<AppQuery, AppQuery> toQuery, Func<AppQuery, AppQuery> withinQuery = null, ScrollStrategy strategy = ScrollStrategy.Auto, double swipePercentage = 0.67,
|
||||
int swipeSpeed = 500, bool withInertia = true, TimeSpan? timeout = null)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void ScrollUpTo(Func<AppQuery, AppWebQuery> toQuery, Func<AppQuery, AppQuery> withinQuery = null, ScrollStrategy strategy = ScrollStrategy.Auto, double swipePercentage = 0.67,
|
||||
int swipeSpeed = 500, bool withInertia = true, TimeSpan? timeout = null)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void ScrollDownTo(Func<AppQuery, AppQuery> toQuery, Func<AppQuery, AppQuery> withinQuery = null, ScrollStrategy strategy = ScrollStrategy.Auto, double swipePercentage = 0.67,
|
||||
int swipeSpeed = 500, bool withInertia = true, TimeSpan? timeout = null)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void ScrollDownTo(Func<AppQuery, AppWebQuery> toQuery, Func<AppQuery, AppQuery> withinQuery = null, ScrollStrategy strategy = ScrollStrategy.Auto, double swipePercentage = 0.67,
|
||||
int swipeSpeed = 500, bool withInertia = true, TimeSpan? timeout = null)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void SetOrientationPortrait()
|
||||
{
|
||||
// Deliberately leaving this as a no-op for now
|
||||
// Trying to set the orientation on the Desktop (the only version of UWP we're testing for the moment)
|
||||
// gives us a 405 Method Not Allowed, which makes sense. Haven't figured out how to determine
|
||||
// whether we're in a mode which allows orientation, but if we were, the next line is probably how to set it.
|
||||
//_session.Orientation = ScreenOrientation.Portrait;
|
||||
}
|
||||
|
||||
public void SetOrientationLandscape()
|
||||
{
|
||||
// Deliberately leaving this as a no-op for now
|
||||
// Trying to set the orientation on the Desktop (the only version of UWP we're testing for the moment)
|
||||
// gives us a 405 Method Not Allowed, which makes sense. Haven't figured out how to determine
|
||||
// whether we're in a mode which allows orientation, but if we were, the next line is probably how to set it.
|
||||
//_session.Orientation = ScreenOrientation.Landscape;
|
||||
}
|
||||
|
||||
public void Repl()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void Back()
|
||||
{
|
||||
QueryWindows("Back").First().Click();
|
||||
}
|
||||
|
||||
public void PressVolumeUp()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void PressVolumeDown()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public object Invoke(string methodName, object argument = null)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public object Invoke(string methodName, object[] arguments)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void DragCoordinates(float fromX, float fromY, float toX, float toY)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void DragAndDrop(Func<AppQuery, AppQuery> @from, Func<AppQuery, AppQuery> to)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void DragAndDrop(string @from, string to)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void SetSliderValue(string marked, double value)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void SetSliderValue(Func<AppQuery, AppQuery> query, double value)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public AppPrintHelper Print { get; }
|
||||
|
||||
public IDevice Device { get; }
|
||||
|
||||
public ITestServer TestServer { get; }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
using System;
|
||||
using OpenQA.Selenium.Appium.Windows;
|
||||
using Xamarin.UITest;
|
||||
|
||||
namespace Xamarin.Forms.Core.UITests
|
||||
{
|
||||
internal class WindowsTestServer : ITestServer
|
||||
{
|
||||
readonly WindowsDriver<WindowsElement> _session;
|
||||
|
||||
public WindowsTestServer(WindowsDriver<WindowsElement> session)
|
||||
{
|
||||
_session = session;
|
||||
}
|
||||
|
||||
public string Post(string endpoint, object arguments = null)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public string Put(string endpoint, byte[] data)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public string Get(string endpoint)
|
||||
{
|
||||
if (endpoint == "version")
|
||||
{
|
||||
try
|
||||
{
|
||||
return _session.CurrentWindowHandle;
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
WindowsTestBase.HandleAppClosed(exception);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
return endpoint;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -74,6 +74,9 @@
|
|||
<ItemGroup>
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="WindowsTestBase.cs" />
|
||||
<Compile Include="WindowsTestServer.cs" />
|
||||
<Compile Include="WinDriverApp.cs" />
|
||||
<Compile Include="WinQuery.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="packages.config" />
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
# Running the UI Tests on UWP
|
||||
|
||||
To run the tests you'll need to install the latest release of [WinAppDriver](https://github.com/Microsoft/WinAppDriver).
|
||||
|
||||
Make sure you've built and deployed the UWP version of ControlGallery on the machine where you're running the tests. Run WinAppDriver.exe (if you installed it in the default location, it will be "C:\Program Files (x86)\Windows Application Driver\WinAppDriver.exe").
|
||||
|
||||
With WinAppDriver running, start the tests with whatever test runner you normally use. The tests will take care of launching ControlGallery.
|
||||
|
||||
Now just sit back and watch the tests run. At the moment, WinAppDriver and our implementation of IApp for UWP have to take control of the mouse and keyboard to run these tests, so interacting with the test machine will probably interfere with the tests. (You may be able to work around this by running the tests in the Simulator; I've had mixed results and some blue screens when trying this, but good luck.)
|
||||
|
||||
Currently we can only run the tests against the desktop UWP application.
|
||||
|
|
@ -1,5 +1,8 @@
|
|||
using System.Threading.Tasks;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Automation.Peers;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using Windows.UI.Xaml.Media;
|
||||
using Xamarin.Forms.PlatformConfiguration.WindowsSpecific;
|
||||
|
@ -85,7 +88,35 @@ namespace Xamarin.Forms.Platform.WinRT
|
|||
#endif
|
||||
|
||||
TaskCompletionSource<CommandBar> tcs = _commandBarTcs;
|
||||
tcs?.SetResult(_commandBar);
|
||||
tcs?.SetResult(_commandBar);
|
||||
}
|
||||
|
||||
protected override DependencyObject GetContainerForItemOverride()
|
||||
{
|
||||
var containerItem = base.GetContainerForItemOverride();
|
||||
|
||||
var pivotItem = containerItem as PivotItem;
|
||||
|
||||
if (pivotItem != null)
|
||||
{
|
||||
// We need to know when the data context changes so we can set the automation name to the page title
|
||||
pivotItem.DataContextChanged += SetPivotItemAutomationName;
|
||||
}
|
||||
|
||||
return containerItem;
|
||||
}
|
||||
|
||||
static void SetPivotItemAutomationName(FrameworkElement frameworkElement,
|
||||
DataContextChangedEventArgs dataContextChangedEventArgs)
|
||||
{
|
||||
var pivotItem = frameworkElement as PivotItem;
|
||||
var page = dataContextChangedEventArgs.NewValue as Page;
|
||||
|
||||
if (pivotItem != null && page?.Title != null)
|
||||
{
|
||||
// This way we can find tabs with automation (for testing, etc.)
|
||||
Windows.UI.Xaml.Automation.AutomationProperties.SetName(pivotItem, page.Title);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -4,6 +4,7 @@ using System.Collections.Specialized;
|
|||
using System.ComponentModel;
|
||||
using Windows.UI.Input;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Automation.Peers;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using Windows.UI.Xaml.Controls.Primitives;
|
||||
using Windows.UI.Xaml.Input;
|
||||
|
@ -320,6 +321,11 @@ namespace Xamarin.Forms.Platform.WinRT
|
|||
((FrameworkElement)Content).DataContext = newCell;
|
||||
}
|
||||
|
||||
protected override AutomationPeer OnCreateAutomationPeer()
|
||||
{
|
||||
return new FrameworkElementAutomationPeer(this);
|
||||
}
|
||||
|
||||
void UpdateFlowDirection(Cell newCell)
|
||||
{
|
||||
if (newCell is ViewCell)
|
||||
|
|
|
@ -1,11 +1,14 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Threading.Tasks;
|
||||
using Windows.Foundation;
|
||||
using Windows.System;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Automation.Peers;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using Windows.UI.Xaml.Data;
|
||||
using Windows.UI.Xaml.Input;
|
||||
|
@ -692,5 +695,16 @@ namespace Xamarin.Forms.Platform.WinRT
|
|||
|
||||
bool _deferSelection = false;
|
||||
Tuple<object, SelectedItemChangedEventArgs> _deferredSelectedItemChangedEvent;
|
||||
|
||||
#if WINDOWS_UWP
|
||||
|
||||
protected override AutomationPeer OnCreateAutomationPeer()
|
||||
{
|
||||
return List == null
|
||||
? new FrameworkElementAutomationPeer(this)
|
||||
: new ListViewAutomationPeer(List);
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
}
|