From 3bc2e5e94b5be9e7ac89d331021432e6c3979eae Mon Sep 17 00:00:00 2001 From: "E.Z. Hart" Date: Tue, 4 Jun 2019 12:28:21 -0600 Subject: [PATCH] Speed up core UI tests and reduce flakiness (#6395) * Speed up ViewUITests and hopefully reduce flakiness * Make focus tests work even when keyboard pops up * Move Date/Time picker tests to the end so they can't break the other bubbling tests Add delay before trying to dismiss dialogs so they can fully appear * Fix accidental triggering of Picker when removing focus from Entry * Bump up number of tests before restart --- .../GestureBubblingTests.cs | 7 +- .../TestPages/TestPages.cs | 2 +- .../CoreGalleryPages/CoreGalleryPage.cs | 276 ++++++++++-------- .../Remotes/BaseViewContainerRemote.cs | 28 +- .../Tests/EntryUITests.cs | 19 +- 5 files changed, 180 insertions(+), 152 deletions(-) diff --git a/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/GestureBubblingTests.cs b/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/GestureBubblingTests.cs index a8238f863..661afe306 100644 --- a/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/GestureBubblingTests.cs +++ b/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/GestureBubblingTests.cs @@ -73,6 +73,7 @@ namespace Xamarin.Forms.Controls.Issues { // These controls show a pop-up which we have to cancel/done out of before we can continue #if __ANDROID__ + System.Threading.Tasks.Task.Delay(2000).Wait(); // If we hit back before the dialog is fully up, it may not dismiss RunningApp.Back(); #elif __IOS__ var cancelButtonText = "Done"; @@ -200,9 +201,6 @@ namespace Xamarin.Forms.Controls.Issues // We don't use 'SearchBar' here because on Android it sometimes finds the wrong control col1.Children.Add(MenuButton("TestSearchBar", () => new SearchBar())); - col2.Children.Add(MenuButton(nameof(DatePicker), () => new DatePicker())); - col2.Children.Add(MenuButton(nameof(TimePicker), () => new TimePicker())); - var slider = new Slider(); slider.On().SetUpdateOnTap(true); col2.Children.Add(MenuButton(nameof(Slider), () => slider)); @@ -211,6 +209,9 @@ namespace Xamarin.Forms.Controls.Issues col2.Children.Add(MenuButton(nameof(Stepper), () => new Stepper())); col2.Children.Add(MenuButton(nameof(BoxView), () => new BoxView { BackgroundColor = Color.DarkMagenta, WidthRequest = 100, HeightRequest = 100 })); + col2.Children.Add(MenuButton(nameof(DatePicker), () => new DatePicker())); + col2.Children.Add(MenuButton(nameof(TimePicker), () => new TimePicker())); + return new ContentPage { Content = layout }; } diff --git a/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/TestPages/TestPages.cs b/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/TestPages/TestPages.cs index 305698aba..958fd8fcd 100644 --- a/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/TestPages/TestPages.cs +++ b/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/TestPages/TestPages.cs @@ -250,7 +250,7 @@ namespace Xamarin.Forms.Controls } static int s_testsrun; - const int ConsecutiveTestLimit = 10; + const int ConsecutiveTestLimit = 20; // Until we get more of our memory leak issues worked out, restart the app // after a specified number of tests so we don't get bogged down in GC diff --git a/Xamarin.Forms.Controls/CoreGalleryPages/CoreGalleryPage.cs b/Xamarin.Forms.Controls/CoreGalleryPages/CoreGalleryPage.cs index 03584bdcd..e00770ad2 100644 --- a/Xamarin.Forms.Controls/CoreGalleryPages/CoreGalleryPage.cs +++ b/Xamarin.Forms.Controls/CoreGalleryPages/CoreGalleryPage.cs @@ -1,106 +1,76 @@ using System; -using System.Linq; using System.Collections.Generic; -using System.Linq.Expressions; -using System.Reflection; -using System.Threading; - +using System.Linq; using Xamarin.Forms.CustomAttributes; namespace Xamarin.Forms.Controls { internal class CoreGalleryPage : ContentPage - where T : View, new () + where T : View, new() { - List _views; + List> _viewContainers; + int _currentIndex; Picker _picker; - StackLayout _moveNextStack; - - ViewContainer _backgroundColorViewContainer; - ViewContainer _opacityViewContainer; - ViewContainer _rotationViewContainer; - ViewContainer _rotationXViewContainer; - ViewContainer _rotationYViewContainer; - ViewContainer _scaleViewContainer; - ViewContainer _translationXViewContainer; - ViewContainer _translationYViewContainer; - - StateViewContainer _focusStateViewContainer; - StateViewContainer _isFocusedStateViewContainer; - StateViewContainer _isVisibleStateViewContainer; - - EventViewContainer _gestureRecognizerEventViewContainer; - EventViewContainer _focusedEventViewContainer; - EventViewContainer _unfocusedEventViewContainer; - - LayeredViewContainer _inputTransparentViewContainer; + Entry _targetEntry; + StackLayout _layout; protected StateViewContainer IsEnabledStateViewContainer { get; private set; } protected StackLayout Layout { get; private set; } - internal CoreGalleryPage () + internal CoreGalleryPage() { - Layout = new StackLayout { - Padding = new Thickness (20) + Layout = new StackLayout + { + Padding = new Thickness(20) }; - var modalDismissButton = new Button () { + var modalDismissButton = new Button() + { Text = "Dismiss Page", - Command = new Command (async () => - { - if (_picker.SelectedIndex == 0) - await Navigation.PopAsync(); - else - _picker.SelectedIndex--; - }) + Command = new Command(async () => + { + if (_picker.SelectedIndex == 0) + { + await Navigation.PopAsync(); + } + else + { + _picker.SelectedIndex--; + } + }) }; - Layout.Children.Add (modalDismissButton); + Layout.Children.Add(modalDismissButton); - Build (Layout); + Build(Layout); Content = new ScrollView { AutomationId = "GalleryScrollView", Content = Layout }; - } - protected virtual void InitializeElement (T element) {} + protected virtual void InitializeElement(T element) { } - protected virtual void Build (StackLayout stackLayout) + protected virtual void Build(StackLayout stackLayout) { - var isFocusedView = new T (); - isFocusedView.SetValueCore (IsFocusedPropertyKey, true); + var isFocusedView = new T(); + isFocusedView.SetValueCore(IsFocusedPropertyKey, true); - var viewContainers = new[] { - _isFocusedStateViewContainer = new StateViewContainer (Test.VisualElement.IsFocused, isFocusedView), - _backgroundColorViewContainer = new ViewContainer (Test.VisualElement.BackgroundColor, new T { BackgroundColor = Color.Blue }), - _focusStateViewContainer = new StateViewContainer (Test.VisualElement.Focus, new T ()), - _gestureRecognizerEventViewContainer = new EventViewContainer (Test.View.GestureRecognizers, new T ()), - _inputTransparentViewContainer = new LayeredViewContainer (Test.VisualElement.InputTransparent, new T { InputTransparent = true }), - IsEnabledStateViewContainer = new StateViewContainer (Test.VisualElement.IsEnabled, new T { IsEnabled = true }), - _focusedEventViewContainer = new EventViewContainer (Test.VisualElement.Focused, new T ()), - _unfocusedEventViewContainer = new EventViewContainer (Test.VisualElement.Unfocused, new T ()), - _isVisibleStateViewContainer = new StateViewContainer (Test.VisualElement.IsVisible, new T { IsVisible = true }), - _opacityViewContainer = new ViewContainer (Test.VisualElement.Opacity, new T { Opacity = 0.5 }), - _rotationViewContainer = new ViewContainer (Test.VisualElement.Rotation, new T { Rotation = 10 }), - _rotationXViewContainer = new ViewContainer (Test.VisualElement.RotationX, new T { RotationX = 33 }), - _rotationYViewContainer = new ViewContainer (Test.VisualElement.RotationY, new T { RotationY = 10 }), - _scaleViewContainer = new ViewContainer (Test.VisualElement.Scale, new T { Scale = 0.5 }), - _translationXViewContainer = new ViewContainer (Test.VisualElement.TranslationX, new T { TranslationX = 30 }), - _translationYViewContainer = new ViewContainer (Test.VisualElement.TranslationY, new T { TranslationY = 30 }), - }; - - // Set state - IsEnabledStateViewContainer.StateChangeButton.Command = new Command (() => { + IsEnabledStateViewContainer = new StateViewContainer(Test.VisualElement.IsEnabled, new T { IsEnabled = true }); + IsEnabledStateViewContainer.StateChangeButton.Command = new Command(() => + { IsEnabledStateViewContainer.View.IsEnabled = !IsEnabledStateViewContainer.View.IsEnabled; }); - _isVisibleStateViewContainer.StateChangeButton.Command = new Command (() => { - _isVisibleStateViewContainer.View.IsVisible = !_isVisibleStateViewContainer.View.IsVisible; + + var isVisibleStateViewContainer = new StateViewContainer(Test.VisualElement.IsVisible, new T { IsVisible = true }); + isVisibleStateViewContainer.StateChangeButton.Command = new Command(() => + { + isVisibleStateViewContainer.View.IsVisible = !isVisibleStateViewContainer.View.IsVisible; }); + var isFocusedStateViewContainer = new StateViewContainer(Test.VisualElement.IsFocused, isFocusedView); + isFocusedStateViewContainer.StateChangeButton.Command = new Command(() => + { - _isFocusedStateViewContainer.StateChangeButton.Command = new Command(() => { - if ((bool)isFocusedView.GetValue(VisualElement.IsFocusedProperty)) { isFocusedView.SetValueCore(IsFocusedPropertyKey, false); @@ -111,81 +81,144 @@ namespace Xamarin.Forms.Controls } }); - _focusStateViewContainer.StateChangeButton.Command = new Command (() => { - if (_focusStateViewContainer.View.IsFocused) { - _focusStateViewContainer.View.Unfocus (); - } else { - _focusStateViewContainer.View.Focus (); + var focusStateViewContainer = new StateViewContainer(Test.VisualElement.Focus, new T()); + focusStateViewContainer.StateChangeButton.Command = new Command(() => + { + if (focusStateViewContainer.View.IsFocused) + { + focusStateViewContainer.View.Unfocus(); + } + else + { + focusStateViewContainer.View.Focus(); } }); - _focusedEventViewContainer.View.Focused += (sender, args) => _focusedEventViewContainer.EventFired (); - _unfocusedEventViewContainer.View.Unfocused += (sender, args) => _unfocusedEventViewContainer.EventFired (); + var focusedEventViewContainer = new EventViewContainer(Test.VisualElement.Focused, new T()); + focusedEventViewContainer.View.Focused += (sender, args) => focusedEventViewContainer.EventFired(); - _gestureRecognizerEventViewContainer.View.GestureRecognizers.Add ( - new TapGestureRecognizer { - Command = new Command (() => _gestureRecognizerEventViewContainer.EventFired ()) + var unfocusedEventViewContainer = new EventViewContainer(Test.VisualElement.Unfocused, new T()); + unfocusedEventViewContainer.View.Unfocused += (sender, args) => unfocusedEventViewContainer.EventFired(); + + var gestureRecognizerEventViewContainer = new EventViewContainer(Test.View.GestureRecognizers, new T()); + gestureRecognizerEventViewContainer.View.GestureRecognizers.Add( + new TapGestureRecognizer + { + Command = new Command(() => gestureRecognizerEventViewContainer.EventFired()) } ); - _views = new List (viewContainers.Select (o => o.ContainerLayout)); - - _moveNextStack = new StackLayout (); - var moveNextButton = new Button (); - moveNextButton.Text = "Move Next"; - moveNextButton.AutomationId = "MoveNextButton"; - moveNextButton.Clicked += delegate (object sender, EventArgs e) { - if (!_views.Any ()) - return; - - if (_currentIndex + 1 >= _views.Count) { - return; - } - - _currentIndex += 1; - - _moveNextStack.Children.RemoveAt (2); - _moveNextStack.Children.Add (_views[_currentIndex]); - _picker.SelectedIndexChanged -= PickerSelectedIndexChanged; - _picker.SelectedIndex = _currentIndex; - _picker.SelectedIndexChanged += PickerSelectedIndexChanged; + _viewContainers = new List> { + isFocusedStateViewContainer, + new ViewContainer (Test.VisualElement.BackgroundColor, new T { BackgroundColor = Color.Blue }), + focusStateViewContainer, + gestureRecognizerEventViewContainer, + new LayeredViewContainer (Test.VisualElement.InputTransparent, new T { InputTransparent = true }), + IsEnabledStateViewContainer, + focusedEventViewContainer, + unfocusedEventViewContainer, + isVisibleStateViewContainer, + new ViewContainer (Test.VisualElement.Opacity, new T { Opacity = 0.5 }), + new ViewContainer (Test.VisualElement.Rotation, new T { Rotation = 10 }), + new ViewContainer (Test.VisualElement.RotationX, new T { RotationX = 33 }), + new ViewContainer (Test.VisualElement.RotationY, new T { RotationY = 10 }), + new ViewContainer (Test.VisualElement.Scale, new T { Scale = 0.5 }), + new ViewContainer (Test.VisualElement.TranslationX, new T { TranslationX = 30 }), + new ViewContainer (Test.VisualElement.TranslationY, new T { TranslationY = 30 }), }; - + + _layout = new StackLayout(); + + _targetEntry = new Entry { AutomationId = "TargetViewContainer", Placeholder = "Jump To ViewContainer" }; + + var goButton = new Button + { + Text = "Go", + AutomationId = "GoButton" + }; + goButton.Clicked += GoClicked; + _picker = new Picker(); - foreach (var container in viewContainers) { + foreach (var container in _viewContainers) + { _picker.Items.Add(container.TitleLabel.Text); } - + _picker.SelectedIndex = _currentIndex; _picker.SelectedIndexChanged += PickerSelectedIndexChanged; - _moveNextStack.Children.Add (_picker); - _moveNextStack.Children.Add (moveNextButton); - _moveNextStack.Children.Add (_views[_currentIndex]); + _layout.Children.Add(_picker); + _layout.Children.Add(_targetEntry); + _layout.Children.Add(goButton); + _layout.Children.Add(_viewContainers[_currentIndex].ContainerLayout); - stackLayout.Children.Add (_moveNextStack); + stackLayout.Children.Add(_layout); - if (!SupportsFocus) { - stackLayout.Children.Remove (_focusStateViewContainer.ContainerLayout); - stackLayout.Children.Remove (_isFocusedStateViewContainer.ContainerLayout); + if (!SupportsFocus) + { + stackLayout.Children.Remove(focusStateViewContainer.ContainerLayout); + stackLayout.Children.Remove(isFocusedStateViewContainer.ContainerLayout); } if (!SupportsTapGestureRecognizer) - stackLayout.Children.Remove (_gestureRecognizerEventViewContainer.ContainerLayout); + { + stackLayout.Children.Remove(gestureRecognizerEventViewContainer.ContainerLayout); + } - foreach (var element in viewContainers) - InitializeElement (element.View); + foreach (var element in _viewContainers) + { + InitializeElement(element.View); + } } - void PickerSelectedIndexChanged (object sender, EventArgs eventArgs) + void GoClicked(object sender, EventArgs e) + { + if (!_viewContainers.Any()) + { + return; + } + + var target = _targetEntry.Text; + _targetEntry.Text = ""; + var index = -1; + + if (string.IsNullOrEmpty(target)) + { + return; + } + + for (int n = 0; n < _viewContainers.Count; n++) + { + if (_viewContainers[n].View.AutomationId == target) + { + index = n; + break; + } + } + + if (index < 0) + { + return; + } + + var targetContainer = _viewContainers[index]; + + _layout.Children.RemoveAt(3); + _layout.Children.Add(targetContainer.ContainerLayout); + + _picker.SelectedIndexChanged -= PickerSelectedIndexChanged; + _picker.SelectedIndex = index; + _picker.SelectedIndexChanged += PickerSelectedIndexChanged; + } + + void PickerSelectedIndexChanged(object sender, EventArgs eventArgs) { _currentIndex = _picker.SelectedIndex; - _moveNextStack.Children.RemoveAt (2); - _moveNextStack.Children.Add (_views[_currentIndex]); + _layout.Children.RemoveAt(3); + _layout.Children.Add(_viewContainers[_currentIndex].ContainerLayout); } - protected virtual bool SupportsTapGestureRecognizer { get { return true; } @@ -196,9 +229,10 @@ namespace Xamarin.Forms.Controls get { return true; } } - protected void Add (ViewContainer view) { - _views.Add (view.ContainerLayout); - _picker.Items.Add(view.TitleLabel.Text); + protected void Add(ViewContainer viewContainer) + { + _viewContainers.Add(viewContainer); + _picker.Items.Add(viewContainer.TitleLabel.Text); } } } \ No newline at end of file diff --git a/Xamarin.Forms.Core.UITests.Shared/Remotes/BaseViewContainerRemote.cs b/Xamarin.Forms.Core.UITests.Shared/Remotes/BaseViewContainerRemote.cs index ed6f67fbf..e4f377104 100644 --- a/Xamarin.Forms.Core.UITests.Shared/Remotes/BaseViewContainerRemote.cs +++ b/Xamarin.Forms.Core.UITests.Shared/Remotes/BaseViewContainerRemote.cs @@ -73,25 +73,11 @@ namespace Xamarin.Forms.Core.UITests Height = scrollBounds.Height, }; } - - while (true) - { -#if __MACOS__ - var result = App.Query(o => o.Raw(ViewQuery)); -#else - var result = App.Query (o => o.Raw(ContainerQuery)); -#endif - - if (result.Any()) - break; - App.Tap(o => o.Raw("* marked:'MoveNextButton'")); - } - - //Assert.True (App.ScrollForElement ( - // ContainerQuery, new Drag (scrollBounds, Drag.Direction.BottomToTop, Drag.DragLength.Medium) - //), "Failed to find element in: " + callerMemberName); - - App.Screenshot("Go to element"); + + App.WaitForElement("TargetViewContainer"); + App.Tap("TargetViewContainer"); + App.EnterText(callerMemberName.Replace("_", "") + "VisualElement"); + App.Tap("GoButton"); } public void TapView() @@ -160,7 +146,9 @@ namespace Xamarin.Forms.Core.UITests return result; } #endif - + + App.WaitForElement(q => q.Raw(query)); + bool found = MaybeGetProperty(App, query, propertyPath, out prop) || MaybeGetProperty(App, query, propertyPath, out prop) || MaybeGetProperty(App, query, propertyPath, out prop) || diff --git a/Xamarin.Forms.Core.UITests.Shared/Tests/EntryUITests.cs b/Xamarin.Forms.Core.UITests.Shared/Tests/EntryUITests.cs index 77060c33b..98b2820b2 100644 --- a/Xamarin.Forms.Core.UITests.Shared/Tests/EntryUITests.cs +++ b/Xamarin.Forms.Core.UITests.Shared/Tests/EntryUITests.cs @@ -1,6 +1,7 @@ using NUnit.Framework; using Xamarin.Forms.Controls.Issues; using Xamarin.Forms.CustomAttributes; +using Xamarin.UITest.Queries; namespace Xamarin.Forms.Core.UITests { @@ -25,14 +26,18 @@ namespace Xamarin.Forms.Core.UITests { var remote = new StateViewContainerRemote(App, Test.VisualElement.Focus, PlatformViewType); remote.GoTo(); - bool isFocused = System.Convert.ToBoolean( App.Query("FocusStateLabel")[0].ReadText()); - Assert.IsFalse(isFocused); + + Assert.IsFalse(IsFocused()); remote.TapView(); - isFocused = System.Convert.ToBoolean(App.Query("FocusStateLabel")[0].ReadText()); - Assert.IsTrue(isFocused); - App.Tap("FocusStateLabel"); - isFocused = System.Convert.ToBoolean(App.Query("FocusStateLabel")[0].ReadText()); - Assert.IsFalse(isFocused); + Assert.IsTrue(IsFocused()); + App.Tap("Go"); // Won't do anything, we just need to take focus away from the Entry + Assert.IsFalse(IsFocused()); + } + + bool IsFocused() + { + var focusedText = App.Query(q => q.Marked("FocusStateLabel").All())[0].ReadText(); + return System.Convert.ToBoolean(focusedText); } [UiTestExempt(ExemptReason.CannotTest, "Invalid interaction")]