diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue33331.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue33331.cs new file mode 100644 index 000000000000..adec73688ab7 --- /dev/null +++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue33331.cs @@ -0,0 +1,63 @@ +using Microsoft.Maui.Controls; + +namespace Maui.Controls.Sample.Issues; + +[Issue(IssueTracker.Github, 33331, "[Android] Picker IsOpen not reset when picker is dismissed", PlatformAffected.Android)] +public class Issue33331 : ContentPage +{ + readonly List _fruits = new() { "Apple", "Banana", "Mango", "Orange", "Pineapple" }; + readonly Picker _testPicker; + readonly Label _isOpenLabel; + + public Issue33331() + { + _testPicker = new Picker + { + AutomationId = "TestPicker", + Title = "Pick a fruit" + }; + _testPicker.ItemsSource = _fruits; + + var openButton = new Button + { + AutomationId = "OpenPickerButton", + Text = "Open Programmatically" + }; + openButton.Clicked += OnOpenPicker; + + _isOpenLabel = new Label + { + AutomationId = "IsOpenLabel", + Text = "IsOpen: False", + FontAttributes = FontAttributes.Bold + }; + + _testPicker.PropertyChanged += OnPickerPropertyChanged; + + Content = new VerticalStackLayout + { + Padding = new Thickness(16), + Spacing = 12, + VerticalOptions = LayoutOptions.Center, + Children = + { + _testPicker, + openButton, + _isOpenLabel + } + }; + } + + void OnPickerPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e) + { + if (e.PropertyName == nameof(Picker.IsOpen)) + { + _isOpenLabel.Text = $"IsOpen: {_testPicker.IsOpen}"; + } + } + + void OnOpenPicker(object sender, EventArgs e) + { + _testPicker.IsOpen = true; + } +} diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue33331.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue33331.cs new file mode 100644 index 000000000000..4d4ec0e29315 --- /dev/null +++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue33331.cs @@ -0,0 +1,30 @@ +using NUnit.Framework; +using UITest.Appium; +using UITest.Core; + +namespace Microsoft.Maui.TestCases.Tests.Issues; + +public class Issue33331 : _IssuesUITest +{ + public override string Issue => "[Android] Picker IsOpen not reset when picker is dismissed"; + + public Issue33331(TestDevice device) : base(device) { } + + [Test] + [Category(UITestCategories.Picker)] + public void PickerCanBeOpenedProgrammatically() + { + App.WaitForElement("TestPicker"); + App.WaitForElement("OpenPickerButton"); + App.WaitForElement("IsOpenLabel"); + + var initialLabel = App.FindElement("IsOpenLabel").GetText(); + Assert.That(initialLabel, Is.EqualTo("IsOpen: False")); + + App.Tap("OpenPickerButton"); + App.ClosePicker(windowsTapx: 50, windowsTapy: 50); + App.WaitForElement("IsOpenLabel"); + var closedLabel = App.FindElement("IsOpenLabel").GetText(); + Assert.That(closedLabel, Is.EqualTo("IsOpen: False")); + } +} \ No newline at end of file diff --git a/src/Core/src/Handlers/Picker/PickerHandler.Android.cs b/src/Core/src/Handlers/Picker/PickerHandler.Android.cs index 66a54812a6d0..bdb4d1bdad84 100644 --- a/src/Core/src/Handlers/Picker/PickerHandler.Android.cs +++ b/src/Core/src/Handlers/Picker/PickerHandler.Android.cs @@ -203,6 +203,7 @@ void OnDialogDismiss(object? sender, EventArgs e) _dialog.DismissEvent -= OnDialogDismiss; VirtualView.IsFocused = false; + VirtualView.IsOpen = false; _dialog = null; } diff --git a/src/TestUtils/src/UITest.Appium/HelperExtensions.cs b/src/TestUtils/src/UITest.Appium/HelperExtensions.cs index 2f5c62d15e59..6c86889be939 100644 --- a/src/TestUtils/src/UITest.Appium/HelperExtensions.cs +++ b/src/TestUtils/src/UITest.Appium/HelperExtensions.cs @@ -67,6 +67,34 @@ public static void RightClick(this IApp app, string element) }); } + /// + /// Closes a picker dialog using platform-specific dismiss actions. + /// For Android, taps the "Cancel" button. + /// For iOS/MacCatalyst, taps the "Done" button. + /// For Windows, either taps coordinates (if provided) or the "Cancel" button. + /// + /// Represents the main gateway to interact with an app. + /// Optional X coordinate for Windows tap. Default is 0. + /// Optional Y coordinate for Windows tap. Default is 0. + public static void ClosePicker(this IApp app, int windowsTapx = 0, int windowsTapy = 0) + { + if (app is AppiumAndroidApp) + { + app.Tap("Cancel"); + } + else if (app is AppiumIOSApp || app is AppiumCatalystApp) + { + app.Tap("Done"); + } + else if (app is AppiumWindowsApp) + { + if (windowsTapx != 0 || windowsTapy != 0) + { + app.TapCoordinates(windowsTapx, windowsTapy); + } + } + } + /// /// Performs a down/press on the matched element, without a matching release ///