From b368e8b9ed3875bd7bfa51a163eb166da5322279 Mon Sep 17 00:00:00 2001 From: Shane Neuville Date: Wed, 19 Jun 2024 07:46:21 -0500 Subject: [PATCH 1/2] Use correct interface type in FrameRenderer (#23124) --- global.json | 3 +++ .../Handlers/Android/FrameRenderer.cs | 2 +- .../Tests/Issues/Issue18526.cs | 26 +++++++++++++++++++ .../tests/TestCases/Issues/Issue18526.xaml | 22 ++++++++++++++++ .../tests/TestCases/Issues/Issue18526.xaml.cs | 14 ++++++++++ 5 files changed, 66 insertions(+), 1 deletion(-) create mode 100644 src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue18526.cs create mode 100644 src/Controls/tests/TestCases/Issues/Issue18526.xaml create mode 100644 src/Controls/tests/TestCases/Issues/Issue18526.xaml.cs diff --git a/global.json b/global.json index 2b3c65d222ba..d05a39062b91 100644 --- a/global.json +++ b/global.json @@ -6,5 +6,8 @@ "MSBuild.Sdk.Extras": "3.0.44", "Microsoft.Build.NoTargets": "3.7.0", "Microsoft.DotNet.Arcade.Sdk": "8.0.0-beta.24310.5" + }, + "sdk": { + "allowPrerelease": false } } diff --git a/src/Controls/src/Core/Compatibility/Handlers/Android/FrameRenderer.cs b/src/Controls/src/Core/Compatibility/Handlers/Android/FrameRenderer.cs index 6cab4f0a2e94..bb64e53baf5b 100644 --- a/src/Controls/src/Core/Compatibility/Handlers/Android/FrameRenderer.cs +++ b/src/Controls/src/Core/Compatibility/Handlers/Android/FrameRenderer.cs @@ -176,7 +176,7 @@ protected override void OnLayout(bool changed, int l, int t, int r, int b) if (Element.Handler is IPlatformViewHandler pvh && - Element is IContentView cv) + Element is ICrossPlatformLayout cv) { pvh.LayoutVirtualView(l, t, r, b, cv.CrossPlatformArrange); } diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue18526.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue18526.cs new file mode 100644 index 000000000000..f05e62185871 --- /dev/null +++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue18526.cs @@ -0,0 +1,26 @@ +using NUnit.Framework; +using NUnit.Framework.Legacy; +using UITest.Appium; +using UITest.Core; + +namespace Microsoft.Maui.TestCases.Tests.Issues; + +public class Issue18526 : _IssuesUITest +{ + public override string Issue => "Border not rendering inside a frame"; + + public Issue18526(TestDevice device) + : base(device) + { } + + [Test] + [Category(UITestCategories.Frame)] + public void BorderShouldRender() + { + var label = App.WaitForElement("label"); + var size = label.GetRect(); + Assert.That(label.GetText(), Is.EqualTo(".NET MAUI")); + Assert.That(size.Width, Is.GreaterThan(0)); + Assert.That(size.Height, Is.GreaterThan(0)); + } +} \ No newline at end of file diff --git a/src/Controls/tests/TestCases/Issues/Issue18526.xaml b/src/Controls/tests/TestCases/Issues/Issue18526.xaml new file mode 100644 index 000000000000..06979dc3ffe6 --- /dev/null +++ b/src/Controls/tests/TestCases/Issues/Issue18526.xaml @@ -0,0 +1,22 @@ + + + + + + + + \ No newline at end of file diff --git a/src/Controls/tests/TestCases/Issues/Issue18526.xaml.cs b/src/Controls/tests/TestCases/Issues/Issue18526.xaml.cs new file mode 100644 index 000000000000..0b00e6317422 --- /dev/null +++ b/src/Controls/tests/TestCases/Issues/Issue18526.xaml.cs @@ -0,0 +1,14 @@ +using Microsoft.Maui.Controls; +using Microsoft.Maui.Controls.Xaml; + +namespace Maui.Controls.Sample.Issues; + +[Issue(IssueTracker.Github, 18526, "Border not rendering inside a frame", PlatformAffected.All)] + +public partial class Issue18526 : ContentPage +{ + public Issue18526() + { + InitializeComponent(); + } +} \ No newline at end of file From a984765999315033592f1256b2bc3a0da9cb2782 Mon Sep 17 00:00:00 2001 From: Matthew Leibowitz Date: Thu, 20 Jun 2024 21:00:28 +0800 Subject: [PATCH 2/2] Squashed commit of the following: (#19629) commit f6c3bfae64a0494762d5c98dbfc975d1231b1d13 Merge: d0b3f84b74 3143629c3c Author: Matthew Leibowitz Date: Mon Jun 17 19:58:40 2024 +0800 Merge remote-tracking branch 'origin/main' into dev/macos-actionsheet commit d0b3f84b74d40bab285b8a8cc3ed5f9cafd01a28 Author: Matthew Leibowitz Date: Tue Jun 11 05:00:38 2024 +0800 Fix the older macOS testing commit 3dc6b4da82b5c03143f921344b0024eb72df293a Author: Matthew Leibowitz Date: Mon Jun 10 23:40:40 2024 +0800 macOS 13 things commit e3e48e4581962266190cde3b91a3b6e5fe1586db Author: Matthew Leibowitz Date: Sat Jun 8 02:15:55 2024 +0800 docs commit 693d79c69652a6cb1a2cec255c88819f55c6f76b Merge: 3dbce49d7f a3c872dfbf Author: Matthew Leibowitz Date: Sat Jun 8 02:08:31 2024 +0800 Merge remote-tracking branch 'origin/main' into dev/macos-actionsheet commit 3dbce49d7f0ecd0fa095ab2e64a043a654c29679 Merge: 17ea9c6fc5 93a1bc49d7 Author: Matthew Leibowitz Date: Sat Jun 8 02:06:16 2024 +0800 Merge remote-tracking branch 'origin/main' into dev/macos-actionsheet commit 17ea9c6fc5d2c6e765754e2348a5d67b10af53c4 Author: Matthew Leibowitz Date: Sat Jun 8 02:05:42 2024 +0800 Fix the tests commit 026da412c5a323956ffdf8b27793596bfafa34f7 Author: Matthew Leibowitz Date: Fri Jun 7 03:56:26 2024 +0800 fixes commit d2b85d52078f32551ce777d7c57660b512f4a425 Author: Matthew Leibowitz Date: Thu Jun 6 23:58:33 2024 +0800 namespaces commit 9fa77cd7ab3180a123cc9cc0b77eb2387181bf2a Merge: 3f9596b976 9d71d3212a Author: Matthew Leibowitz Date: Thu Jun 6 23:57:00 2024 +0800 Merge branch 'main' into dev/macos-actionsheet # Conflicts: # src/Controls/samples/Controls.Sample.UITests/Test.cs # src/Controls/src/Core/Platform/AlertManager/AlertManager.iOS.cs # src/Controls/tests/TestCases.Shared.Tests/Tests/Concepts/AlertsGalleryTests.cs # src/Controls/tests/TestCases/Concepts/AlertsGalleryPage.cs # src/TestUtils/src/UITest.Appium/AppiumCatalystApp.cs # src/TestUtils/src/UITest.Appium/HelperExtensions.cs commit 3f9596b976170023eade99f3e39cdf7946fde523 Author: Matthew Leibowitz Date: Sat Dec 30 09:29:31 2023 +0200 Add some UI tests Done: iOS/macOS/Android. TODO: Windows commit 68c930ffe6cacf8f35d7a1db6aa93500056a4892 Author: Matthew Leibowitz Date: Tue Dec 19 18:15:51 2023 +0200 macOS does not use PopoverPresentationController Fixes #18156 --- src/Controls/src/Core/Page/Page.cs | 2 +- .../Platform/AlertManager/AlertManager.iOS.cs | 9 +- src/Controls/tests/CustomAttributes/Test.cs | 10 ++ .../Tests/Concepts/AlertsGalleryTests.cs | 149 ++++++++++++++++++ .../TestCases/Concepts/AlertsGalleryPage.cs | 96 +++++++++++ .../tests/TestCases/CoreViews/CorePageView.cs | 1 + .../Actions/AppiumAndroidAlertActions.cs | 92 +++++++++++ .../Actions/AppiumAppleAlertActions.cs | 80 ++++++++++ .../Actions/AppiumCatalystAlertActions.cs | 68 ++++++++ .../Actions/AppiumIOSAlertActions.cs | 29 ++++ .../src/UITest.Appium/AppiumAndroidApp.cs | 1 + .../src/UITest.Appium/AppiumCatalystApp.cs | 1 + .../src/UITest.Appium/AppiumIOSApp.cs | 1 + .../src/UITest.Appium/AppiumQuery.cs | 13 ++ .../src/UITest.Appium/HelperExtensions.cs | 87 +++++++++- src/TestUtils/src/UITest.Core/IQuery.cs | 1 + 16 files changed, 632 insertions(+), 8 deletions(-) create mode 100644 src/Controls/tests/TestCases.Shared.Tests/Tests/Concepts/AlertsGalleryTests.cs create mode 100644 src/Controls/tests/TestCases/Concepts/AlertsGalleryPage.cs create mode 100644 src/TestUtils/src/UITest.Appium/Actions/AppiumAndroidAlertActions.cs create mode 100644 src/TestUtils/src/UITest.Appium/Actions/AppiumAppleAlertActions.cs create mode 100644 src/TestUtils/src/UITest.Appium/Actions/AppiumCatalystAlertActions.cs create mode 100644 src/TestUtils/src/UITest.Appium/Actions/AppiumIOSAlertActions.cs diff --git a/src/Controls/src/Core/Page/Page.cs b/src/Controls/src/Core/Page/Page.cs index bc49235eccba..007b91ce6c84 100644 --- a/src/Controls/src/Core/Page/Page.cs +++ b/src/Controls/src/Core/Page/Page.cs @@ -282,7 +282,7 @@ public Task DisplayActionSheet(string title, string cancel, string destr /// Displays a platform action sheet, allowing the application user to choose from several buttons. /// /// Title of the displayed action sheet. Can be to hide the title. - /// Text to be displayed in the 'Cancel' button. Can be null to hide the action. + /// Text to be displayed in the 'Cancel' button. Can be null to hide the cancel action. /// Text to be displayed in the 'Destruct' button. Can be to hide the destructive option. /// The flow direction to be used by the action sheet. /// Text labels for additional buttons. diff --git a/src/Controls/src/Core/Platform/AlertManager/AlertManager.iOS.cs b/src/Controls/src/Core/Platform/AlertManager/AlertManager.iOS.cs index e4323b096bbf..14a8c19e4988 100644 --- a/src/Controls/src/Core/Platform/AlertManager/AlertManager.iOS.cs +++ b/src/Controls/src/Core/Platform/AlertManager/AlertManager.iOS.cs @@ -187,12 +187,15 @@ static void PresentPopUp(Page sender, Window virtualView, UIWindow platformView, presentingWindow = senderPageWindow; } - if (UIDevice.CurrentDevice.UserInterfaceIdiom == UIUserInterfaceIdiom.Pad && arguments != null) + if (UIDevice.CurrentDevice.UserInterfaceIdiom == UIUserInterfaceIdiom.Pad && + arguments is not null && + alert.PopoverPresentationController is not null && + platformView.RootViewController?.View is not null) { var topViewController = GetTopUIViewController(presentingWindow); UIDevice.CurrentDevice.BeginGeneratingDeviceOrientationNotifications(); var observer = NSNotificationCenter.DefaultCenter.AddObserver(UIDevice.OrientationDidChangeNotification, - n => { alert.PopoverPresentationController.SourceRect = topViewController.View.Bounds; }); + n => alert.PopoverPresentationController.SourceRect = topViewController.View.Bounds); arguments.Result.Task.ContinueWith(t => { @@ -216,7 +219,7 @@ static void PresentPopUp(Page sender, Window virtualView, UIWindow platformView, static UIViewController GetTopUIViewController(UIWindow platformWindow) { var topUIViewController = platformWindow.RootViewController; - while (topUIViewController.PresentedViewController is not null) + while (topUIViewController?.PresentedViewController is not null) { topUIViewController = topUIViewController.PresentedViewController; } diff --git a/src/Controls/tests/CustomAttributes/Test.cs b/src/Controls/tests/CustomAttributes/Test.cs index ae46f9c3a975..1eed63091e90 100644 --- a/src/Controls/tests/CustomAttributes/Test.cs +++ b/src/Controls/tests/CustomAttributes/Test.cs @@ -744,6 +744,16 @@ public enum InputTransparency CascadeTransLayoutOverlayWithButton, } + public enum Alerts + { + AlertCancel, + AlertAcceptCancelClickAccept, + AlertAcceptCancelClickCancel, + ActionSheetClickItem, + ActionSheetClickCancel, + ActionSheetClickDestroy, + } + public static class InputTransparencyMatrix { // this is both for color diff and cols diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Concepts/AlertsGalleryTests.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Concepts/AlertsGalleryTests.cs new file mode 100644 index 000000000000..5527b2fa9bb6 --- /dev/null +++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Concepts/AlertsGalleryTests.cs @@ -0,0 +1,149 @@ +using NUnit.Framework; +using NUnit.Framework.Legacy; +using UITest.Appium; +using UITest.Core; + +namespace Microsoft.Maui.TestCases.Tests +{ + public class AlertsGalleryTests : CoreGalleryBasePageTest + { + public AlertsGalleryTests(TestDevice device) + : base(device) + { + } + + protected override void NavigateToGallery() + { + App.NavigateToGallery("Alerts Gallery"); + } + +// TODO: UI testing alert code is not yet implemented on Windows. +#if !WINDOWS + [Test] + public void AlertCancel() + { + var test = Test.Alerts.AlertCancel; + + var remote = new EventViewContainerRemote(UITestContext, test); + remote.GoTo(test.ToString()); + + var textBeforeClick = remote.GetEventLabel().GetText(); + ClassicAssert.AreEqual($"Event: {test} (none)", textBeforeClick); + + remote.TapView(); + + var alert = App.WaitForElement(() => App.GetAlert()); + ClassicAssert.NotNull(alert); + + var alertText = alert.GetAlertText(); + CollectionAssert.Contains(alertText, "Alert Title Here"); + CollectionAssert.Contains(alertText, "Alert Message Here"); + + var buttons = alert.GetAlertButtons(); + CollectionAssert.IsNotEmpty(buttons); + ClassicAssert.True(buttons.Count == 1, $"Expected 1 buttonText, found {buttons.Count}."); + + var cancel = buttons.First(); + ClassicAssert.AreEqual("CANCEL", cancel.GetText()); + + cancel.Click(); + + App.WaitForNoElement(() => App.GetAlert()); + + var textAfterClick = remote.GetEventLabel().GetText(); + ClassicAssert.AreEqual($"Event: {test} (SUCCESS 1)", textAfterClick); + } + + [Test] + [TestCase(Test.Alerts.AlertAcceptCancelClickAccept, "ACCEPT")] + [TestCase(Test.Alerts.AlertAcceptCancelClickCancel, "CANCEL")] + public void AlertAcceptCancel(Test.Alerts test, string buttonText) + { + var remote = new EventViewContainerRemote(UITestContext, test); + remote.GoTo(test.ToString()); + + var textBeforeClick = remote.GetEventLabel().GetText(); + ClassicAssert.AreEqual($"Event: {test} (none)", textBeforeClick); + + remote.TapView(); + + var alert = App.WaitForElement(() => App.GetAlert()); + ClassicAssert.NotNull(alert); + + var alertText = alert.GetAlertText(); + CollectionAssert.Contains(alertText, "Alert Title Here"); + CollectionAssert.Contains(alertText, "Alert Message Here"); + + var buttons = alert.GetAlertButtons() + .Select(b => (Element: b, Text: b.GetText())) + .ToList(); + CollectionAssert.IsNotEmpty(buttons); + ClassicAssert.True(buttons.Count == 2, $"Expected 2 buttons, found {buttons.Count}."); + CollectionAssert.Contains(buttons.Select(b => b.Text), "ACCEPT"); + CollectionAssert.Contains(buttons.Select(b => b.Text), "CANCEL"); + + var button = buttons.Single(b => b.Text == buttonText); + button.Element.Click(); + + App.WaitForNoElement(() => App.GetAlert()); + + var textAfterClick = remote.GetEventLabel().GetText(); + ClassicAssert.AreEqual($"Event: {test} (SUCCESS 1)", textAfterClick); + } + + [Test] + [TestCase(Test.Alerts.ActionSheetClickItem, "ITEM 2")] + [TestCase(Test.Alerts.ActionSheetClickCancel, "CANCEL")] + [TestCase(Test.Alerts.ActionSheetClickDestroy, "DESTROY")] + public void ActionSheetClickItem(Test.Alerts test, string itemText) + { + var remote = new EventViewContainerRemote(UITestContext, test); + remote.GoTo(test.ToString()); + + var textBeforeClick = remote.GetEventLabel().GetText(); + ClassicAssert.AreEqual($"Event: {test} (none)", textBeforeClick); + + remote.TapView(); + + var alert = App.WaitForElement(() => App.GetAlert()); + ClassicAssert.NotNull(alert); + + var alertText = alert.GetAlertText(); + CollectionAssert.Contains(alertText, "Action Sheet Title Here"); + + var buttons = alert.GetAlertButtons() + .Select(b => (Element: b, Text: b.GetText())) + .ToList(); + CollectionAssert.IsNotEmpty(buttons); + ClassicAssert.True(buttons.Count >= 4 && buttons.Count <= 5, $"Expected 4 or 5 buttons, found {buttons.Count}."); + CollectionAssert.Contains(buttons.Select(b => b.Text), "DESTROY"); + CollectionAssert.Contains(buttons.Select(b => b.Text), "ITEM 1"); + CollectionAssert.Contains(buttons.Select(b => b.Text), "ITEM 2"); + CollectionAssert.Contains(buttons.Select(b => b.Text), "ITEM 3"); + + // handle the case where the dismiss button is an actual button + if (buttons.Count == 5) + CollectionAssert.Contains(buttons.Select(b => b.Text), "CANCEL"); + + if (buttons.Count == 4 && itemText == "CANCEL") + { + // handle the case where the dismiss button is a "click outside the popup" + + alert.DismissAlert(); + } + else + { + // handle the case where the dismiss button is an actual button + + var button = buttons.Single(b => b.Text == itemText); + button.Element.Click(); + } + + App.WaitForNoElement(() => App.GetAlert()); + + var textAfterClick = remote.GetEventLabel().GetText(); + ClassicAssert.AreEqual($"Event: {test} (SUCCESS 1)", textAfterClick); + } +#endif + } +} diff --git a/src/Controls/tests/TestCases/Concepts/AlertsGalleryPage.cs b/src/Controls/tests/TestCases/Concepts/AlertsGalleryPage.cs new file mode 100644 index 000000000000..d4756fc656b0 --- /dev/null +++ b/src/Controls/tests/TestCases/Concepts/AlertsGalleryPage.cs @@ -0,0 +1,96 @@ +using System; +using System.Threading.Tasks; +using Microsoft.Maui.Controls; + +namespace Maui.Controls.Sample +{ + internal class AlertsGalleryPage : CoreGalleryBasePage + { + protected override void Build() + { + // ALERTS + + // Test with a single button alert that can be dismissed by tapping the button + Add(Test.Alerts.AlertCancel, async t => + { + await DisplayAlert( + "Alert Title Here", + "Alert Message Here", + "CANCEL"); + t.ReportSuccessEvent(); + }); + + // Test alert with options to Accept or Cancel, Accept is the correct option + Add(Test.Alerts.AlertAcceptCancelClickAccept, async t => + { + var result = await DisplayAlert( + "Alert Title Here", + "Alert Message Here", + "ACCEPT", "CANCEL"); + if (result) + t.ReportSuccessEvent(); + else + t.ReportFailEvent(); + }); + + // Test alert with options to Accept or Cancel, Cancel is the correct option + Add(Test.Alerts.AlertAcceptCancelClickCancel, async t => + { + var result = await DisplayAlert( + "Alert Title Here", + "Alert Message Here", + "ACCEPT", "CANCEL"); + if (result) + t.ReportFailEvent(); + else + t.ReportSuccessEvent(); + }); + + // ACTION SHEETS + + // Test action sheet with items and Cancel, Item 2 is the correct option + Add(Test.Alerts.ActionSheetClickItem, async t => + { + var result = await DisplayActionSheet( + "Action Sheet Title Here", + "CANCEL", "DESTROY", + "ITEM 1", "ITEM 2", "ITEM 3"); + if (result == "ITEM 2") + t.ReportSuccessEvent(); + else + t.ReportFailEvent(); + }); + + // Test action sheet with items and Cancel, Cancel is the correct option + Add(Test.Alerts.ActionSheetClickCancel, async t => + { + var result = await DisplayActionSheet( + "Action Sheet Title Here", + "CANCEL", "DESTROY", + "ITEM 1", "ITEM 2", "ITEM 3"); + if (result == "CANCEL") + t.ReportSuccessEvent(); + else + t.ReportFailEvent(); + }); + + // Test action sheet with items and Cancel, Destroy is the correct option + Add(Test.Alerts.ActionSheetClickDestroy, async t => + { + var result = await DisplayActionSheet( + "Action Sheet Title Here", + "CANCEL", "DESTROY", + "ITEM 1", "ITEM 2", "ITEM 3"); + if (result == "DESTROY") + t.ReportSuccessEvent(); + else + t.ReportFailEvent(); + }); + } + + ExpectedEventViewContainer